[Cmake-Android]音視頻總結:
-
[Cmake-Android音視頻]SDK,NDK基本介紹
-
[Cmake-Android音視頻]NDK-r14b編譯ffmpeg3.4支持neon,硬解碼
-
[Cmake-Android音視頻]創建支持ffmpeg3.4的項目
-
[Cmake-Android音視頻]ffmpeg3.4實現解封裝
-
[Cmake-Android音視頻]ffmpeg3.4軟硬解碼和多線程解碼
-
[Cmake-Android音視頻]ffmpeg3.4視頻格式轉換和顯示
-
[Cmake-Android音視頻]ffmpeg3.4音頻重採樣
-
[Cmake-Android音視頻]OpenSLES音頻播放
視頻格式轉換流程圖
函數介紹
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" />