[Cmake-Android音視頻]ffmpeg3.4視頻格式轉換和顯示

[Cmake-Android]音視頻總結:

 

視頻格式轉換流程圖

 

函數介紹

sws_getContext(...)

創建像素格式轉換的上下文,每次會創建新的空間。多線程建議使用。

 

struct SwsContext *sws_getCachedContext(...)                       

創建像素格式轉換的上下文,不會每次創建新的空間。會根據傳入的上下文在緩衝裏面查找,如果輸入輸出參數沒有變化,就會返回緩衝中已經存在的swsContext。如果發生了變化,則會清除掉之前創建的swsContext,重新創建一個新的。

sws_getCachedContext(
                    struct SwsContext *context,
                    int srcW, int srcH, 
                    enum AVPixelFormat srcFormat,
                    int dstW, int dstH,
                    enum AVPixelFormat dstFormat,
                    int flags,
                    SwsFilter *srcFilter,
                    SwsFilter *dstFilter,
                    const double *param);

參數說明:

  • 第一參數可以傳NULL,默認會開闢一塊新的空間。
  • srcW,srcH, srcFormat, 原始數據的寬高和原始像素格式(YUV420),
  • dstW,dstH,dstFormat; 目標寬,目標高,目標的像素格式(這裏的寬高可能是手機屏幕分辨率,RGBA8888),這裏不僅僅包含了尺寸的轉換和像素格式的轉換
  • flag 提供了一系列的算法,快速線性,差值,矩陣,不同的算法性能也不同,快速線性算法性能相對較高。只針對尺寸的變換。對像素格式轉換無此問題

#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

後面還有兩個參數是做過濾器用的,一般用不到,傳NULL,最後一個參數是跟flag算法相關,也可以傳NULL。

 

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

參數說明

  • struct SwsContext *c // 像素格式轉換的上下文
  • uint8_t *const srcSlice[] // 對應的具體數據的數組,是指針數組
  • const int srcStride[] // linesize, 一行對應的大小。
  • int srcSliceY // 用不到,直接傳0即可。
  • int srcSliceH // 圖像的高度。
  • uint8_t *const dst[] // 目標的地址。指針數組。
  • const int dstStride[] // 輸入的linesize

 

void sws_freeContext(struct SwsContext *swsContext);

清理swsContext

sws_freeContext(swsContext); 
swsContext = null;             //記得把swsContext置爲空

 

關鍵代碼參考

// 初始化像素格式轉換上下文
SwsContext *vctx = NULL;
int outWidth = 1280;
int outHeight = 720;
char *rgb = new char[1920*1080*4];

//獲取像素格式化上下文        
vctx = sws_getCachedContext(
        vctx,
        frame->width,
        frame->height,
        (AVPixelFormat)frame->format,
        outWidth,
        outHeight,
        AV_PIX_FMT_RGBA,
        SWS_FAST_BILINEAR,
        0, 0, 0);
        
if (!vctx) 
{
    LOGE("sws_getCachedContext failed!");
} else 
{
    // 開始像素格式轉換
    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
    );
    LOGI("sws_scale = %d", h);
}

注意:

像素格式的相關函數包含在 libswscale.so 庫中

同時在代碼中包含頭文件 #include <libswscale/swscale.h> 

 

NDK繪製surfaceView顯示

經過上面的格式轉換之後,我們就可以用GLSurfaceView來繪製視頻畫面了。

在cmake中添加android庫

target_link_libraries( # Specifies the target library.
        native-lib
        android
        avcodec avformat avutil swresample swscale
        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

引入頭文件

#include <android/native_window.h> 
#include <android/native_window_jni.h>

native代碼參考

//顯示窗口初始化
ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env, surface);
ANativeWindow_setBuffersGeometry(nativeWindow, outWidth, outHeight, WINDOW_FORMAT_RGBA_8888);
ANativeWindow_Buffer nativeWindowBuffer;  //後備緩衝

//在surfaceView上顯示圖像
ANativeWindow_lock(nativeWindow, &nativeWindowBuffer, 0);
uint8_t *dst = (uint8_t *)nativeWindowBuffer.bits;
//因爲格式爲RGB8888,所以像素點的個數爲w*h*4
memcpy(dst, rgb, outWidth*outHeight*4); 
ANativeWindow_unlockAndPost(nativeWindow);

java代碼參考

public class TestPlay extends GLSurfaceView implements Runnable,SurfaceHolder.Callback {


    public TestPlay(Context context) {
        super(context);
    }

    public TestPlay(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void run() {
        open("/sdcard/test1.mp4", getHolder().getSurface());
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        new Thread(this).start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }


    public native void open(String path, Object surface);

}

最後在xml文件中引用TestPlay控件即可顯示出圖像。

<c.firstffmpeg.TestPlay
    android:layout_width="match_parent"
    android:layout_height="250dp" />

 

 

 

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