【Android FFMPEG 開發】FFMPEG AVFrame 圖像格式轉換 YUV -> RGBA ( 獲取 SwsContext | 初始化圖像數據存儲內存 | 圖像格式轉換 )



I . FFMPEG AVFrame 圖像數據幀處理 前置操作



FFMPEG 解碼 AVPacket 數據到 AVFrame 數據前置操作 :


① FFMPEG 初始化 : 參考博客 【Android FFMPEG 開發】FFMPEG 初始化 ( 網絡初始化 | 打開音視頻 | 查找音視頻流 )

② FFMPEG 獲取 AVStream 音視頻流 : 參考博客 【Android FFMPEG 開發】FFMPEG 獲取 AVStream 音視頻流 ( AVFormatContext 結構體 | 獲取音視頻流信息 | 獲取音視頻流個數 | 獲取音視頻流 )

③ FFMPEG 獲取 AVCodec 編解碼器 : 參考博客 【Android FFMPEG 開發】FFMPEG 獲取編解碼器 ( 獲取編解碼參數 | 查找編解碼器 | 獲取編解碼器上下文 | 設置上下文參數 | 打開編解碼器 )

④ FFMPEG 讀取音視頻流中的數據到 AVPacket : 參考博客 【Android FFMPEG 開發】FFMPEG 讀取音視頻流中的數據到 AVPacket ( 初始化 AVPacket 數據 | 讀取 AVPacket )

⑤ FFMPEG 解碼 AVPacket 數據到 AVFrame : 參考博客 【Android FFMPEG 開發】FFMPEG 解碼 AVPacket 數據到 AVFrame ( AVPacket->解碼器 | 初始化 AVFrame | 解碼爲 AVFrame 數據 )



II . FFMPEG 解碼 AVPacket 數據到 AVFrame 流程



FFMPEG 解碼 AVPacket 數據到 AVFrame 流程 :


〇 前置操作 : FFMPEG 環境初始化 , 獲取 AVStream 音視頻流 , 獲取 AVCodec 編解碼器 , 讀取音視頻流中的數據到 AVPacket , 解碼 AVPacket 數據到 AVFrame , 然後才能進行下面的操作 ;


① 獲取 SwsContext : sws_getContext ( )

SwsContext *swsContext = sws_getContext(
        //源圖像的 寬 , 高 , 圖像像素格式
        avCodecContext->width, avCodecContext->height, avCodecContext->pix_fmt,
        //目標圖像 大小不變 , 不進行縮放操作 , 只將像素格式設置成 RGBA 格式的
        avCodecContext->width, avCodecContext->height, AV_PIX_FMT_RGBA,
        //使用的轉換算法 , FFMPEG 提供了許多轉換算法 , 有快速的 , 有高質量的 , 需要自己測試
        SWS_BILINEAR,
        //源圖像濾鏡 , 這裏傳 NULL 即可
        0,
        //目標圖像濾鏡 , 這裏傳 NULL 即可
        0,
        //額外參數 , 這裏傳 NULL 即可
        0
        );

② 初始化圖像數據存儲空間 : av_image_alloc ( )

av_image_alloc(dst_data, dst_linesize,
               avCodecContext->width, avCodecContext->height, AV_PIX_FMT_RGBA,
               1);

③ 轉換圖像格式 : sws_scale ( )

sws_scale(
        //SwsContext *swsContext 轉換上下文
        swsContext,
        //要轉換的數據內容
        avFrame->data,
        //數據中每行的字節長度
        avFrame->linesize,
        0,
        avFrame->height,
        //轉換後目標圖像數據存放在這裏
        dst_data,
        //轉換後的目標圖像行數
        dst_linesize
        );


III. FFMPEG 解碼前後的圖像格式



AVPacket 數據解碼後的數據存儲在 AVFrame 結構體中 , 如果是視頻數據 , 那麼存儲的是一幀圖像 , 圖像的像素格式是 YUV 格式的 , 一般 Android 中繪製需要使用 ARGB 的像素格式 , 這裏需要將圖像的存儲格式由 YUV 格式轉爲 ARGB 格式 ;



IV . FFMPEG 獲取 SwsContext



1 . SwsContext 結構體 : 轉換圖像格式 , 首先要獲取 SwsContext 結構體指針 , 在該 SwsContext 結構體中封裝了圖像轉換相關的參數信息 , 如 源圖像 目標圖像的寬高 , 像素格式信息等 ; 調用 sws_getContext ( ) 方法可以獲取 SwsContext * 結構體指針 ;


2 . SwsContext ( ) 函數原型 : 爲 SwsContext 結構體分配內存 , 並返回其結構體指針 ;


① int srcW 參數 : 源圖像的寬度 ;

② int srcH 參數 : 源圖像的高度 ;

③ enum AVPixelFormat srcFormat 參數 : 源圖像的像素格式 ;

④ int dstW 參數 : 目標圖像的寬度 ;

⑤ int dstH 參數 : 目標圖像的高度 ;

⑥ enum AVPixelFormat dstFormat 參數 : 目標圖像的像素格式 ;

⑦ int flags 參數 : 使用的轉換算法 , 可以選擇高速度 , 或者高質量等參數 ;

⑧ SwsFilter *srcFilter 參數 : 源圖像濾鏡 ;

⑨ SwsFilter *dstFilter 參數 : 目標圖像濾鏡 ;

⑩ const double *param 參數 : 額外參數 ;

/**
 * Allocate and return an SwsContext. You need it to perform
 * scaling/conversion operations using sws_scale().
 *
 * @param srcW the width of the source image
 * @param srcH the height of the source image
 * @param srcFormat the source image format
 * @param dstW the width of the destination image
 * @param dstH the height of the destination image
 * @param dstFormat the destination image format
 * @param flags specify which algorithm and options to use for rescaling
 * @param param extra parameters to tune the used scaler
 *              For SWS_BICUBIC param[0] and [1] tune the shape of the basis
 *              function, param[0] tunes f(1) and param[1] f´(1)
 *              For SWS_GAUSS param[0] tunes the exponent and thus cutoff
 *              frequency
 *              For SWS_LANCZOS param[0] tunes the width of the window function
 * @return a pointer to an allocated context, or NULL in case of error
 * @note this function is to be removed after a saner alternative is
 *       written
 */
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);

3 . 獲取 SwsContext 代碼示例 :

    SwsContext *swsContext = sws_getContext(
            //源圖像的 寬 , 高 , 圖像像素格式
            avCodecContext->width, avCodecContext->height, avCodecContext->pix_fmt,

            //目標圖像 大小不變 , 不進行縮放操作 , 只將像素格式設置成 RGBA 格式的
            avCodecContext->width, avCodecContext->height, AV_PIX_FMT_RGBA,

            //使用的轉換算法 , FFMPEG 提供了許多轉換算法 , 有快速的 , 有高質量的 , 需要自己測試
            SWS_BILINEAR,

            //源圖像濾鏡 , 這裏傳 NULL 即可
            0,

            //目標圖像濾鏡 , 這裏傳 NULL 即可
            0,

            //額外參數 , 這裏傳 NULL 即可
            0
            );


V . FFMPEG 初始化圖像數據存儲內存



1 . 圖像數據保存 : 需要兩個變量來進行存儲 , 一個是指針 , 指向一塊內存 , 該內存中存儲實際的圖像數據 , 一個是 int 數值 , 存儲該內存中存儲了多少數據 ;

① 指針 : 將圖像數據保存到 uint8_t *dst_data[4] 指針數組中的指針元素指向的內存中 ;

② 行數 : int dst_linesize[4] 中存儲其行數 , 代表了上面指針指向的內存每行存儲了多少數據 ;


2 . 圖像數據指針的操作 :

① 初始化 : 這個內存需要用專門的函數 av_image_alloc ( ) 進行初始化 ;

② 釋放 : 這個指針需要使用專門的函數 void av_freep(void *ptr) 進行釋放 ;


3 . av_image_alloc ( ) 函數原型 : 根據圖像的寬高 , 像素格式 , 爲 相應的 指向圖像數據的指針 和 行數 進行初始化 ;


① uint8_t *pointers[4] 參數 : 指向圖像數據的指針 , 這是四個指針 , 這裏只是用了一個 , 也就是第一個 ;

② int linesizes[4] 參數 : 存儲每個圖像數據存儲的數據行數 ;

③ int w 參數 : 圖像的寬度 ;

④ int h 參數 : 圖像的高度 ;

⑤ enum AVPixelFormat pix_fmt : 圖像的像素格式 , ARGB 格式的 ;

⑥ int align 參數 : 設置 1 即可 ;

/**
 * Allocate an image with size w and h and pixel format pix_fmt, and
 * fill pointers and linesizes accordingly.
 * The allocated image buffer has to be freed by using
 * av_freep(&pointers[0]).
 *
 * @param align the value to use for buffer size alignment
 * @return the size in bytes required for the image buffer, a negative
 * error code in case of failure
 */
int av_image_alloc(uint8_t *pointers[4], int linesizes[4],
                   int w, int h, enum AVPixelFormat pix_fmt, int align);

4 . av_image_alloc ( ) 代碼示例 :

//指針數組 , 數組中存放的是指針
uint8_t *dst_data[4];

//普通的 int 數組
int dst_linesize[4];

//初始化 dst_data 和 dst_linesize , 爲其申請內存 , 注意使用完畢後需要釋放內存
av_image_alloc(dst_data, dst_linesize,
               avCodecContext->width, avCodecContext->height, AV_PIX_FMT_RGBA,
               1);


VI . FFMPEG 初圖像格式轉換



1 . 準備工作完畢 : 轉換使用的上下文 SwsContext , 轉換後的數據存儲 指針 和 行數 , 準備就緒後 , 可以開始轉換 AVFrame 中的 YUV 像素格式的圖像爲 RGBA 像素格式 ;


2 . 轉換使用方法 : 調用 sws_scale ( ) 方法 , 執行轉換操作 ;


3 . sws_scale ( ) 函數原型 : 轉換圖像像素格式 ;


① struct SwsContext *c 參數 : 轉換上下文 ;

② const uint8_t *const srcSlice[] 參數 : 源圖像數據指針 ;

③ const int srcStride[] 參數 : 源圖像每行有多少個數據 ;

④ int srcSliceY 參數 : 源圖像數據數組處理的索引值 , 從 0 開始計數 , 一般是 0 ;

⑤ int srcSliceH 參數 : 源圖像的高度 , 即有多少行數據 ;

⑥ uint8_t *const dst[] 參數 : 圖像數組指針數組 ;

⑦ const int dstStride[] 參數 : 圖像數據每行的數據個數 ;

/**
 * Scale the image slice in srcSlice and put the resulting scaled
 * slice in the image in dst. A slice is a sequence of consecutive
 * rows in an image.
 *
 * Slices have to be provided in sequential order, either in
 * top-bottom or bottom-top order. If slices are provided in
 * non-sequential order the behavior of the function is undefined.
 *
 * @param c         the scaling context previously created with
 *                  sws_getContext()
 * @param srcSlice  the array containing the pointers to the planes of
 *                  the source slice
 * @param srcStride the array containing the strides for each plane of
 *                  the source image
 * @param srcSliceY the position in the source image of the slice to
 *                  process, that is the number (counted starting from
 *                  zero) in the image of the first row of the slice
 * @param srcSliceH the height of the source slice, that is the number
 *                  of rows in the slice
 * @param dst       the array containing the pointers to the planes of
 *                  the destination image
 * @param dstStride the array containing the strides for each plane of
 *                  the destination image
 * @return          the height of the output slice
 */
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[]);

4 . sws_scale ( ) 使用示例 :

sws_scale(
        //SwsContext *swsContext 轉換上下文
        swsContext,
        //要轉換的數據內容
        avFrame->data,
        //數據中每行的字節長度
        avFrame->linesize,
        0,
        avFrame->height,
        //轉換後目標圖像數據存放在這裏
        dst_data,
        //轉換後的目標圖像行數
        dst_linesize
        );


VII . FFMPEG AVFrame 圖像格式轉換 YUV -> RGBA 代碼示例



//1 . 獲取轉換上下文
SwsContext *swsContext = sws_getContext(
        //源圖像的 寬 , 高 , 圖像像素格式
        avCodecContext->width, avCodecContext->height, avCodecContext->pix_fmt,
        //目標圖像 大小不變 , 不進行縮放操作 , 只將像素格式設置成 RGBA 格式的
        avCodecContext->width, avCodecContext->height, AV_PIX_FMT_RGBA,
        //使用的轉換算法 , FFMPEG 提供了許多轉換算法 , 有快速的 , 有高質量的 , 需要自己測試
        SWS_BILINEAR,
        //源圖像濾鏡 , 這裏傳 NULL 即可
        0,
        //目標圖像濾鏡 , 這裏傳 NULL 即可
        0,
        //額外參數 , 這裏傳 NULL 即可
        0
        );

//2 . 初始化圖像存儲內存

//指針數組 , 數組中存放的是指針
uint8_t *dst_data[4];

//普通的 int 數組
int dst_linesize[4];

//初始化 dst_data 和 dst_linesize , 爲其申請內存 , 注意使用完畢後需要釋放內存
av_image_alloc(dst_data, dst_linesize,
               avCodecContext->width, avCodecContext->height, AV_PIX_FMT_RGBA,
               1);

//3 . 格式轉換
sws_scale(
        //SwsContext *swsContext 轉換上下文
        swsContext,
        //要轉換的數據內容
        avFrame->data,
        //數據中每行的字節長度
        avFrame->linesize,
        0,
        avFrame->height,
        //轉換後目標圖像數據存放在這裏
        dst_data,
        //轉換後的目標圖像行數
        dst_linesize
        );

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