【Android FFMPEG 开发】FFMPEG ANativeWindow 原生绘制 ( Java 层获取 Surface | 传递画布到本地 | 创建 ANativeWindow )
文章目錄
- I . FFMPEG ANativeWindow 原生繪制
- II . FFMPEG 原生繪制流程
- III . Java 層獲取 Surface 畫布
- IV . 傳遞 Surface 畫布到 Native 層
- V . Native 層創(chuàng)建 ANativeWindow 原生繪制窗口
I . FFMPEG ANativeWindow 原生繪制
FFMPEG ANativeWindow 原生繪制前置操作 :
① FFMPEG 初始化 : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 初始化 ( 網絡初始化 | 打開音視頻 | 查找音視頻流 )
② FFMPEG 獲取 AVStream 音視頻流 : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 獲取 AVStream 音視頻流 ( AVFormatContext 結構體 | 獲取音視頻流信息 | 獲取音視頻流個數 | 獲取音視頻流 )
③ FFMPEG 獲取 AVCodec 編解碼器 : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 獲取編解碼器 ( 獲取編解碼參數 | 查找編解碼器 | 獲取編解碼器上下文 | 設置上下文參數 | 打開編解碼器 )
④ FFMPEG 讀取音視頻流中的數據到 AVPacket : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 讀取音視頻流中的數據到 AVPacket ( 初始化 AVPacket 數據 | 讀取 AVPacket )
⑤ FFMPEG 解碼 AVPacket 數據到 AVFrame : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 解碼 AVPacket 數據到 AVFrame ( AVPacket->解碼器 | 初始化 AVFrame | 解碼為 AVFrame 數據 )
⑥ FFMPEG AVFrame 圖像格式轉換 YUV -> RGBA : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG AVFrame 圖像格式轉換 YUV -> RGBA ( 獲取 SwsContext | 初始化圖像數據存儲內存 | 圖像格式轉換 )
II . FFMPEG 原生繪制流程
FFMPEG 解碼 AVPacket 數據到 AVFrame 流程 :
〇 前置操作 : FFMPEG 環(huán)境初始化 , 獲取 AVStream 音視頻流 , 獲取 AVCodec 編解碼器 , 讀取音視頻流中的數據到 AVPacket , 解碼 AVPacket 數據到 AVFrame , AVFrame 圖像格式轉換 YUV -> RGBA , 然后才能進行下面的操作 ;
① Java 層獲取 Surface 對象 : Surface 畫布可以在 SurfaceView 的 SurfaceHolder 中獲取
//繪制圖像的 SurfaceView SurfaceView surfaceView;//在 SurfaceView 回調函數中獲取 SurfaceHolder surfaceHolder = surfaceView.getHolder() ; //獲取 Surface 畫布 Surface surface = surfaceHolder.getSurface() ;② 將 Surface 對象傳遞到 Native 層 : 在 SurfaceHolder.Callback 接口的 surfaceChanged 實現方法中 , 將 Surface 畫布傳遞給 Native 層 ;
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//畫布改變 , 橫豎屏切換 , 按下 Home 鍵 , 按下菜單鍵//將 Surface 傳到 Native 層 , 在 Native 層繪制圖像native_set_surface(holder.getSurface()); }//調用該方法將 Surface 傳遞到 Native 層 native void native_set_surface(Surface surface);③ 創(chuàng)建 ANativeWindow : 在 Native 層的 C++ 代碼中 , 接收 Surface 畫布 , 并創(chuàng)建 ANativeWindow 本地繪制窗口 , 原生繪制主要在 ANativeWindow 中進行 ;
//CPP 中接收 Surface 畫布 , 并創(chuàng)建 ANativeWindow extern "C" JNIEXPORT void JNICALL Java_kim_hsl_ffmpeg_Player_native_1set_1surface(JNIEnv *env, jobject instance, jobject surface) {// 將從 Java 層傳遞的 Surface 對象轉換成 ANativeWindow 結構體// 如果之前已經有了 ANativeWindow 結構體 , 那么先將原來的釋放掉//釋放原來的 ANativeWindowif(aNativeWindow){ANativeWindow_release(aNativeWindow);}//轉換新的 ANativeWindowaNativeWindow = ANativeWindow_fromSurface(env, surface); }III . Java 層獲取 Surface 畫布
1 . Surface 畫布 : 這里的 Surface 畫布從 SurfaceView 中獲得 , SurfaceHolder.Callback 的監(jiān)聽方法中獲取 SurfaceHolder 及 Surface ;
2 . 設置 SurfaceHolder 回調函數 : 首先要獲取 SurfaceView 的 SurfaceHolder ; 設置 SurfaceHolder 監(jiān)聽回調函數 SurfaceHolder.Callback ;
//監(jiān)聽獲取畫布this.surfaceHolder = this.surfaceView.getHolder();surfaceHolder.addCallback(this);3 . 獲取 Surface 畫布 : 在 surfaceChanged 回調方法中 , 獲取 Surface 畫布 , 這樣可以保證在橫豎屏切換時可以實時獲取到最新畫布 ;
@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//畫布改變 , 橫豎屏切換 , 按下 Home 鍵 , 按下菜單鍵//holder.getSurface() 就是 Surface 畫布 ; }4 . Surface 畫布獲取 代碼示例 :
package kim.hsl.ffmpeg;import android.util.Log; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView;/*** Java 層與 Native 層交互 接口*/ public class Player implements SurfaceHolder.Callback {private static final String TAG = "Player";// 加載動態(tài)庫static {System.loadLibrary("native-lib");}/*** 視頻顯示組件*/private SurfaceView surfaceView;/*** 控制 Surface 畫布接口*/private SurfaceHolder surfaceHolder;...public void setSurfaceView(SurfaceView surfaceView) {this.surfaceView = surfaceView;//監(jiān)聽獲取畫布this.surfaceHolder = this.surfaceView.getHolder();surfaceHolder.addCallback(this);}@Overridepublic void surfaceCreated(SurfaceHolder holder) {//畫布創(chuàng)建}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//畫布改變 , 橫豎屏切換 , 按下 Home 鍵 , 按下菜單鍵//holder.getSurface() 就是 Surface 畫布 ; }@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {//畫布銷毀} }IV . 傳遞 Surface 畫布到 Native 層
1 . 原生繪制需求 : 在 Native 層使用 C/C++ 進行原生繪制需要將 Surface 畫布傳遞到 Native 層進行繪制 ;
2 . 定義傳遞方法 : 在 Java 層定義傳遞 Surface 畫布的 Native 方法 ;
native void native_set_surface(Surface surface);3 . 實現 Surface 傳遞方法 : 在 Native 層實現 Java 中定義的方法 ;
extern "C" JNIEXPORT void JNICALL Java_kim_hsl_ffmpeg_Player_native_1set_1surface(JNIEnv *env, jobject instance, jobject surface) {... }4 . 傳遞 Surface 畫布操作 : 在 surfaceChanged 函數中 , 通過調用 SurfaceHolder 的 getSurface ( ) 方法獲取 Surface 畫布 , 再調用 native_set_surface(holder.getSurface()) , 將畫布傳遞到 Surface 層 ;
@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//畫布改變 , 橫豎屏切換 , 按下 Home 鍵 , 按下菜單鍵//將 Surface 傳到 Native 層 , 在 Native 層繪制圖像native_set_surface(holder.getSurface());}V . Native 層創(chuàng)建 ANativeWindow 原生繪制窗口
1 . ANativeWindow 創(chuàng)建 : 在 Native 層實現 Java 中定義的本地方法 native_set_surface ( ) , 在該方法中傳入了 Surface 對象作為參數 ; 在該 Native 方法中 , 調用了 ANativeWindow_fromSurface ( ) 方法 , 將 Surface 對象轉為了 ANativeWindow 原生繪制窗口 ;
2 . ANativeWindow_fromSurface ( ) 函數原型 :
① JNIEnv* env 參數 : JNI 環(huán)境 , 在 JNI 方法中自帶 ;
② jobject surface 參數 : Java 層傳入的 Surface 對象 ;
/*** Return the ANativeWindow associated with a Java Surface object,* for interacting with it through native code. This acquires a reference* on the ANativeWindow that is returned; be sure to use ANativeWindow_release()* when done with it so that it doesn't leak.*/ ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface);3 . Native 層創(chuàng)建 ANativeWindow 原生繪制窗口 代碼示例 :
/*** 原生繪制窗口*/ ANativeWindow * aNativeWindow;extern "C" JNIEXPORT void JNICALL Java_kim_hsl_ffmpeg_Player_native_1set_1surface(JNIEnv *env, jobject instance, jobject surface) {//加同步鎖pthread_mutex_lock(&mutex);// 將從 Java 層傳遞的 Surface 對象轉換成 ANativeWindow 結構體// 如果之前已經有了 ANativeWindow 結構體 , 那么先將原來的釋放掉//釋放原來的 ANativeWindowif(aNativeWindow){ANativeWindow_release(aNativeWindow);}//轉換新的 ANativeWindowaNativeWindow = ANativeWindow_fromSurface(env, surface);//解除同步鎖pthread_mutex_unlock(&mutex);}總結
以上是生活随笔為你收集整理的【Android FFMPEG 开发】FFMPEG ANativeWindow 原生绘制 ( Java 层获取 Surface | 传递画布到本地 | 创建 ANativeWindow )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android FFMPEG 开发】F
- 下一篇: 【Android FFMPEG 开发】F