ffmpeg源碼簡析(一)libswscale中的sws_getContext,sws_scale()等


前言

libswscale 是一個主要用於處理圖片像素數據的類庫。可以完成圖片像素格式的轉換,圖片的拉伸,圖像的濾波等工作。libswscale 常用的函數數量很少,一般情況下就 3 個:

sws_getContext():初始化一個SwsContext。
sws_scale():處理圖像數據。
sws_freeContext():釋放一個SwsContext。

SwsContext 結構體

SwsContext 是轉換格式的上下文結構體,也是使用 libswscale 的時候一個貫穿始終的結構體,這個結構體的定義很復雜,里面包含了 libswscale 所需要的全部變量。

一個個分析這些變量是不太現實的,這里只簡單說明下其中的幾個變量,即定義源圖像和目標圖像的寬高、像素格式和圖像算法等信息(就是 sws_getContext() 函數中的參數)。


1. sws_getContext()

sws_getContext() 是對函數內部定義的 SwsContext 結構體中的各個成員變量進行賦值,成功執行的話返回該 SwsContext,否則返回 NULL,返回的 SwsContext 在之后的 sws_scale() 和 sws_freeContext() 皆會用到。其函數原型:

struct SwsContext *sws_getContext(
            int srcW, /* 源圖像的寬度 */
            int srcH, /* 源圖像的寬度 */
            enum AVPixelFormat srcFormat, /* 源圖像的像素格式 */
            int dstW, /* 目標圖像的寬度 */
            int dstH, /* 目標圖像的高度 */
            enum AVPixelFormat dstFormat, /* 目標圖像的像素格式 */
            int flags,/* 選擇縮放算法(只有當源圖像和目標圖像大小不同時有效),一般選擇SWS_FAST_BILINEAR */
            SwsFilter *srcFilter, /* 源圖像的濾波器信息, 若不需要傳NULL */
            SwsFilter *dstFilter, /* 目標圖像的濾波器信息, 若不需要傳NULL */
            const double *param /* 特定縮放算法需要的參數,默認為NULL */
            );

與其類似的函數還有: sws_getCachedContext ,區別在於: sws_getContext 可以用於多路碼流轉換,為每個不同的碼流都指定一個不同的轉換上下文,而 sws_getCachedContext 只能用於一路碼流轉換。


以下是一個 sws_getContext 的簡單例子:

struct SwsContext *swsContext = sws_getContext(in_width, in_height, AV_PIX_FMT_BGRA, out_width, out_height, AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL);

將 sws_getContext 的返回值賦值給 swsContext。


2. sws_freeContext()

void sws_freeContext(struct SwsContext *swsContext);

用來釋放 sws_getContext() 返回的 SwsContext 對象指針。


3. sws_scale()

sws_scale() 函數主要是用來做視頻像素格式和分辨率的轉換,其優勢在於:可以在同一個函數里實現:1.圖像色彩空間轉換, 2.分辨率縮放,3.前后圖像濾波處理。不足之處在於:效率相對較低,不如 libyuv 或 shader,其關聯的函數就是上面的 sws_getContext() 和 sws_freeContext()。

它的聲明位於 libswscale\swscale.h,如下所示:

int sws_scale(struct SwsContext *c,
              const uint8_t *const srcSlice[],
              const int srcStride[],
              int srcSliceY, int srcSliceH,
              uint8_t *const dst[], const int dstStride[]);

參數說明:

  • SwsContext *c:轉換格式的上下文結構體,也就是 sws_getContext() 函數返回的結果。
  • srcSlice[]:源圖像的每個顏色通道的數據指針。其實就是解碼后的 AVFrame 中的 data[] 數組。因為不同像素的存儲格式不同,所以 srcSlice[] 數組也有可能不同。
  • srcStride[]:源圖像的每個顏色通道的跨度。也就是每個通道的行字節數,對應的是解碼后的 AVFrame 中的 linesize[] 數組,根據它可以確立下一行的起始位置。
  • srcSliceY、int srcSliceH:定義在源圖像上處理區域,srcSliceY 是起始位置,srcSliceH 是處理多少行。如果 srcSliceY=0,srcSliceH=height,表示一次性處理完整個圖像。這種設置是為了多線程並行,例如可以創建兩個線程,第一個線程處理 [0, h/2-1] 行,第二個線程處理 [h/2, h-1] 行,並行處理加快速度。
  • dst[]、dstStride[]:定義目標圖像信息(目標圖像輸出的每個顏色通道數據指針每個顏色通道行字節數)。

最后再整理一次,要使用 swscale,只要使用 sws_getContext() 進行初始化、sws_scale() 進行主要轉換、sws_freeContext() 結束,即可完成全部動作。


4. 簡單示例

以下為一個簡單的范例程式,可從 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

img


Output Image

img


參考:

FFmpeg源代碼簡單分析:libswscale的sws_getContext()

FFmpeg源代碼簡單分析:libswscale的sws_scale()

sws_scale函數的用法-具體應用



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM