本次使用的ffmpeg版本是4.2,解码的调用方式为:
int32_t iRet = -1;
// 最后一个包解码完成后,需要取完解码器中剩余的缓存帧;
// 调用avcodec_send_packet时塞空包进去,;
// 解码器就会知道所有包解码完成,再调用avcodec_receive_frame时,将会取出缓存帧;
// AVPacket packet;
// av_init_packet(&packet);
// pkt.data = NULL;
// pkt.size = 0;
// avcodec_send_packet(ctx, pkt);
iRet = avcodec_send_packet(ctx, pkt);
if (iRet != 0 && iRet != AVERROR(EAGAIN)) {
get_ffmepg_err_str(iRet);
if (iRet == AVERROR_EOF)
iRet = 0;
return iRet;
}
while (true) {
// 每解出来一帧,丢到队列中;
iRet = avcodec_receive_frame(ctx, frame);
if (iRet != 0) {
if (iRet == AVERROR(EAGAIN)) {
return 0;
} else
return iRet;
}
PushRenderBuffer();
// 音频解码后,如果需要重采样,也可以在此处进行resample;
}
以前的版本解码方式为:
int32_t iRet = -1;
iRet = avcodec_send_packet(ctx, pkt);
if (iRet != 0 && iRet != AVERROR(EAGAIN)) {
get_ffmepg_err_str(iRet);
if (iRet == AVERROR_EOF)
iRet = 0;
return iRet;
}
avcodec_decode_video2(pCodecCtx, frame, iGotPicture, pkt);
新旧版本更新时,注意接口的使用方法,新版本avcodec_send_packet一次,需要循环调用avcodec_receive_frame多次,返回EAGAIN后,结束当前这次的解码,音频解码也是一样
一、软件解码:
先上一段代码,再做下说明,这是解码视频文件,以AVStream的方式获取文件信息,再创建解码器
int32_t ret = avformat_open_input(&m_pAVFormatIC, pPath, NULL, NULL);
if (avformat_find_stream_info(m_pAVFormatIC, NULL) < 0) {
return;
}
for (uint32_t i = 0; i < m_pAVFormatIC->nb_streams; i++) {
switch (m_pAVFormatIC->streams[i]->codec->codec_type) {
case AVMEDIA_TYPE_VIDEO:
// 视频流;
AVCodec *find_codec = avcodec_find_decoder(m_pAVFormatIC->streams[i]->codec->codec_id);
AVCodecContext *codec_ctx = avcodec_alloc_context3(find_codec);
avcodec_parameters_to_context(codec_ctx, m_pAVFormatIC->streams[i]->codecpar);
codec_ctx->opaque = this;
codec_ctx->thread_count = 5;
codec_ctx->thread_safe_callbacks = 1;
// avcodec_receive_frame的frame数据内存交由自己申请,自己释放,减少内存申请及拷贝;
codec_ctx->get_buffer2 = DemuxStream::CustomFrameAllocBuffer;
avcodec_open2(codec_ctx, find_codec, NULL);
continue;
case AVMEDIA_TYPE_AUDIO:
// 音频流;
// 同上;
continue;
}
}
1、m_pAVFormatIC->streams[i]->codec->codec_type 来判断是否包含音频和视频数据,如果是mp3文件,codec_type有Video是表示MP3的封面图片帧;
2、avcodec_find_decoder和avcodec_alloc_context3创建的指针,不需要覆盖stream中的指针,这是错误的用法,stream只是记录当前文件的流信息,不要用来保存context,创建的context由自己来保管;
3、avcodec_parameters_to_context,一定要做,这是把解析到的留信息设置到context,不需要在自己一个参数一个参数的设置;
4、多线程软解,thread_count不修改的话默认值是1,使用单个线程进行解码,遇到一些大文件,比如4K,30帧以上的