ffmpeg学习笔记-native原生绘制
上次已將ffmpeg的動(dòng)態(tài)庫(kù)編譯出來(lái)了,并且使用了ffmpeg的轉(zhuǎn)碼功能,成功將mp4格式視頻轉(zhuǎn)化為yuv視頻,這篇文章基于上次測(cè)試的demo,使用surfaceview顯示解碼完成的像素?cái)?shù)據(jù)
布局設(shè)置和權(quán)限添加
布局
<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="開(kāi)始"android:onClick="mPlay" /></FrameLayout>權(quán)限
<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");}}實(shí)現(xiàn)控制器native方法
-
使用javah生成頭文件,這里可能存在無(wú)法找到Surface簽名的問(wèn)題,這時(shí)候需要指定classpath路徑
javah -classpath E:\eclipse-adt\sdk\platforms\android-15\android.jar;. com.cj5785.ffmpegnativeplayer.NativePlayer
格式說(shuō)明:-classpath后面跟的是android.jar路徑,最后接native方法類的全名 -
新建jni文件夾,將頭文件移至jni文件夾,添加本地依賴
-
復(fù)制生成ffmpeg的include目錄和so動(dòng)態(tài)庫(kù)到j(luò)ni目錄
-
將之前的Android.mk和Application.mk復(fù)制到j(luò)ni文件夾,并做適當(dāng)修改
Android.mk主要修改模塊名,使其與控制器調(diào)用相統(tǒng)一
Application.mk主要將APP_PLATFORM := android-8修改為APP_PLATFORM := android-9
注意,此處如果不修改Application.mk將導(dǎo)致android/native_window_jni.h無(wú)法找到,同時(shí),由于使用了這個(gè)頭文件,需要在Android.mk配置-landroid -
使用開(kāi)源庫(kù)libyuv實(shí)現(xiàn)yuv轉(zhuǎn)化為RGBA_8888
下載開(kāi)源庫(kù)libyuv,下載地址libyuv下載地址
將libyuv下的所有文件放入jni目錄(NDK工程規(guī)范,必須存在jni目錄)
修改libyuv的Android.mk文件,將最后的include $(BUILD_STATIC_LIBRARY)改為include $(BUILD_SHARED_LIBRARY),這樣就可以生成so動(dòng)態(tài)庫(kù)了
還可以將LOCAL_MODULE := libyuv_static改為L(zhǎng)OCAL_MODULE := libyuv,方便so管理
在jni目錄下執(zhí)行ndk-build即可對(duì)libyuv進(jìn)行編譯
編譯生成的so動(dòng)態(tài)庫(kù)位于與jni目錄同級(jí)的lib下
將lib添加到工程jni目錄下,為了便于管理,將jni的include目錄進(jìn)行重新分配,重新分配目錄如下:(已將libyuv的include加入到工程,這里沒(méi)有列出目錄下包含的頭文件)
- 修改Android.mk,使其能找到so動(dòng)態(tài)庫(kù)
- 修改Application.mk,更改APP_PLATFORM,使其可以使用android/native_window_jni.h和android/native_window.h頭文件
- 實(shí)現(xiàn)jni頭文件聲明的函數(shù)
調(diào)用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);} }至此,已經(jīng)可以編譯生成apk了,在手機(jī)上測(cè)試也沒(méi)有問(wèn)題
更改布局和主活動(dòng),使其可以播放多個(gè)測(cè)試視頻
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="開(kāi)始"android:onClick="mPlay" /></LinearLayout></FrameLayout>在string.xml中添加數(shù)組值
<string-array name="video_list"><item>naxienian.mp4</item><item>cuc_ieschool.mkv</item><item>sintel.wmv</item><item>Nocturne.m4a</item> </string-array>需要注意的問(wèn)題
在native的實(shí)現(xiàn)過(guò)程中,I420ToARGB()方法在調(diào)用的時(shí)候,UV的位置是顛倒的,需要對(duì)調(diào)UV的位置
在這個(gè)示例程序中,旨在說(shuō)明native是怎么繪制的,其代碼存在嚴(yán)重不足,比如在主線程中繪制界面
部分視頻會(huì)出現(xiàn)花屏現(xiàn)象,這個(gè)問(wèn)題在后面多線程解碼的時(shí)候會(huì)解決
轉(zhuǎn)載于:https://www.cnblogs.com/cj5785/p/10664660.html
總結(jié)
以上是生活随笔為你收集整理的ffmpeg学习笔记-native原生绘制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 走过小公司的坑之入职一周
- 下一篇: 什么是Dao层、Entity层、Serv