单解封装后得到视频流信息,我们可以从第一帧视频(音频)流信息中获取编码格式,从而确定解码器类型。
(1)定位第一帧音视频流信息
使用循环的方式查找,找到后break退出,代码如下:
int videoStream = -1;
int audioStream = -1;
for (int i = 0; i < pFormatCtx->nb_streams; i++) //循环查找视频中包含的流信息,
{
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) //视频流
{
videoStream = i;
break;
}
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && audioStream < 0) //音频流
{
audioStream = i;
break;
}
}
pFormatCtx
:描述一个媒体文件或媒体流的构成和基本信息结构,代码中解封装得到的变量。
(2)得到音视频流编码信息
if (videoStream >= 0)
pCodecCtx = pFormatCtx->streams[videoStream]->codec; //查找视频解码器类型
if (audioStream >= 0)
aCodecCtx = pFormatCtx->streams[audioStream]->codec; ///查找音频解码器类型
使用AVCodecID枚举(代码中使用codec_id变量) 获取码流对应的标准,进而选择对应的解码器。
(1)软件解码器 ,使用函数:avcodec_find_decoder
,代码如下:
codec_id = pCodecCtx->codec_id;
pCodec = avcodec_find_decoder(codec_id);
(2)硬件解码器(英伟达、英特尔),使用函数:avcodec_find_decoder_by_name
,代码如下:
-------- 英伟达 --------:
codec_id = pCodecCtx->codec_id;
char hardWareDecoderName[32] = {0};
if (AV_CODEC_ID_H264 == codec_id)
sprintf(hardWareDecoderName, "h264_cuvid");
else if (AV_CODEC_ID_HEVC == codec_id)
sprintf(hardWareDecoderName, "hevc_cuvid");
else if (AV_CODEC_ID_MPEG1VIDEO == codec_id)
sprintf(hardWareDecoderName, "mpeg1_cuvid");
else if (AV_CODEC_ID_MPEG2VIDEO == codec_id)
sprintf(hardWareDecoderName, "mpeg2_cuvid");
else if (AV_CODEC_ID_MPEG4 == codec_id)
sprintf(hardWareDecoderName, "mpeg4_cuvid");
-------- 英特尔 --------:
codec_id = pCodecCtx->codec_id;
char hardWareDecoderName[32] = {0};
if (AV_CODEC_ID_H264 == codec_id)
sprintf(hardWareDecoderName, "h264_qsv");
else if (AV_CODEC_ID_HEVC == codec_id)
sprintf(hardWareDecoderName, "hevc_qsv");
else if (AV_CODEC_ID_MPEG1VIDEO == codec_id)
sprintf(hardWareDecoderName, "mpeg1_qsv");
else if (AV_CODEC_ID_MPEG2VIDEO == codec_id)
sprintf(hardWareDecoderName, "mpeg2_qsv");
else if (AV_CODEC_ID_MPEG4 == codec_id)
sprintf(hardWareDecoderName, "mpeg4_qsv");
if (strlen(hardWareDecoderName) > 0)
pCodec = avcodec_find_decoder_by_name(hardWareDecoderName);
使用avcodec_alloc_context3
函数创建编码器,avcodec_open2
函数打开编码器,代码如下:
// 配置解码器
pCodecCtx = avcodec_alloc_context3(pCodec); //得到的解码器
pCodecCtx->thread_count = 8; //只有软件解码需要配置,解码线程数
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P; //指明像素格式
//打开解码器
if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0)
{
//返回参数小于0表示编码器打开失败
avcodec_close(pCodecCtx);
avcodec_free_context(&pCodecCtx);
pCodecCtx = nullptr;
}
解码函数如下,参数输入解码前的码流数据,输出为解码后视频帧pFrame(格式为yuv420p,之前解码器配置的类型),以及放回解码结果。
bool decode(uint8_t *inputbuf, int frame_size)
{
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = inputbuf;
pkt.size = frame_size;
if (avcodec_send_packet(pCodecCtx, &pkt) != 0)
{
av_packet_unref(&pkt);
return false;
}
while (0 == avcodec_receive_frame(pCodecCtx, pFrame));
av_packet_unref(&pkt);
return true;
}
补充:
前面初始化配置解码器的时候,并没有设置视频的宽高信息。因为h264的每一帧数据都带有编码的信息,当然也包括这些宽高信息了,因此解码完之后,使用pCodecCtx->width, pCodecCtx->height
便可以知道视频的宽高是多少。