1、原理
FFmpeg解码获得的AVPacket只包含视频压缩数据,并没有包含相关的解码信息(比如:h264的sps pps头信息,AAC的adts头信息),没有这些编码头信息解码器(MediaCodec)是识别不到不能解码的。在FFmpeg中,这些头信息是保存在解码器上下文(AVCodecContext)的extradata中的,所以我们需要为每一种格式的视频添加相应的解码头信息,这样解码器(MediaCodec)才能正确解析每一个AVPacket里的视频数据。
- 旧版:av_bitstream_filter_filter
可以实现头信息添加,但是容易造成内存泄漏,处理比较麻烦
给AVPacket添加指定解码格式的头信息。
- 新版:AVBitStreamFilter
使用简单,没有内存泄漏问题。
2、添加过程
1、找到相应解码器的过滤器
bsfilter = av_bsf_get_by_name("h264_mp4toannexb");
2、初始化过滤器上下文:
av_bsf_alloc(bsfilter, &bsf_ctx); //AVBSFContext;
3、添加解码器属性:
avcodec_parameters_copy(bsf_ctx->par_in, video->codecpar);
4、初始化过滤器上下文
av_bsf_init(video->bsf_ctx);
5、送入AVPacket过滤
av_bsf_send_packet(bsf_ctx, avPacket);
6、接收过滤后的AVPacket:
av_bsf_receive_packet(bsf_ctx, avPacket);
7、释放资源:
av_bsf_free(&bsf_ctx);
3、实现
3.1、初始化过滤器
void JfFFmpeg::start() {
...
const char *codecName = ((const AVCodec*) video->pVCodecCtx->codec)->name;
supMediaCodec = callJava->onCallIsSupCodec(codecName);
if (supMediaCodec){
LOGD("当前设备支持硬解码当前视频");
//找到相应解码器的过滤器
if (strcasecmp(codecName,"h264") == 0) {
bsFilter = av_bsf_get_by_name("h264_mp4toannexb");
} else if (strcasecmp(codecName,"h265") == 0){
bsFilter = av_bsf_get_by_name("hevc_mp4toannexb");
}
if (bsFilter == NULL){
goto end;
}
//初始化过滤器上下文:
if (av_bsf_alloc(bsFilter,&video->abs_ctx) != 0){
supMediaCodec = false;
goto end;
}
//添加解码器属性
if (avcodec_parameters_copy(video->abs_ctx->par_in,video->codecpar) < 0){
supMediaCodec = false;
av_bsf_free(&video->abs_ctx);
video->abs_ctx = NULL;
goto end;
}
//初始化过滤器上下文
if (av_bsf_init(video->abs_ctx) != 0){
supMediaCodec = false;
av_bsf_free(&video->abs_ctx);
video->abs_ctx = NULL;
goto end;
}
video->abs_ctx->time_base_in = video->time_base;
}
end:
if (supMediaCodec){
video->codectype = CODEC_MEDIACODEC;
}
audio->play();
video->play();
...
}
3.2、过滤AVPacket
void *playVideo(void *data){
JfVideo *video = (JfVideo *)data;
while (video->playStatus != NULL && !video->playStatus->exit){
...
if (video->codectype == CODEC_MEDIACODEC){
LOGD("硬解码视频");
//送入AVPacket过滤
if (av_bsf_send_packet(video->abs_ctx,avPacket) != 0) {
av_packet_free(&avPacket);//AVPacket中的第一个参数,就是引用,减到0才真正释放
av_free(avPacket);
avPacket = NULL;
continue;
}
//接收过滤后的AVPacket
while (av_bsf_receive_packet(video->abs_ctx,avPacket) == 0){
LOGD("开始解码");
av_packet_free(&avPacket);//AVPacket中的第一个参数,就是引用,减到0才真正释放
av_free(avPacket);
continue;
}
avPacket = NULL;
}
...
}
pthread_exit(&video->thread_play);
}