ffmpeg学习笔记-native原生绘制
上次已將ffmpeg的動態庫編譯出來了,并且使用了ffmpeg的轉碼功能,成功將mp4格式視頻轉化為yuv視頻,這篇文章基于上次測試的demo,使用surfaceview顯示解碼完成的像素數據
布局設置和權限添加
布局
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent" ><com.cj5785.ffmpegnativeplayer.view.MySurfaceViewandroid:id="@+id/surface_view"android:layout_width="fill_parent"android:layout_height="fill_parent"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="開始"android:onClick="mPlay" /></FrameLayout>權限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />編寫自定義view和控制器
自定義View
package com.cj5785.ffmpegnativeplayer.view;import android.content.Context; import android.graphics.PixelFormat; import android.util.AttributeSet; import android.view.SurfaceHolder; import android.view.SurfaceView;public class MySurfaceView extends SurfaceView {public MySurfaceView(Context context) {super(context);init();}public MySurfaceView(Context context, AttributeSet attrs) {super(context, attrs);init();}public MySurfaceView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();}//初始化像素格式private void init() {SurfaceHolder holder = getHolder();holder.setFormat(PixelFormat.RGBA_8888);} }控制器
package com.cj5785.ffmpegnativeplayer;import android.view.Surface;public class NativePlayer {public native void render(String input, Surface surface);static {System.loadLibrary("avutil-54");System.loadLibrary("swresample-1");System.loadLibrary("avcodec-56");System.loadLibrary("avformat-56");System.loadLibrary("swscale-3");System.loadLibrary("postproc-53");System.loadLibrary("avfilter-5");System.loadLibrary("avdevice-56");System.loadLibrary("ffmpeg_native_player");}}實現控制器native方法
-
使用javah生成頭文件,這里可能存在無法找到Surface簽名的問題,這時候需要指定classpath路徑
javah -classpath E:\eclipse-adt\sdk\platforms\android-15\android.jar;. com.cj5785.ffmpegnativeplayer.NativePlayer
格式說明:-classpath后面跟的是android.jar路徑,最后接native方法類的全名 -
新建jni文件夾,將頭文件移至jni文件夾,添加本地依賴
-
復制生成ffmpeg的include目錄和so動態庫到jni目錄
-
將之前的Android.mk和Application.mk復制到jni文件夾,并做適當修改
Android.mk主要修改模塊名,使其與控制器調用相統一
Application.mk主要將APP_PLATFORM := android-8修改為APP_PLATFORM := android-9
注意,此處如果不修改Application.mk將導致android/native_window_jni.h無法找到,同時,由于使用了這個頭文件,需要在Android.mk配置-landroid -
使用開源庫libyuv實現yuv轉化為RGBA_8888
下載開源庫libyuv,下載地址libyuv下載地址
將libyuv下的所有文件放入jni目錄(NDK工程規范,必須存在jni目錄)
修改libyuv的Android.mk文件,將最后的include $(BUILD_STATIC_LIBRARY)改為include $(BUILD_SHARED_LIBRARY),這樣就可以生成so動態庫了
還可以將LOCAL_MODULE := libyuv_static改為LOCAL_MODULE := libyuv,方便so管理
在jni目錄下執行ndk-build即可對libyuv進行編譯
編譯生成的so動態庫位于與jni目錄同級的lib下
將lib添加到工程jni目錄下,為了便于管理,將jni的include目錄進行重新分配,重新分配目錄如下:(已將libyuv的include加入到工程,這里沒有列出目錄下包含的頭文件)
- 修改Android.mk,使其能找到so動態庫
- 修改Application.mk,更改APP_PLATFORM,使其可以使用android/native_window_jni.h和android/native_window.h頭文件
- 實現jni頭文件聲明的函數
調用native,使其能夠播放
package com.cj5785.ffmpegnativeplayer;import java.io.File;import com.cj5785.ffmpegnativeplayer.view.MySurfaceView;import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.view.Surface; import android.view.View;public class MainActivity extends Activity {private NativePlayer player;private MySurfaceView mySurfaceView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mySurfaceView = (MySurfaceView) findViewById(R.id.surface_view);player = new NativePlayer();}public void mPlay(View view) {String input = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar + "oneplus.mp4";Surface surface = mySurfaceView.getHolder().getSurface();player.render(input, surface);} }至此,已經可以編譯生成apk了,在手機上測試也沒有問題
更改布局和主活動,使其可以播放多個測試視頻
MainActivity.java
package com.cj5785.ffmpegnativeplayer;import java.io.File;import com.cj5785.ffmpegnativeplayer.view.MySurfaceView;import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.view.Surface; import android.view.View; import android.widget.ArrayAdapter; import android.widget.Spinner;public class MainActivity extends Activity {private NativePlayer player;private MySurfaceView mySurfaceView;private Spinner sp_video;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mySurfaceView = (MySurfaceView) findViewById(R.id.surface_view);sp_video = (Spinner)findViewById(R.id.sp_video);player = new NativePlayer();//視頻列表String[] videoArray = getResources().getStringArray(R.array.video_list);ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1,videoArray);sp_video.setAdapter(adapter);}public void mPlay(View view) {String filename = sp_video.getSelectedItem().toString();String input = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separatorChar + filename;Surface surface = mySurfaceView.getHolder().getSurface();player.render(input, surface);} }activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent" ><com.cj5785.ffmpegnativeplayer.view.MySurfaceViewandroid:id="@+id/surface_view"android:layout_width="fill_parent"android:layout_height="fill_parent"/><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="horizontal"><Spinner android:id="@+id/sp_video"android:layout_width="wrap_content"android:layout_height="wrap_content"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="開始"android:onClick="mPlay" /></LinearLayout></FrameLayout>在string.xml中添加數組值
<string-array name="video_list"><item>naxienian.mp4</item><item>cuc_ieschool.mkv</item><item>sintel.wmv</item><item>Nocturne.m4a</item> </string-array>需要注意的問題
在native的實現過程中,I420ToARGB()方法在調用的時候,UV的位置是顛倒的,需要對調UV的位置
在這個示例程序中,旨在說明native是怎么繪制的,其代碼存在嚴重不足,比如在主線程中繪制界面
部分視頻會出現花屏現象,這個問題在后面多線程解碼的時候會解決
轉載于:https://www.cnblogs.com/cj5785/p/10664660.html
總結
以上是生活随笔為你收集整理的ffmpeg学习笔记-native原生绘制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 走过小公司的坑之入职一周
- 下一篇: 什么是Dao层、Entity层、Serv