目录
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对齐后的存储宽度。
要先参考这个文章搭建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