package com.yanzuoguang.util; import com.yanzuoguang.util.helper.StringHelper; import com.yanzuoguang.util.log.Log; import com.yanzuoguang.util.vo.BaseVo; import io.swagger.annotations.ApiModelProperty; import org.bytedeco.javacv.FFmpegFrameGrabber; import org.bytedeco.javacv.FFmpegFrameRecorder; import static org.bytedeco.ffmpeg.global.avcodec.*; import static org.bytedeco.ffmpeg.global.avutil.*; /** * 压缩视频参数 * * @author 颜佐光 */ public class MediaParameter extends BaseVo { public static final int VIDEO_CODEC_MP4 = AV_CODEC_ID_H264; public static final String VIDEO_FORMAT_MP4 = "mp4"; public static final int VIDEO_FIXED_MP4 = AV_PIX_FMT_YUV420P; public static final int SOUND_CODEC_MP3 = AV_CODEC_ID_VORBIS; public static final String ROTATE = "rotate"; /** * 音频转码后的格式,为空时表示不改变格式 */ @ApiModelProperty(notes = "音频转码后的格式,为空时表示不改变格式") private int audioCodec = AV_CODEC_ID_NONE; /** * 音频帧速率FrameRate */ @ApiModelProperty(notes = "音频转码后的格式,为空时表示不改变格式") private double audioFrameRate = AV_SAMPLE_FMT_NONE; /** * 音频转码的比特率,为0时表示采取原来的值 */ @ApiModelProperty(notes = "音频转码的比特率,为0时表示采取原来的值") private int audioBitRate; /** * 音频最小比率率,为0时表示最小无效 */ @ApiModelProperty(notes = "音频最小比率率,为0时表示最小无效") private int minAudioBitRate; /** * 音频比特率压缩比,算法为: 最终比特率= Math.max(minAudioBitRate,audioBitRate * audioBitRateZip) */ @ApiModelProperty(notes = "音频比特率压缩比,算法为: 最终比特率= Math.max(minAudioBitRate,audioBitRate * audioBitRateZip)") private float audioBitRateZip = 0; /** * 视频转码后的格式 */ @ApiModelProperty(notes = "视频转码后的格式") private String videoFormat; /** * 视频转码后的央视 */ @ApiModelProperty(notes = "视频转码后的央视") private int videoCodeC = AV_CODEC_ID_NONE; /** * 视频像素格式 */ @ApiModelProperty(notes = "视频像素格式") private int videoPixelFormat = AV_PIX_FMT_NONE; /** * 视频宽度,为0时默认为原来的宽度 */ @ApiModelProperty(notes = "视频宽度,为0时默认为原来的宽度") private int videoWidth; /** * 视频高度,为0时默认为原来的高度 */ @ApiModelProperty(notes = "视频高度,为0时默认为原来的高度") private int videoHeight; /** * 最小视频宽度 */ @ApiModelProperty(notes = "最小视频宽度") private int minVideoWidth; /** * 最小视频高度 */ @ApiModelProperty(notes = "最小视频高度") private int minVideoHeight; /** * 视频帧速率FrameRate */ @ApiModelProperty(notes = "视频帧速率FrameRate") private double videoFrameRate; /** * 视频原角度 */ @ApiModelProperty(notes = "视频原角度") private double videoRotate; /** * 视频变更角度,最终角度=视频原角度+视频变更角度 */ @ApiModelProperty(notes = "视频变更角度,最终角度=视频原角度+视频变更角度") private double videoRotateChange; /** * 视频宽度高度压缩比,算法为: 最终宽度=Math.max(minVideoWidth,videoWidth * videoSizeZip), * * 最终高度:=Math.max(minVideoHeight,videoHeight * videoSizeZip) */ @ApiModelProperty(notes = "视频宽度高度压缩比,算法为: 最终宽度=Math.max(minVideoWidth,videoWidth * videoSizeZip)," + "最终高度:=Math.max(minVideoHeight,videoHeight * videoSizeZip)") private float videoSizeZip = 0; /** * 视频转码的比特率,为0时表示采取原来的值 */ @ApiModelProperty(notes = "视频转码的比特率,为0时表示采取原来的值") private int videoBitRate; /** * 视频最小比率率,为0时表示最小无效 */ @ApiModelProperty(notes = "视频最小比率率,为0时表示最小无效") private int minVideoBitRate; /** * 视频比特率压缩比,算法为: 最终比特率= Math.max(minAudioBitRate,audioBitRate * audioBitRateZip) */ @ApiModelProperty(notes = "视频比特率压缩比,算法为: 最终比特率= Math.max(minAudioBitRate,audioBitRate * audioBitRateZip)") private float videoBitRateZip = 0; /** * 默认构造函数 */ public MediaParameter() { } /** * 视频压缩 * * @param videoSizeZip 宽,高,压缩比 * @param videoBitRateZip 比特率压缩比,相当于视频质量 */ public MediaParameter(float videoSizeZip, float videoBitRateZip) { this.videoSizeZip = videoSizeZip; this.videoBitRateZip = videoBitRateZip; } /** * 视频压缩 * * @param audioBitRateZip 音频质量压缩比 * @param videoSizeZip 宽,高,压缩比 * @param videoBitRateZip 比特率压缩比,相当于视频质量 */ public MediaParameter(float audioBitRateZip, float videoSizeZip, float videoBitRateZip) { this.audioBitRateZip = audioBitRateZip; this.videoSizeZip = videoSizeZip; this.videoBitRateZip = videoBitRateZip; } /** * 音频最终比特率 * * @return */ public int getAudioBitRateFinally() { return getMinValue(this.minAudioBitRate, this.audioBitRate, this.audioBitRateZip); } /** * 视频最终比特率 * * @return */ public int getVideoBitRateFinally() { return getMinValue(this.minVideoBitRate, this.videoBitRate, this.videoBitRateZip); } /** * 音频最终宽度 * * @return */ public int getVideoWidthFinally() { return getWidthHeightFinally(true); } /** * 音频最终高度 * * @return */ public int getVideoHeightFinally() { return getWidthHeightFinally(false); } private int getWidthHeightFinally(boolean width) { // 最大宽度和最大高度 int maxWidth = Math.max(this.videoWidth, this.videoHeight); int maxHeight = Math.min(this.videoWidth, this.videoHeight); // 获取允许的最小宽度和高度 int minWidth = Math.max(this.minVideoWidth, this.minVideoHeight); int minHeight = Math.min(this.minVideoWidth, this.minVideoHeight); // 计算最小宽度和高度 int calcMinWidth = maxWidth * minHeight / maxHeight; int calcMinHeight = maxHeight * minWidth / maxWidth; // 设置最小的宽高 if (minWidth == 0) { minWidth = calcMinWidth; calcMinHeight = minHeight; } if (minHeight == 0) { minHeight = calcMinHeight; calcMinWidth = minWidth; } // 判断宽高比列 if (calcMinHeight > minHeight) { minHeight = calcMinHeight; } else { minWidth = calcMinWidth; } if (width) { int minValue = this.videoWidth > this.videoHeight ? minWidth : minHeight; return getMinValue(minValue, this.videoWidth, this.videoSizeZip); } else { int minValue = this.videoWidth < this.videoHeight ? minWidth : minHeight; return getMinValue(minValue, this.videoHeight, this.videoSizeZip); } } private int getMinValue(int min, int from, float zip) { if (from < min) { return from; } else { return Math.max(min, (int) (from * zip)); } } /** * 音频最终高度 * * @return */ public double getVideoRotateFinally() { return this.videoRotate + this.videoRotateChange; } /** * 检查参数 */ public void check() { this.audioBitRateZip = StringHelper.getFirst(this.audioBitRateZip, 1f); this.videoSizeZip = StringHelper.getFirst(this.videoSizeZip, 1f); this.videoBitRateZip = StringHelper.getFirst(this.videoBitRateZip, 1f); if (audioBitRateZip > 1) { throw new RuntimeException("audioBitRateZip请在0~1之间"); } if (videoSizeZip > 1) { throw new RuntimeException("videoSizeZip请在0~1之间"); } if (videoBitRateZip > 1) { throw new RuntimeException("videoBitRateZip请在0~1之间"); } } /** * 设置默认参数 * * @param grabber 参数的视频 */ public void init(FFmpegFrameGrabber grabber) { this.check(); // 读取音频属性 this.audioBitRate = StringHelper.getFirst(this.audioBitRate, grabber.getAudioBitrate()); this.audioCodec = StringHelper.getFirstRun(AV_CODEC_ID_NONE, AV_CODEC_ID_NONE, this.audioCodec, grabber.getAudioCodec()); Log.info(MediaParameter.class, "getAudioCodecName:%s", grabber.getAudioCodecName()); this.audioFrameRate = StringHelper.getFirstRun((double) AV_SAMPLE_FMT_NONE, (double) AV_SAMPLE_FMT_NONE, this.audioFrameRate, grabber.getAudioFrameRate()); // 视频宽度高度 this.videoWidth = StringHelper.getFirst(this.videoWidth, grabber.getImageWidth()); this.videoHeight = StringHelper.getFirst(this.videoHeight, grabber.getImageHeight()); // 读取视频格式 this.videoFormat = StringHelper.getFirst(this.videoFormat, grabber.getFormat()); this.videoCodeC = StringHelper.getFirstRun(AV_CODEC_ID_NONE, AV_CODEC_ID_NONE, this.videoCodeC, grabber.getVideoCodec()); Log.info(MediaParameter.class, "getVideoCodecName:%s", grabber.getVideoCodecName()); this.videoPixelFormat = StringHelper.getFirstRun(AV_PIX_FMT_NONE, AV_PIX_FMT_NONE, this.videoPixelFormat, grabber.getPixelFormat()); // 读取视频属性 this.videoRotate = StringHelper.toDouble(grabber.getVideoMetadata(ROTATE)); this.videoFrameRate = StringHelper.getFirst(this.videoFrameRate, grabber.getVideoFrameRate()); this.videoBitRate = StringHelper.getFirst(this.videoBitRate, grabber.getVideoBitrate()); } /** * 设置转码参数 * * @param recorder 需要转码的对象 */ public void init(FFmpegFrameGrabber grabber, FFmpegFrameRecorder recorder) { recorder.setTimestamp(grabber.getTimestamp()); recorder.setFormat(grabber.getFormat()); recorder.setOptions(grabber.getOptions()); recorder.setMetadata(grabber.getMetadata()); recorder.setAudioMetadata(grabber.getAudioMetadata()); recorder.setVideoMetadata(grabber.getVideoMetadata()); recorder.setAspectRatio(recorder.getAspectRatio()); recorder.setAudioOptions(recorder.getAudioOptions()); recorder.setVideoOptions(grabber.getVideoOptions()); // 设置音频比特率 recorder.setAudioBitrate(this.getAudioBitRateFinally()); // recorder.setAudioQuality(this.audioBitRateZip); /* recorder.setAudioCodec(this.audioCodec); // 设置声道 recorder.setAudioChannels(grabber.getAudioChannels()); // 设置声音质量 recorder.setAudioQuality(recorder.getAudioQuality()); // 设置音频属性 recorder.setSampleFormat(grabber.getSampleFormat()); recorder.setSampleRate(grabber.getSampleRate()); /* */ // 视频格式 recorder.setFormat(this.videoFormat); // 设置转码视频格式 recorder.setVideoCodec(this.videoCodeC); // 设置像素格式 recorder.setPixelFormat(this.videoPixelFormat); // 设置转码后的视频角度 recorder.setVideoMetadata(ROTATE, String.valueOf(this.getVideoRotateFinally())); // 设置视频比特率 recorder.setVideoBitrate(StringHelper.getFirst(this.getVideoBitRateFinally(), this.getAudioBitRateFinally())); // 设置帧速率 // recorder.setFrameRate(this.videoFrameRate); // recorder.setVideoQuality(this.videoBitRateZip); } /** * 设置最小压缩比特率 */ public void initZipBitrate() { // 设置视频压缩率 this.audioBitRateZip = StringHelper.getFirst(this.audioBitRateZip, 0.25f); // 设置视频压缩率 this.videoBitRateZip = StringHelper.getFirst(this.videoBitRateZip, 0.1f); // 设置压缩大小 this.videoSizeZip = StringHelper.getFirst(this.videoSizeZip, 0.1f); // 设置音频最小比特率 this.minAudioBitRate = StringHelper.getFirst(this.minAudioBitRate, 48043); } /** * 设置宽高最小480 *360,最小标准 */ public void initZip480_360() { // 设置最小宽度,指的是宽度和高度中的大值,宽度>高度 this.minVideoWidth = StringHelper.getFirst(this.minVideoWidth, 480); // 设置最小高度,指的是宽度和高度中的小值,宽度>高度 this.minVideoHeight = StringHelper.getFirst(this.minVideoHeight, 360); // 设置视频最小比特率 this.minVideoBitRate = StringHelper.getFirst(this.minVideoBitRate, 295581); } /** * 设置宽高最小800*600,老台式机标准 */ public void initZip800_600() { // 设置最小宽度,指的是宽度和高度中的大值,宽度>高度 this.minVideoWidth = StringHelper.getFirst(this.minVideoWidth, 800); // 设置最小高度,指的是宽度和高度中的小值,宽度>高度 this.minVideoHeight = StringHelper.getFirst(this.minVideoHeight, 600); // 设置视频最小比特率 this.minVideoBitRate = StringHelper.getFirst(this.minVideoBitRate, 600000); } /** * 设置宽最小960,高度根据宽度自适应(途比达默认标准) */ public void initZip960() { // 设置最小宽度,指的是宽度和高度中的大值,宽度>高度 this.minVideoWidth = StringHelper.getFirst(this.minVideoWidth, 960); // 设置视频最小比特率 this.minVideoBitRate = StringHelper.getFirst(this.minVideoBitRate, 2955811); } /** * 设置宽高最小1920*1080(微信录制视频标准) */ public void initZip1920_1080() { // 设置最小宽度,指的是宽度和高度中的大值,宽度>高度 this.minVideoWidth = StringHelper.getFirst(this.minVideoWidth, 1920); // 设置最小高度,指的是宽度和高度中的小值,宽度>高度 this.minVideoHeight = StringHelper.getFirst(this.minVideoHeight, 1080); // 设置视频最小比特率 this.minVideoBitRate = StringHelper.getFirst(this.minVideoBitRate, 8311136); } /** * 抓码为mp4 */ public void initMp4() { this.audioCodec = SOUND_CODEC_MP3; this.videoCodeC = VIDEO_CODEC_MP4; this.videoFormat = VIDEO_FORMAT_MP4; this.videoPixelFormat = VIDEO_FIXED_MP4; } /** * 压缩mp4 */ public void initZipMp4() { // 设置结果为mp4 this.initMp4(); // 设置最小宽高 this.initZip960(); // 设置最小压缩比特率 this.initZipBitrate(); } public int getAudioCodec() { return audioCodec; } public void setAudioCodec(int audioCodec) { this.audioCodec = audioCodec; } public double getAudioFrameRate() { return audioFrameRate; } public void setAudioFrameRate(double audioFrameRate) { this.audioFrameRate = audioFrameRate; } public int getAudioBitRate() { return audioBitRate; } public void setAudioBitRate(int audioBitRate) { this.audioBitRate = audioBitRate; } public int getMinAudioBitRate() { return minAudioBitRate; } public void setMinAudioBitRate(int minAudioBitRate) { this.minAudioBitRate = minAudioBitRate; } public float getAudioBitRateZip() { return audioBitRateZip; } public void setAudioBitRateZip(float audioBitRateZip) { this.audioBitRateZip = audioBitRateZip; } public String getVideoFormat() { return videoFormat; } public void setVideoFormat(String videoFormat) { this.videoFormat = videoFormat; } public int getVideoCodeC() { return videoCodeC; } public void setVideoCodeC(int videoCodeC) { this.videoCodeC = videoCodeC; } public int getVideoPixelFormat() { return videoPixelFormat; } public void setVideoPixelFormat(int videoPixelFormat) { this.videoPixelFormat = videoPixelFormat; } public int getVideoWidth() { return videoWidth; } public void setVideoWidth(int videoWidth) { this.videoWidth = videoWidth; } public int getVideoHeight() { return videoHeight; } public void setVideoHeight(int videoHeight) { this.videoHeight = videoHeight; } public int getMinVideoWidth() { return minVideoWidth; } public void setMinVideoWidth(int minVideoWidth) { this.minVideoWidth = minVideoWidth; } public int getMinVideoHeight() { return minVideoHeight; } public void setMinVideoHeight(int minVideoHeight) { this.minVideoHeight = minVideoHeight; } public double getVideoFrameRate() { return videoFrameRate; } public void setVideoFrameRate(double videoFrameRate) { this.videoFrameRate = videoFrameRate; } public double getVideoRotate() { return videoRotate; } public void setVideoRotate(double videoRotate) { this.videoRotate = videoRotate; } public double getVideoRotateChange() { return videoRotateChange; } public void setVideoRotateChange(double videoRotateChange) { this.videoRotateChange = videoRotateChange; } public float getVideoSizeZip() { return videoSizeZip; } public void setVideoSizeZip(float videoSizeZip) { this.videoSizeZip = videoSizeZip; } public int getVideoBitRate() { return videoBitRate; } public void setVideoBitRate(int videoBitRate) { this.videoBitRate = videoBitRate; } public int getMinVideoBitRate() { return minVideoBitRate; } public void setMinVideoBitRate(int minVideoBitRate) { this.minVideoBitRate = minVideoBitRate; } public float getVideoBitRateZip() { return videoBitRateZip; } public void setVideoBitRateZip(float videoBitRateZip) { this.videoBitRateZip = videoBitRateZip; } }