1、前言
最近在研究FFmepg滤镜方面的知识,索性就准备尝试一下代码给视频添加水印。
一开始想直接FFmpeg直接c代码加水印,写完后测试了一下比较慢,毕竟软解得看CPU即使设置了多线程编解码还是一个吊样,然后想到了另一条路硬解码然后ffmpeg数据处理水印接着送入硬编码这样效率会很高,毕竟GPU还是很快的。软解永远是兜底方案
注:这不是一篇单纯的FFmpeg水印命令文章
注:本篇使用JNI开发
2、效果
3、流程
仅核心流程具体细节参照示例
原视频AAC解码H264编码YUV编码H264合成MediaCodecAVFilterMediaCodecMediaMuxerAudioAACMPEG4
AVFilter是FFmpeg库下的一个流媒体过滤器,它用于对组件常用于多媒体处理与编辑,包含多种滤镜,比如旋转,加水印,多宫格等等,源码位于ffmpeg/libavfilter
中。
4、准备
-
FFmpeg so库 音视频(1) - FFmpeg4.3.4编译
-
libyuv so库 libyuv 库编译
5、示例
5.1 提取音频/视频流轨道MediaFormat
//视频流提取器
MediaExtractor mediaExtractor = new MediaExtractor();
//设置视频源
mediaExtractor.setDataSource(path);
//寻找视频流
for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
MediaFormat mediaFormat = mediaExtractor.getTrackFormat(i);
if (mediaFormat.getString(MediaFormat.KEY_MIME).contains("video/")) {
//视频流
videoMediaFmt = mediaFormat;
//选择当前视频轨道
mediaExtractor.selectTrack(i);
} else if (mediaFormat.getString(MediaFormat.KEY_MIME).contains("audio/")) {
//音频流
readAudioMediaFormat = mediaFormat;
//记录音频流索引
audioExtractorSelectIndex = i;
}
}
//音频流提取器
MediaExtractor audioMediaExtractor = new MediaExtractor();
audioMediaExtractor.setDataSource(path);
//直接选择音频流
audioMediaExtractor.selectTrack(audioExtractorSelectIndex);
MediaExtractor
视频信息的提取类:
-
selectTrack 选择轨道 完毕后所有API都将基于改轨道进行信息提取
-
getTrackFormat 根据索引获取当前轨道的MediaFormat
5.2 创建视频解码器
int colorFmtType = MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
//设置解码器解码的YUV类型
videoMediaFmt.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFmtType);
//重新设置缓冲区大小
videoMediaFmt.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, videoMediaFmt.getInteger(MediaFormat.KEY_WIDTH) * videoMediaFmt.getInteger(MediaFormat.KEY_HEIGHT) * 3 / 2);
//根据类型创建合适的硬解码器
MediaCodec decode = MediaCodec.createDecoderByType(videoMediaFmt.getString(MediaFormat.KEY_MIME));
finalVideoMediaFmt = videoMediaFmt;
decode.configure(finalVideoMediaFmt, null, null, 0);
//设置解码异步回调
decode.setCallback(mediaCallback);
decode.start();
5.3 创建视频编码器
MediaCodec encode = MediaCodec.createEncoderByType(videoMediaFmt.getString(MediaFormat.KEY_MIME));
MediaCodec mediaFormat = MediaFormat.createVideoFormat(videoMediaFmt.getString(MediaFormat.KEY_MIME),videoMediaFmt.getInteger(MediaFormat.KEY_WIDTH), videoMediaFmt.getInteger(MediaFormat.KEY_HEIGHT));
// 编码器接受的YUV格式
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
//设置比特率 越大越清晰
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, videoMediaFmt.getInteger(MediaFormat.KEY_WIDTH) * videoMediaFmt.getInteger(MediaFormat.KEY_HEIGHT) * 3);
//设置帧率FPS
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, videoMediaFmt.getInteger(MediaFormat.KEY_FRAME_RATE));
//设置I帧
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
//设置采样率
mediaFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
//设置csd-0 1 H264需要写入头部
mediaFormat.setByteBuffer("csd-0",