近期在研究怎样使用SDL显示ffmpeg库解码后的YUV数据,在网上有很多的文章介绍调用SDL的步骤:
1 绑定显示窗口的句柄到SDL上
2 初始化SDL
3 设置显示的surface
4 设置Overlay表面
5 显示YUV数据大小进行自适应的显示
1 并且SDL调用分为两种显示模式,一种为windib,另一种为DX5
当SDL显示使用的windib模式时,SDL_SetVideoModel中的参数需要对图片的长宽和屏幕分辨率进行比较,取较大值作为该接口的长宽设置参数,才能使图像随窗口变化;使用DX5时,只需要传进原始图像的长宽即可
当对听一个视频文件进行窗口大小的变化时,不需要在变化时再次对SDL进行初始化,只需在SDL_DisplayYUVOverlay时,要显示一帧时,将窗口大小参数每次都将为参数穿进去就可以了,但是在播放器中难免要切换视频文件,如果文件的分辨率不一致,就要对SDL再次进行初始化了,由于SDL_SetVideoModel创建的Surface不再适用,所以切换后的视频文件可能会显示不正常。
2 在对YUV分量分辨进行拷贝赋值时,起初的版本是:
char *pbuf; //YUV数据
memcpy(u,pbuf,w*h);
memcpy(v,pubf+w*h*5/4,w*h/4);
memcpy(v,pubf+w*h,w*h/4);
通过这样的拷贝赋值后,在调用Display接口进行显示,发现在两台相同分辨率的电脑上,一台显示正常,另一台会出现花屏的现象,开始以为是初始化时创建的Suface的长宽值不对应造成的,最后经过检查,并不是这个问题导致的了,将以上的赋值代码修改为网上常见的版本:
{
SDL_Overlay overlay = SDL_CreateYUVOverlay(w, h, SDL_YV12_OVERLAY, screen);
char *outy = buffer;
char *outv = buffer+w*h;
char *outu= buffer+w*h*5/4;
for(y=0;y<screen->h ;y++)
{
op[0]=overlay->pixels[0]+overlay->pitches[0]*y;
op[1]=overlay->pixels[1]+overlay->pitches[1]*(y/2);
op[2]=overlay->pixels[2]+overlay->pitches[2]*(y/2);
memcpy(op[0],outy+y*w,w);
if(y%2 == 0)
{
memcpy(op[1],outu+w/2*y/2,w/2);
memcpy(op[2],outv+w/2*y/2,w/2);
}
}
其实两段代码对于YUV分量的拷贝过程基本是相同的,但是SDL做了一个处理很关键,对于overlay->pitches[0]这个参数,他决定了在拷贝分量时,能在拷贝完一行的像素点后,确定下一行的起始位置。
如果屏幕的分辨率要大于图片宽度,则会选择将差值的宽度值留空,可能不做赋值处理(处理有待考究),这样保证图片的一行像素拷贝在屏幕的对应行上,而起始的那段代码会出现由于不能确定第一行复制的结束为止,而会紧接着继续拷贝分量值,从而在某些电脑会出现花屏问题了。