用FFMPEG 开发在DirectShow 环境下生成FLV的筛选器(Filter)的时候,碰到了一个比较古怪的问题。因为该筛选器的输入格式需要支持常见的RGB,和YUV格式。但是在输入是RGB格式的时候,最终生成的视频图像是翻转的。而用YUV格式确实没有问题的。
分析了一下程序,因为ffmpeg支持的最终存入FLV的格式是YUV420P,需要调用sws_scale进行图像的格式转换,应该是调用sws_scale进行图像格式转换的时候发生的图像反转。虽然问题很显然,但是却一直找不到好的办法,这个问题困扰了好久,也查看了ffmpeg的源代码。本想也一段代码,先把RGB格式的图像先手工做一次反转,再通过sws_scale进行处理,那样负负得正正好解决问题,当然实现这样的反转代码也比较简单,稍微花点时间就可以搞定。但是进行编解码处理的程序关键是性能,这样处理,因为图像反转操作,白白损失了大量的cpu。后来发现其实,有一个非常巧妙的方法可以解决这个问题。或许ffmpeg在开发的时候,他们早已考虑到了这个问题,已经预留了这个后门了。 办法是这样的: 先看看sws_scale的函数定义 int sws_scale(struct SwsContext *ctx,uint8_t* src[],int srcStride[], 其中src和srcStride定义了输入图像的四个平面的数据起始指针和四个平面中每一行包含的像素的个数。 dst和dstStride是输出变量,定义的是输出图像的四个平面的数据起始指针和四个平面包含的数据的大小。 为什么一个图像有四个平面,可以找一下YUV格式的一些详细介绍就可以明白。 当然,RGB格式是按照紧凑格式进行编码的,因此只有一个平面,也就是说只要设置src[0]就可以,src[1],src[2],src[3]都为NULL。 我们就在设置src[0]和srcStride[0]的地方做文章。 按照一般处理src[0]和srcStride[0]分别设置为起始图像数据的开始和图像每一行的像素个数。 那如果把src[0] 设置为 width * ( height - 1) srcStride[0] = -height 结果会如何呢?是不是就会把图像倒过来呢? 实际确实如此。进行图像倒置的操作尽然如此简单。这样避免了人为再添加一次图像的反转操作,提高了编码的性能。
如果图像格式是YUV420,可以这样实现图像翻转: picture->data[0] += picture->linesize[0] * (height - 1); picture->linesize[0] *= -1; picture->data[1] += picture->linesize[1] * (height / 2 - 1); picture->linesize[1] *= -1; picture->data[2] += picture->linesize[2] * (height / 2 - 1); picture->linesize[2] *= -1;
|