ffmpeg 中 swscale 的用法

如果想将某个PixelFormat转换至另一个PixelFormat,例如,将YUV420P转换成YUYV422,或是想变换图的大小,都可以使用swscale达成。

其中,PixelFormat 的列表在 libavutil/pixfmt.h 内定义。

swscale的用法可以参考libswscale/swscale-example.c的sample code。主要function有三个

  • sws_getContext()
  • sws_scale()
  • sws_freeContext()

其中,我们可以把sws_getContext() 看成初始化函数,把sws_freeContext()看成结束函数。这两个函数分别在起始及结束各执行一次即可。

真正主要的函数,是sws_scale()。

sws_getContext() 的宣告如下

SwsContext *sws_getContext(int srcW, int srcH, enum PixelFormat srcFormat, int dstW, int dstH, enum PixelFormat dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param)

总共有十个参数,其中,较重要的是前七个;
前三个参数分别代表 source 的宽、高及PixelFormat;
四到六个参数分别代表 destination 的宽、高及PixelFormat;
第七个参数则代表要使用哪种scale的方法;此参数可用的方法可在 libswscale/swscale.h 内找到。

最后三个参数,如无使用,可以都填上NULL。

sws_getContext会回传一个 SwsContext struct,我们可以把这个 struct 看成是个 handler,之后的sws_scale和sws_freeContext皆会用到。

以下是一个sws_getContext的简单例子:

struct SwsContext *img_convert_ctx;
img_convert_ctx = sws_getContext(in_width, in_height, PIX_FMT_YUV420P,
out_width, out_height, PIX_FMT_YUV420P, SWS_POINT,
NULL, NULL, NULL);

一开始,我们宣告img_convert_ctx 为指向 SwsContext 的一个 pointer;接著,我们将 sws_getContext 的回传值赋予给img_convert_ctx。

注意sws_getContext的参数;in_width及in_height分别代表 source 的宽及高,out_width及out_height分别代表转换后的宽与高;input 和 output 的 PixelFormat 皆为 YUV420P;使用SWS_POINT的scale方法。

初始化完成后,接著就要进行主要的 scale 动作;我们透过 sws_scale() 完成。sws_scale() 的宣告如下

int sws_scale(SwsContext *c, uint8_t* src[], int srcStride[], int srcSliceY, int srcSliceH, uint8_t* dst[], int dstStride[])

总共有七个参数;
第一个参数即是由 sws_getContext 所取得的参数。
第二个 src 及第六个 dst 分别指向input 和 output 的 buffer。
第三个 srcStride 及第七个 dstStride 分别指向 input 及 output 的 stride;如果不知道什么是 stride,姑且可以先把它看成是每一列的 byte 数。
第四个 srcSliceY,就注解的意思来看,是指第一列要处理的位置;这里我是从头处理,所以直接填0。想知道更详细说明的人,可以参考 swscale.h 的注解。
第五个srcSliceH指的是 source slice 的高度。

举一个例子如下

sws_scale(img_convert_ctx, inbuf, inlinesize, 0, in_height, outbuf, outlinesize);

这里应该比较好懂,可以参考上面的参数说明。

最后,全部处理完后,需呼叫sws_freeContext() 结束。用法很简单,把sws_getContext 取得的参数填入即可。如下

sws_freeContext(img_convert_ctx);

最后再整理一次,要使用swscale,只要使用 sws_getContext() 进行初始化、sws_scale() 进行主要转换、sws_freeContext() 结束,即可完成全部动作。

以下为一个简单的范例程式,可从foreman.yuv内取出第一张图,转换大小后存成另一张图。

=====================================================================================

/* 
* 需设定 SRCFILE 及 DSTFILE, 长宽等资讯 
* 需 link libswscale 
* 主要有三个 function 
* sws_getContext() 是 initial 用, sws_freeContext() 是结束用 
* sws_scale() 是主要运作的 function 
* 预设只会转换第一张 YUV, 如果要转换整个档, 可以把 Decoding loop 的注解拿掉 
*/ 

#include "libswscale/swscale.h" 

#define SRCFILE "foreman_cif.yuv" 
#define DSTFILE "out.yuv" 

int main() 
{ 
// 设定原始 YUV 的长宽 
const int in_width = 352; 
const int in_height = 288; 
// 设定目的 YUV 的长宽 
const int out_width = 640; 
const int out_height = 480; 

const int read_size = in_width * in_height * 3 / 2; 
const int write_size = out_width * out_height * 3 / 2; 
struct SwsContext *img_convert_ctx; 
uint8_t *inbuf[4]; 
uint8_t *outbuf[4]; 
int inlinesize[4] = {in_width, in_width/2, in_width/2, 0}; 
int outlinesize[4] = {out_width, out_width/2, out_width/2, 0}; 

uint8_t in[352*288*3>>1]; 
uint8_t out[640*480*3>>1]; 

FILE *fin = fopen(SRCFILE, "rb"); 
FILE *fout = fopen(DSTFILE, "wb"); 

if(fin == NULL) { 
printf("open input file %s error.\n", SRCFILE); 
return -1; 
} 

if(fout == NULL) { 
printf("open output file %s error.\n", DSTFILE); 
return -1; 
} 

inbuf[0] = malloc(in_width*in_height); 
inbuf[1] = malloc(in_width*in_height>>2); 
inbuf[2] = malloc(in_width*in_height>>2); 
inbuf[3] = NULL; 

outbuf[0] = malloc(out_width*out_height); 
outbuf[1] = malloc(out_width*out_height>>2); 
outbuf[2] = malloc(out_width*out_height>>2); 
outbuf[3] = NULL; 

// ********* Initialize software scaling ********* 
// ********* sws_getContext ********************** 
img_convert_ctx = sws_getContext(in_width, in_height, PIX_FMT_YUV420P, 
out_width, out_height, PIX_FMT_YUV420P, SWS_POINT, 
NULL, NULL, NULL); 
if(img_convert_ctx == NULL) { 
fprintf(stderr, "Cannot initialize the conversion context!\n"); 
return -1; 
} 

fread(in, 1, read_size, fin); 

memcpy(inbuf[0], in, in_width*in_height); 
memcpy(inbuf[1], in+in_width*in_height, in_width*in_height>>2); 
memcpy(inbuf[2], in+(in_width*in_height*5>>2), in_width*in_height>>2); 

// ********* 主要的 function ****** 
// ********* sws_scale ************ 
sws_scale(img_convert_ctx, inbuf, inlinesize, 
0, in_height, outbuf, outlinesize); 

memcpy(out, outbuf[0], out_width*out_height); 
memcpy(out+out_width*out_height, outbuf[1], out_width*out_height>>2); 
memcpy(out+(out_width*out_height*5>>2), outbuf[2], out_width*out_height>>2); 

fwrite(out, 1, write_size, fout); 

// ********* 结束的 function ******* 
// ********* sws_freeContext ******* 
sws_freeContext(img_convert_ctx); 

fclose(fin); 
fclose(fout); 

return 0; 
} 


 

=====================================================================================

以下两张图为执行结果

Input Image

foreman_352x288

Output Image

foreman_640x480

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章