FFmpeg: FFmepg中的sws_scale() 函數分析


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

1.sws_getContext():

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 只能用於一路碼流轉換。

2.sws_freeContext

// 釋放sws_scale
void sws_freeContext(struct SwsContext *swsContext);

真正用來做轉換的函數則是: sws_scale() ,其函數定義如下:

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[]);

下面對其函數參數進行詳細說明:

1.參數 SwsContext *c, 轉換格式的上下文。也就是 sws_getContext 函數返回的結果。
2.參數 const uint8_t *const srcSlice[], 輸入圖像的每個顏色通道的數據指針。其實就是解碼后的AVFrame中的data[]數組。因為不同像素的存儲格式不同,所以srcSlice[]維數
也有可能不同。
以YUV420P為例,它是planar格式,它的內存中的排布如下:
YYYYYYYY UUUU VVVV
使用FFmpeg解碼后存儲在AVFrame的data[]數組中時:
data[0]——-Y分量, Y1, Y2, Y3, Y4, Y5, Y6, Y7, Y8……
data[1]——-U分量, U1, U2, U3, U4……
data[2]——-V分量, V1, V2, V3, V4……
linesize[]數組中保存的是對應通道的數據寬度 ,
linesize[0]——-Y分量的寬度
linesize[1]——-U分量的寬度
linesize[2]——-V分量的寬度

而RGB24,它是packed格式,它在data[]數組中則只有一維,它在存儲方式如下:
data[0]: R1, G1, B1, R2, G2, B2, R3, G3, B3, R4, G4, B4……
這里要特別注意,linesize[0]的值並不一定等於圖片的寬度,有時候為了對齊各解碼器的CPU,實際尺寸會大於圖片的寬度,這點在我們編程時(比如OpengGL硬件轉換/渲染)要特別注意,否則解碼出來的圖像會異常。

3.參數const int srcStride[],輸入圖像的每個顏色通道的跨度。.也就是每個通道的行字節數,對應的是解碼后的AVFrame中的linesize[]數組。根據它可以確立下一行的起始位置,不過stride和width不一定相同,這是因為:
a.由於數據幀存儲的對齊,有可能會向每行后面增加一些填充字節這樣 stride = width + N;
b.packet色彩空間下,每個像素幾個通道數據混合在一起,例如RGB24,每個像素3字節連續存放,因此下一行的位置需要跳過3*width字節。

4.參數int srcSliceY, int srcSliceH,定義在輸入圖像上處理區域,srcSliceY是起始位置,srcSliceH是處理多少行。如果srcSliceY=0,srcSliceH=height,表示一次性處理完整個圖像。這種設置是為了多線程並行,例如可以創建兩個線程,第一個線程處理 [0, h/2-1]行,第二個線程處理 [h/2, h-1]行。並行處理加快速度。
5.參數uint8_t *const dst[], const int dstStride[]定義輸出圖像信息(輸出的每個顏色通道數據指針,每個顏色通道行字節數)

代碼示例:將解碼后的數據轉換成1280*720的RGBA8888 格式

1. 定義轉換格式的上下文

vctx = sws_getCachedContext(
                        vctx,
                        frame->width, // 源圖像的寬度
                        frame->height, //  源圖像的高度
                        (AVPixelFormat)frame->format,
                        outWidth,
                        outHeight,
                        AV_PIX_FMT_RGBA,
                        SWS_FAST_BILINEAR,
                        0, 0, 0
                        );

2. 開始轉換

int outWidth = 1280;
int outHeight = 720;
char *rgb = new char[1920*1080*4];
uint8_t *data[AV_NUM_DATA_POINTERS] = {0};
data[
0] = (uint8_t *)rgb; int lines[AV_NUM_DATA_POINTERS] = {0}; lines[0] = outWidth * 4; int h = sws_scale(vctx, (const uint8_t **)frame->data, frame->linesize, 0, frame->height, data, lines ); 

參考鏈接:

1.sws_scale概念講解

2.FFmpeg視頻解碼中的YUV420P格式

 


免責聲明!

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



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