使用FFMPEG3.4.2版本进行视频的解码为YUV格式

最近关注的流媒体方向技术,FFMPEG学习是绕不过去的,不过网上关于FFMPEG的应用基本是基于2015年左右的版本,最新的FFMPEG视频解码部分变动还是挺大的,特此记录一下解码过程:

首先当然是FFMPEG的解码器等的初始化:

// 一定要先调用该注册FFMPEG,否则下面的都无法使用

av_register_all();

// 调用该初始化函数才能读取RTSP/RTMP等网络流的协议数据,否则干瞪眼,只解码本地文件的可以无视

avformat_network_init();

初始化完后需要申请一个AVFormatContext对象  *pFormatCtx = avformat_alloc_context();用于解码的上下文,有点FFMPEG基础的都明白这是老套路了。

然后呢还是老套路,调用以下两个函数:

// decodeUrl即为你要进行解码的链接,可以是本地文件路径,也可以是RTSP/RTMP等网络视频流的URL路径

avformat_open_input(&pFormatCtx, decodeUrl, NULL, NULL);

avformat_find_stream_info(pFormatCtx, NULL);

然后就在pFormatCtx->nb_streams找对应的解码类型为视频格式的解码器,这里跟老版本是有点不一样的,codecpar是新推荐的成员变量:

videoindex = -1;
    for (uint32_t i = 0; i < pFormatCtx->nb_streams; i++)
    {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoindex = i;
            break;
        }

    }

接下来还是有点区别的,不过一般人还是可以靠自己解决的

AVCodec  *pCodec = avcodec_find_decoder(pFormatCtx->streams[videoindex]->codecpar->codec_id);
    if (pCodec == NULL)
    {
        return -1;
    }

AVCodecContext  *pCodecCtx = avcodec_alloc_context3(pCodec);

获取视频解码器并创建解码器上下文,很好理解,套路一样就是细节改了改;

下面就开始打开解码器了,这个没变:

avcodec_open2(pCodecCtx, pCodec, NULL);

然后创建视频帧及YUV帧对象跟数据包对象

AVFrame *pFrame = av_frame_alloc();
AVFrame *pFrameYUV = av_frame_alloc();
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));

接下来就是循环读取数据并解码了

while (av_read_frame(pFormatCtx, packet) >= 0)
{
 if (packet->stream_index == videoindex)
{
// 视频帧进行解码,注意这里很不一样了,之前一个函数调用即可,现在需要自己再次写一个解码函数,具体看下面函数实现
 ret = Decode(pCodecCtx, pFrame, packet);
			if (ret < 0)
			{
				printf("Decode Error.\n");
				av_packet_unref(packet);
				continue;
			}

// 这里即为解码出来的数据进行YUV转换存储
			y_size = pCodecCtx->width*pCodecCtx->height;
			fwrite(pFrame->data[0], 1, y_size, fp_yuv);      //Y 
			fwrite(pFrame->data[1], 1, y_size / 4, fp_yuv);  //U
			fwrite(pFrame->data[2], 1, y_size / 4, fp_yuv);  //V
			printf("Write one frame...\n");
		}
// 用完了别忘了释放packet
 av_packet_unref(packet);
}

上面的没啥好说的,主要是Decode函数的实现不一样了,改动挺大的,下面贴出实现代码

int Decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt)
{
	int ret;

        // 先发送包数据到解码上下文中
	ret = avcodec_send_packet(dec_ctx, pkt);
	if (ret < 0)
	{
		printf("Error sending a packet for decoding\n");
		return ret;
	}

        // 然后从解码上下文中读取帧数据到frame对象中
	return avcodec_receive_frame(dec_ctx, frame);
}

不知道官方基于何种意愿做此修改的,个人觉着麻烦多了,因为我要多写不少代码。。。

至此解码工作基本完成,当然用完了资源别忘了还给人家:

av_frame_free(&pFrameYUV);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);

avformat_close_input(&pFormatCtx);

然后测试了下本地视频文件的解码,测试OK,做完后觉着很不错嘛,然而,然而真的那么如你所愿么?显然不是的。。。

在我一时兴起测试RTSP流的时候发现FFMPEG会报错,无法获取,提示什么FFMPEG编译没有添加-lpthread之类的选项,这是什么鬼?懵逼了,因为我是从官方下载的动态链接库,这是你会发现还是GG好使的。。。话不多说,贴解决办法:

在avformat_open_input函数调用中最后一个options参数进行设置RTSP传输基于TCP即可。。。

AVDictionary* options = NULL;
av_dict_set(&options, "rtsp_transport", "tcp", 0);

avformat_open_input(&pFormatCtx, c_TestFile, NULL, &options);

是的,这么设置过后就可以解码RTSP流数据了,知道了后是不是很简单,当然用完了资源记得一定要还:

av_dict_free(&options);

好的,想到的就这么多了。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值