在Linux环境下使用ffmpeg将YUYV格式的yuv数据转换成yuv420p格式的yuv数据

本文介绍如何使用FFmpeg组件将来自摄像头的YUYV格式数据转换为YUV420P格式,并通过具体代码实现展示了整个过程。

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

yuyv转换为yuv420p格式

参考文章

参考大佬的文章
参考雷神的文章,向雷神致敬

参考了这两篇文章还有网上的文章大部分都是从解码器输出的frame中的视频数据进行格式转换。由于自己刚刚入门,有一个需求是对packet.data摄像头读取的原始yuv数据进行格式转换,我的环境是Ubuntu18.04,在使用AVDictionary *option 设置图像的参数的时候使用,都能设置图像的分辨率和帧率

	av_dict_set(&option,"video_size","640x480",0);
	av_dict_set(&option,"framerate","30",0); 

但是我使用

av_dict_set(&option,"pixel_format","nv12",0)

设置yuv视频格式的时候明明函数返回执行成功了,但是视频格式还是没有被设置成nv12的yuv的视频格式。写入文件的数据还是yuyv422的格式,参考了雷神的文章后,知道了使用sws_scale()函数进行操作,但是围绕sws_scale()函数进行的配套辅助操作自己并不是很明白,在看来这些文章思考了一段时间联想到了音频的重采样,音频重采样不就是对音频三元组中的采样率、采样大小、通道数这些音频属性进行改变吗?视频数据格式的转换不就是不也是视频属性的改变吗?在音频重采样中不也是从设备读取的原始数据packet.data中获取数据吗?于是我仿照音频重采样的思路也构造了转换前后的数据缓冲区src_frame和dst_frame.在进行转换结果证明自己的猜想是对的。从packet.data中的数据进行格式转换需要创建frame,包括把数据输入前后也要构造输入输出数据frame。

代码演示

void h264_codec(void)
{
	char errors[1024]={0};
	int file_fd=0;
	//create file
	file_fd=open("./video.yuv",O_CREAT|O_RDWR,0666);
	//ctx
	AVFormatContext *fmt_ctx=NULL;
	AVDictionary *option =NULL;
	//格式转换结构体
	struct SwsContext *img_convert_ctx = NULL;
	//packet
	int count =0;
	AVPacket pkt;
	char *devicename="/dev/video0";
	//注册设备
	avdevice_register_all();
	AVInputFormat *iformat=av_find_input_format("video4linux2");
	if(av_dict_set(&option,"pixel_format","nv12",0)<0) //指定格式
	{
		printf("set pixel_format error\n");
		return;
	}
	av_dict_set(&option,"video_size","640x480",0);
	av_dict_set(&option,"framerate","30",0);   //指定帧率
	//打开设备
	int ret=avformat_open_input(&fmt_ctx,devicename,iformat,&option);
	if (ret<0)
	{
		av_strerror(ret,errors,1024);
		return;
	}
	av_init_packet(&pkt);
	//转换前后frame
	AVFrame	*src_frame,*dst_frame;
	src_frame=av_frame_alloc();
	src_frame->width=640;
	src_frame->height=480;
	dst_frame=av_frame_alloc();
	dst_frame->width=640;
	dst_frame->height=480;

	//转换前后数据缓冲区
	uint8_t * src_buffer;
	uint8_t * dts_buffer;
	//构建转换后frame数据缓冲区
	dts_buffer=(uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P,
															 dst_frame->width,
															 dst_frame->height, 
															 1));
	av_image_fill_arrays(dst_frame->data, 
						dst_frame->linesize, 
						dts_buffer,
						AV_PIX_FMT_YUV420P,	//转换成为的格式
						dst_frame->width,
						dst_frame->height, 
						1);
	//构建转换前frame数据缓冲区
	src_buffer=(uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUYV422, 
															src_frame->width,
															src_frame->height, 
															1));
	av_image_fill_arrays(src_frame->data, 
						src_frame->linesize, 
						src_buffer,
						AV_PIX_FMT_YUYV422, //转换前的格式
						src_frame->width,
						src_frame->height, 
						1);
	//分配并返回SwsContext,使用sws_scale()进行缩放/转换操作
	img_convert_ctx = sws_getContext(src_frame->width, 
									src_frame->height, 
									AV_PIX_FMT_YUYV422, 
									dst_frame->width, 
									dst_frame->height, 
									AV_PIX_FMT_YUV420P, 
									SWS_BICUBIC,
									NULL, 
									NULL, 
									NULL); 

	while (ret=av_read_frame(fmt_ctx,&pkt)==0&&count++<300)
	{
		printf("packet size is %d(%p),count=%d\n",pkt.size,pkt.data,count);
		memcpy(src_frame->data[0],pkt.data,pkt.size);
		//格式转换操作
		sws_scale(img_convert_ctx, 
				(const uint8_t* const*)src_frame->data, 
				src_frame->linesize, 
				0, 
				480, 
				dst_frame->data,
				dst_frame->linesize);
		//具体的yuv各分量的大小结合yuv的格式结构来计算,转换后的结构为420
		write(file_fd,dst_frame->data[0],307200);   //Y分量
		write(file_fd,dst_frame->data[1],307200/4); //U分量
		write(file_fd,dst_frame->data[2],307200/4);	//V分量
		av_packet_unref(&pkt); 
	}
	close(file_fd);
	avformat_close_input(&fmt_ctx);
	av_log_set_level(AV_LOG_DEBUG);
}

当然上面的代码是将yuyv的视频格式转换为420p的视频格式,再上面的代码上对参数稍微作一些参数的修改也可以将packet.data(格式为yuyv)中的数据转换为其他的yuv格式或者是RGB格式。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值