Ffmpeg中AVFrame数据保存成YUV--讨论AVFrame的linesize

目录

YUV播放器

AVFrame中保存成YUV实现

linesize的意义

实测(PC机-64bits-win10)

总结:


YUV播放器

首先要有一个YUVplayer用来播放测试的YUV数据,雷神改良过的YUV播放器:

修改了一个YUV/RGB播放器_雷霄骅的博客-CSDN博客

播放器播放界面如下,支持YUV、RGB格式播放。

AVFrame中保存成YUV实现

        如下实现了两种保存YUV的方式。

        第一种比较简单,直接将Y数据首地址指向的内存保存到文件里,长度直接按width x height计算,同样处理U、V数据。这种保存方式适用于Y、U、V的数据都是连续存储,中间没有任何跨度。

        但现实情况是如果图像的width和CPU的对齐不匹配,会出现数据不是连续存储的情况。比如图片宽高是720x576,而CPU是按照64字节对齐的方式存储数据,则Y数据每行的最后48字节是空的,保存YUV时只需要保存Y每行的前720字节。

void yuvStore(AVFrame *pFrame){
	if(NULL == pFrame){
		printf("NULL = pFrame !\n");
	}
	
	char file_name[128] = {0};
	time_t t;
	struct tm *timer;
	time(&t);
	timer = localtime(&t);
	
	switch(pFrame->format){
		case AV_PIX_FMT_YUV420P:
		{
			sprintf(file_name,"yuv420p_%dx%d_%d-%d-%d-%d-%d-%d.yuv",
			pFrame->width,pFrame->height,timer->tm_year+1900,timer->tm_mon+1,timer->tm_mday,timer->tm_hour,timer->tm_min,timer->tm_sec);
			FILE *fp_yuv = fopen(file_name,"wb+");
			if(NULL == fp_yuv){
				printf("NULL == fp_yuv!\n");
			}
			printf("AV_PIX_FMT_YUV420P, width = %d, height = %d\n",pFrame->width,pFrame->height);
			printf("",pFrame->linesize[0],pFrame->linesize[1],pFrame->linesize[2]);

			#if 0 //方法1
			int y_size = pFrame->width*pFrame->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
			#else //方法2
			for(int i=0;i<pFrame->height;i++){
				fwrite(pFrame->data[0],1,pFrame->width,fp_yuv);    //Y 
				pFrame->data[0]+=pFrame->linesize[0];
			}

			for(int i=0;i<pFrame->height/2;i++){
				fwrite(pFrame->data[1],1,pFrame->width/2,fp_yuv);   //U
				pFrame->data[1]+=pFrame->linesize[1];
			}			

			for(int i=0;i<pFrame->height/2;i++){
				fwrite(pFrame->data[2],1,pFrame->width/2,fp_yuv);   //V
				pFrame->data[2]+=pFrame->linesize[2];
			}	
			#endif

			fclose(fp_yuv);
            break;
		}

		default:
			printf("unsupport format !\n");
			break;
	}
}

linesize的意义

AVFrame包含如下成员(ffmpeg5.1):

        uint8_t *data[AV_NUM_DATA_POINTERS];//指针数组,指向每个通道地址。比如YUV420P的数据,data[0]指向Y数据,data[1]指向U数据,data[2]指向V数据
        int linesize[AV_NUM_DATA_POINTERS];//可以理解为每行数据的跨度。
        int width, height;//如果是视频、描述宽高
        int nb_samples;//如果是音频、描述音频采样点
        int format;//格式,视频见AVPixelFormat,音频见AVSampleFormat

        linesize通常等于图像的宽度。但如果图像的宽度和CPU保存数据的对齐宽度不匹配,比如CPU是按照64字节进行对齐保存的,而图像原始宽度是720(64对齐后是768),解码出来的YUV,每行数据会按照64对齐,也就是Y通道的第一行首地址和第二行首地址之间的内存offset不是720,而是768。 linesize描述的是图片每两行之间的内存跨度。

实测(PC机-64bits-win10)

        1920x1080的分辨率,1920是64对齐,linesize=width。

        720x576的分辨率,720不是64对齐,linesize=width+padding。

总结:

        linesize描述了图像每行之间内存的offset,可能等于图像width。取决于图像宽度是否=CPU对齐后的存储宽度。

tips:

要先参考这个文章搭建ffmpeg的win测试环境。

VScode配置ffmpeg+sdl2.0开发环境(window+MinGW)_小葫芦写代码的博客-CSDN博客_vscode ffmpeg

测试工程地址:将下边的工程下载后,就可以测试这个API

基于vscode构建的ffmpeg+sdl2.0的视频播放器demo_vscode设置sdl2环境-桌面系统文档类资源-CSDN下载

使用ffmpeg生成一段分辨率为720*576的视频

ffmpeg -i test.mp4 -s 720x576 -f mp4 720.mp4

猜你喜欢

转载自blog.csdn.net/weixin_37515325/article/details/128540310