ffmpeg基础(四) 解码视频

博客给出了解码流程图相关代码,且代码中包含每一步的解释,聚焦信息技术领域的解码流程代码展示。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 解码流程图:

直接上代码,里面有每一步的解释 

void CTestYUV::decode()
{
	/*
	av_register_all初始化所有组件,只有调用了该函数才能使用复用器和解码器。
	*/
	av_register_all();
	avformat_network_init();
	//AVFormatContext设备上下文
	/*
	
	*/
	AVFormatContext* pFormatContext = NULL;
	const char* path = "D://1.mp4";
	//AVDictionary* opt = NULL;//设置选项
	//av_dict_set(&opt, "rtsp_transport","tcp",0);
	//av_dict_set(&opt,"max_delay","550",0);//设置延迟时间
	int ret=avformat_open_input(&pFormatContext, path,NULL,NULL);//打开文件
	//int ret=avformat_open_input(&pFormatContext, path,NULL,&opt);//打开网络流
	if (ret)
	{
		//打开失败
		return;
	}
	//打开成功
	//寻找解码器信息,h264还是h265,
	ret=avformat_find_stream_info(pFormatContext,NULL);
	if (ret)
	{
		//寻找解码器失败
		return;
	}
	int time = pFormatContext->duration;
	int mbitime = (time / 1000000)/60;
	cout << "时间" << mbitime;
	//打印输出或者输入格式的详细信息,比如持续时间,比特率,编解码器,编码格式
	av_dump_format(pFormatContext, NULL, path, 0);

	//寻找流
	int VideoStream = -1, AudioStream = -1;
	VideoStream=av_find_best_stream(pFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1,NULL,0);


	AVCodecContext* pCodecContext = NULL;

	pCodecContext = avcodec_alloc_context3(NULL);
	avcodec_parameters_to_context(pCodecContext, pFormatContext->streams[VideoStream]->codecpar);
	
	//寻找一个解码器
	AVCodec* cCodec=avcodec_find_decoder(pCodecContext->codec_id);
	if (!cCodec)
	{
		return;//寻找器失败
	}
	//pFormatContext->streams[VideoStream]->codec已经被否决
	ret=avcodec_open2(pCodecContext, cCodec,NULL);
	//解码视频
	AVFrame* frame = av_frame_alloc();//申请原始空间或者帧空间
	AVFrame* frameYUV = av_frame_alloc();//创建yuv空间

	int width= pCodecContext->width;
	int height= pCodecContext->height;
	int fmt = pCodecContext->pix_fmt;
	//分配空间,进行图像转换
	//int nSize=avpicture_get_size(AV_PIX_FMT_YUV420P, width,height);//已经被否决
	int nSize = av_image_get_buffer_size(AV_PIX_FMT_YUV420P, width, height, 1);
	uint8_t* buff = NULL;
	buff=(uint8_t*)av_malloc(nSize);//分配空间

	//一帧图像
	//被否决avpicture_fill((AVPicture*)frameYUV, buff, AV_PIX_FMT_YUV420P, width, height);
	av_image_fill_arrays(frameYUV->data, frameYUV->linesize, buff, AV_PIX_FMT_YUV420P, width, height, 1);



	//av_malloc与c语言malloc等价
	AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket));

	//转换上下文
	SwsContext* swsCtx = NULL;
	swsCtx=sws_getContext(width, height, (AVPixelFormat)fmt,width,height, AV_PIX_FMT_YUV420P,SWS_BICUBIC,NULL,NULL,NULL);

	//读帧
	int ss = 0;
	int nFrameCount = 0;
	//ss=av_read_frame(pFormatContext, packet);
	while (av_read_frame(pFormatContext, packet)>=0)
	{
		if (packet->stream_index== VideoStream)
		{
			/*
			//这个刚刚返回-1094995529,是解码器上下文不匹配,
			packet->stream_index== VideoStream,没有进行筛选,有可能为音频,视频,字幕,需要用到对应的解码器
			*/			
			ret = avcodec_send_packet(pCodecContext, packet);
			if (ret < 0) { 
				
			}
			while (ret >= 0) {
				ret = avcodec_receive_frame(pCodecContext, frame);//这个返回-11,是因为刚刚av_packet_unref没有释放packet。
				if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
					break;
				}
				else if (ret < 0) {
				//	goto end;  //end处进行资源释放等善后处理
				}
				if (ret >= 0) {
					sws_scale(swsCtx, (const uint8_t**)frame->data, frame->linesize, 0,
						height, frameYUV->data, frameYUV->linesize);
					nFrameCount++;
					cout << "帧数:" << nFrameCount<<"\n"<<endl;
				}
			}
		}
		av_packet_unref(packet);
	}
	sws_freeContext(swsCtx);
	av_frame_free(&frame);
	av_frame_free(&frameYUV);
	avcodec_close(pCodecContext);
	avformat_close_input(&pFormatContext);
}

 

### FFMPEG基础视频解码教程 #### 初始化资源和配置环境 为了能够顺利地进行视频解码操作,首先需要确保开发环境中已经正确安装并配置好FFmpeg及其依赖项。这可以通过Conan等工具来简化构建过程,使得不同平台上都能方便快捷地获取到所需的库版本[^1]。 #### 打开输入流与读取包 当准备就绪之后,下一步便是打开待处理的媒体文件作为输入源,并从中逐个提取数据包(packet)。每个这样的数据包内含有一帧或多帧未经压缩的数据片段,在此阶段并不涉及具体的编/解码工作: ```c AVFormatContext *fmt_ctx = NULL; if (avformat_open_input(&fmt_ctx, input_filename, NULL, NULL) != 0){ fprintf(stderr, "Could not open source file %s\n", input_filename); } // Read packets from the file. while(av_read_frame(fmt_ctx, &pkt)>=0){ // Process packet... } ``` #### 寻找解码器并分配上下文 一旦获得了有效的数据包,则需进一步查找对应的解码器实例用于后续的操作。这里通过`avcodec_find_decoder()`函数实现,它接收一个参数指定要寻找的具体编码ID;接着创建一个新的解码器上下文对象供实际调用时传递给底层API使用[^2]。 ```c const AVCodec *decoder = avcodec_find_decoder(codec_id); if (!decoder){ fprintf(stderr,"Failed to find decoder for stream #%u\n", i); } AVCodecContext *dec_ctx = avcodec_alloc_context3(decoder); if(!dec_ctx){ fprintf(stderr,"Failed to allocate the Decoder Context\n"); } ``` #### 配置解码器参数及启动初始化 有了上述准备工作后,现在可以设置一些必要的选项比如分辨率大小、像素格式等等,随后正式开启该解码器以便开始真正的解码流程。值得注意的是,某些情况下还需要额外提供硬件加速的支持信息给定解码器以提高效率。 ```c if (avcodec_parameters_to_context(dec_ctx, codecpar)<0){ fprintf(stderr,"Failed to copy codec parameters to decoder context.\n"); } if (avcodec_open2(dec_ctx, decoder, NULL) < 0){ fprintf(stderr,"Failed to open video decoder for stream #%u\n",i); } ``` #### 循环发送数据包至解码队列 此时已准备好所有前置条件,接下来就是不断地向解码器提交新到来的数据包直到遇到结束标志为止。每次成功递交都会触发内部机制尝试解析出完整的图像帧,如果当前包不足以构成完整画面则会被暂存起来等待更多补充材料到达后再做判断。 ```c int ret=0; ret = avcodec_send_packet(dec_ctx,&pkt); if(ret<0 && ret!=AVERROR(EAGAIN)){ printf("Error sending a packet for decoding\n"); } ``` #### 接收已完成解码的结果帧 随着一个个数据包被送入解码管道之中,最终会产出一系列经过还原后的原始YUV格式图片序列——即所谓的“frame”。每当有一个新的可用帧产生出来以后就可以立即对其进行各种各样的后期加工或是直接显示在屏幕上完成整个回放环节了。 ```c AVFrame* frame = av_frame_alloc(); for(;;){ ret = avcodec_receive_frame(dec_ctx, frame); if(ret==AVERROR(EAGAIN)||ret==AVERROR_EOF) break; else if(ret<0){ fprintf(stderr,"Error during decoding\n"); exit(1); } // Use or display decoded frames here... av_frame_unref(frame); } ``` #### 清理释放占用资源 最后一步自然是妥善清理掉之前申请过的各类内存空间以及其他形式上的临时性存储单元,防止程序退出之时留下任何潜在隐患影响系统的稳定性。 ```c avcodec_free_context(&dec_ctx); avformat_close_input(&fmt_ctx); av_frame_free(&frame); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值