struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param);
创建转换上下文,参数分析:
flags参数是指选择的转换算法,如果没有精准的要求,这些算法差别不大,一般用SWS_BILINEAR参数,可选类型定义在//libswscale/swscale.h内,如下:
Intel® Pentium® Silver N6000 @ 1.10GHz 4
Thread(s) per core: 1 Core(s) per socket: 4
1920x1080p25 UYVY422–>NV12
SWS_BILINEAR–>30-40ms
SWS_FAST_BILINEAR–>10ms
#define SWS_FAST_BILINEAR 1
#define SWS_BILINEAR 2
#define SWS_BICUBIC 4
#define SWS_X 8
#define SWS_POINT 0x10
#define SWS_AREA 0x20
#define SWS_BICUBLIN 0x40
#define SWS_GAUSS 0x80
#define SWS_SINC 0x100
#define SWS_LANCZOS 0x200
#define SWS_SPLINE 0x400
i9-9900KF CPU @ 3.60GHz 8核16线程下 3840x2160时,yuv420p->uyvy422,用SWS_BILINEAR转化一帧视频消耗31ms,用SWS_POINT消耗3ms。以上各个参数分析
AV_PIX_FMT_UYVY422 1080p50转成AV_PIX_FMT_UYVY422 720p50时,SWS_FAST_BILINEAR和SWS_AREA消耗cpu最小,并且负载均衡,其内部应该开启多个线程一起转换了。其他几个参数负载极不均衡而且对cpu消耗很大。
srcFilter, 输入图像的滤波器信息, 如果不用赋值NULL
dstFilter, 输出图像的滤波器信息, 如果不用赋值NULL
param 特定缩放算法需要的参数,如果不用赋值NULL
int attribute_align_arg sws_scale(struct SwsContext *c, const uint8_t * const srcSlice[], const int srcStride[], int srcSliceY, int srcSliceH, uint8_t *const dst[], const int dstStride[])
真正做转换的函数,参数分析:
这个转换是深拷贝,需要给dst申请空间,实测yuv420->uyvy422每执行一次就输出一帧,开始不需要多帧输入填充缓存。
sws_scale转换包含像素格式转换和缩放拉伸转换,输入输出可以是rgb或yuv中的任意一种。
srcSlice对应frame->data
yuv各种格式在AVFrame::data[]中的存储方式,另一篇博客:YUV的plannar,packet及semi-planar格式
srcStride这个参数填入frame->linesize linesize[]
数组中保存的是对应通道的数据宽度
linesize[0]——-Y分量的宽度
linesize[1]——-U分量的宽度
linesize[2]——-V分量的宽度
linesize[0]的值并不一定等于图片的宽度,有时候为了对齐各解码器的CPU(32位/64位),实际尺寸会大于图片的宽度,这点在我们编程时(比如OpengGL硬件转换/渲染)要特别注意,否则解码出来的图像会异常,增加linesize[0]的值使得,linesize[0]/32或linesize[0]/64为整数,叫做对齐cpu字节。
以上是planner格式,如果是packet格式,如1920x1080 uyvy422,linesize[1],linesize[2]都为0,因为只有一个分量了,此时linesize[0]为1920*2 = 3840
实测yuv422格式,1080i50视频,在x86上没有格式对齐, frame->linesize[0]为1920x2。此时frame->linesize[0] x frame->width即为此帧frame数据大小。
如果是planner格式的像素,frame的大小应该是(frame->linesize[0]+frame->linesize[1]+frame->linesize[2]) x frame->width。如果用宽x高x像素格式字节数算的话,当像需要补齐字节时,得到的值会小于frame包含的真实值,进行memcpy赋值时会少复制内容。
参数int srcSliceY, int srcSliceH,定义在输入图像上处理区域
srcSliceY是起始位置,srcSliceH是处理多少行。如果srcSliceY=0,srcSliceH=height,表示一次性处理完整个图像。这种设置是为了多线程并行,例如可以创建两个线程,第一个线程处理 [0, h/2-1]行,第二个线程处理 [h/2, h-1]行。并行处理加快速度。
参数uint8_t *const dst[], const int dstStride[]定义输出图像信息(输出的每个颜色通道数据指针,每个颜色通道行字节数)
与其类似的函数还有: sws_getCachedContext ,区别在于: sws_getContext 可以用于多路码流转换,为每个不同的码流都指定一个不同的转换上下文,而 sws_getCachedContext 只能用于一路码流转换。
/** * Free the swscaler context swsContext. * If swsContext is NULL, then does nothing. */ void sws_freeContext(struct SwsContext *swsContext);
释放sws_scale
字节对齐解释:比如704*576分辨率的视频,它的width=704,height=576,摄像机芯片一般会要求64或者128对齐,当128位对齐时,704不能被128整除,需要在每一行结尾补64字节0x00占位,它的linesize也就是每一行的长度768。