白话:
由于习惯了使用Linux终端的命令行,因此以下介绍Android工程的创建不依赖任何IDE工具(如:eclipse、android studio等),本文的操作全部是在命令行中操作,仅仅是个人习惯。还有Android开发环境的正确配置是本文的基础。如果为配置Android开发环境,需要先配置好Android开发环境。另外本文通过介绍一个”使用FFmpeg抓取视频文件中的一帧图像“的例子介绍如何在Android工程中使用FFmpeg的库。
3.1 创建Android工程
- 使用Android SDK中的工具察看机器中所安装的所有SDK版本,命令:
android list targets
注:android命令工具是在SDK的tools目录下,其是一个脚本文件。
- 根据使用的SDK版本创建Android工程,target-id处填写你需要的版本,我使用的是android 7.0因此对应的是android-24,命令:
android create project --target <target-id> --name MyFirstApp --path <path-to-workspace>/MyFirstApp --activity MainActivity --package com.example.myapp
- 在生成的Android工程目录下创建jni目录,将2文中生成的包含FFmpeg头文件的”include“目录和包含so库文件的”lib“目录拷贝到此jni目录下。
- 编写MainActivity.java文件和main.xml(MainActivity对应的layout文件)文件:
MainActivity.java:
package com.rk.myapp; import android.app.Activity; import android.os.Bundle; import android.graphics.Bitmap; import android.util.Log; import android.widget.ImageView; import com.rk.myapp.R; public class MainActivity extends Activity { private static final String TAG = "FFMpeg_JNI"; private ImageView mImageView; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mImageView = (ImageView) findViewById(R.id.image_frame); } public void onStart() { super.onStart(); nativeInit(); /* 20151010_132114.mp4 2a8s.mkv db.ts shiziwang.rmvb */ nativeSetUri("/mnt/usb/AD0C-15F0/Test/video/db.ts"); nativePrepare(); nativeStart(); Log.i(TAG, "onstart end"); } public void onResume() { super.onResume(); Bitmap b = nativeGetFrameBitmap(); mImageView.setImageBitmap(b); Log.i(TAG, "onResume(), bitmap is " + b); Log.i(TAG, "onResume(), w = " + b.getWidth() + ", h = " + b.getHeight()); } public void onStop() { super.onStop(); nativeDeinit(); } // 加载FFMpegJni库,我们在此库的原文件中将调用FFmpeg的so库里的方法完成我们需要的功能。 static { System.loadLibrary("FFMpegJni"); } // 使用FFmpeg获取视频里某一帧的图像的过程: // (1)初始化FFmpeg,这是使用FFmpeg的第一步。 // (2)使用FFmpeg解码视频,找到那一帧。 // (3)将那一帧的图像原始数据转为native层的bitmap对象。 // (4)java层获取native的bitmap对象,并且在imageview上显示。 private native void nativeInit(); //FFmpeg初始化的native接口 private native void nativeSetUri(String uri); //设置视频的路径的native接口 private native Bitmap nativeGetFrameBitmap(); //获取视频帧的native接口 private native void nativePrepare(); //FFmpeg解码之前的准备工作 private native void nativeStart(); //FFmpeg开始解码,并且将解码的数据保存为bitmap对象。 private native void nativeDeinit(); //使用完FFmpeg后,资源的释放。 }
main.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ImageView android:id="@+id/image_frame" android:layout_width="fill_parent" android:layout_height="fill_parent" android:scaleType="centerInside" /> </LinearLayout>
- 在Android工程目录运行命令编译Android工程(我一般使用debug版本先开发测试),编译成功后在工程目录下会多出bin、gen、libs目录:
ant debug
- 进入bin目录下的classes目录,使用javah生成JNI接口的头文件, 命令:
javah -d ./ -bootclasspath ~/Android/android-sdk-linux/platforms/android-24/android.jar -jni com.rk.myapp.MainActivity
注:(1)”~/Android/android-sdk-linux/platforms/android-24/android.jar“是sdk的android.jar,生成头文件的时候需要用到,否这找不到 Android框架的一些类而报错。
(2)"com.rk.myapp.MainActivity"既是定义native接口class,其在的目录为 ”MyFirstApp/bin/classes/com/rk/myapp目录下。
- 在当前目录下生成的头文件为:com_rk_myapp_MainActivity.h,红色标注的为与MainActivity文件中所定义native接口相对应的头文件中的定义。
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_rk_myapp_MainActivity */ #ifndef _Included_com_rk_myapp_MainActivity #define _Included_com_rk_myapp_MainActivity #ifdef __cplusplus extern "C" { #endif #undef com_rk_myapp_MainActivity_BIND_ABOVE_CLIENT #define com_rk_myapp_MainActivity_BIND_ABOVE_CLIENT 8L #undef com_rk_myapp_MainActivity_BIND_ADJUST_WITH_ACTIVITY #define com_rk_myapp_MainActivity_BIND_ADJUST_WITH_ACTIVITY 128L #undef com_rk_myapp_MainActivity_BIND_ALLOW_OOM_MANAGEMENT #define com_rk_myapp_MainActivity_BIND_ALLOW_OOM_MANAGEMENT 16L #undef com_rk_myapp_MainActivity_BIND_AUTO_CREATE #define com_rk_myapp_MainActivity_BIND_AUTO_CREATE 1L #undef com_rk_myapp_MainActivity_BIND_DEBUG_UNBIND #define com_rk_myapp_MainActivity_BIND_DEBUG_UNBIND 2L #undef com_rk_myapp_MainActivity_BIND_EXTERNAL_SERVICE #define com_rk_myapp_MainActivity_BIND_EXTERNAL_SERVICE -2147483648L #undef com_rk_myapp_MainActivity_BIND_IMPORTANT #define com_rk_myapp_MainActivity_BIND_IMPORTANT 64L #undef com_rk_myapp_MainActivity_BIND_NOT_FOREGROUND #define com_rk_myapp_MainActivity_BIND_NOT_FOREGROUND 4L #undef com_rk_myapp_MainActivity_BIND_WAIVE_PRIORITY #define com_rk_myapp_MainActivity_BIND_WAIVE_PRIORITY 32L #undef com_rk_myapp_MainActivity_CONTEXT_IGNORE_SECURITY #define com_rk_myapp_MainActivity_CONTEXT_IGNORE_SECURITY 2L #undef com_rk_myapp_MainActivity_CONTEXT_INCLUDE_CODE #define com_rk_myapp_MainActivity_CONTEXT_INCLUDE_CODE 1L #undef com_rk_myapp_MainActivity_CONTEXT_RESTRICTED #define com_rk_myapp_MainActivity_CONTEXT_RESTRICTED 4L #undef com_rk_myapp_MainActivity_MODE_APPEND #define com_rk_myapp_MainActivity_MODE_APPEND 32768L #undef com_rk_myapp_MainActivity_MODE_ENABLE_WRITE_AHEAD_LOGGING #define com_rk_myapp_MainActivity_MODE_ENABLE_WRITE_AHEAD_LOGGING 8L #undef com_rk_myapp_MainActivity_MODE_MULTI_PROCESS #define com_rk_myapp_MainActivity_MODE_MULTI_PROCESS 4L #undef com_rk_myapp_MainActivity_MODE_NO_LOCALIZED_COLLATORS #define com_rk_myapp_MainActivity_MODE_NO_LOCALIZED_COLLATORS 16L #undef com_rk_myapp_MainActivity_MODE_PRIVATE #define com_rk_myapp_MainActivity_MODE_PRIVATE 0L #undef com_rk_myapp_MainActivity_MODE_WORLD_READABLE #define com_rk_myapp_MainActivity_MODE_WORLD_READABLE 1L #undef com_rk_myapp_MainActivity_MODE_WORLD_WRITEABLE #define com_rk_myapp_MainActivity_MODE_WORLD_WRITEABLE 2L #undef com_rk_myapp_MainActivity_DEFAULT_KEYS_DIALER #define com_rk_myapp_MainActivity_DEFAULT_KEYS_DIALER 1L #undef com_rk_myapp_MainActivity_DEFAULT_KEYS_DISABLE #define com_rk_myapp_MainActivity_DEFAULT_KEYS_DISABLE 0L #undef com_rk_myapp_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL #define com_rk_myapp_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L #undef com_rk_myapp_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL #define com_rk_myapp_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L #undef com_rk_myapp_MainActivity_DEFAULT_KEYS_SHORTCUT #define com_rk_myapp_MainActivity_DEFAULT_KEYS_SHORTCUT 2L #undef com_rk_myapp_MainActivity_RESULT_CANCELED #define com_rk_myapp_MainActivity_RESULT_CANCELED 0L #undef com_rk_myapp_MainActivity_RESULT_FIRST_USER #define com_rk_myapp_MainActivity_RESULT_FIRST_USER 1L #undef com_rk_myapp_MainActivity_RESULT_OK #define com_rk_myapp_MainActivity_RESULT_OK -1L /* * Class: com_rk_myapp_MainActivity * Method: nativeInit * Signature: ()V */ JNIEXPORT void JNICALL Java_com_rk_myapp_MainActivity_nativeInit (JNIEnv *, jobject); /* * Class: com_rk_myapp_MainActivity * Method: nativeSetUri * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_com_rk_myapp_MainActivity_nativeSetUri (JNIEnv *, jobject, jstring); /* * Class: com_rk_myapp_MainActivity * Method: nativeGetFrameBitmap * Signature: ()Landroid/graphics/Bitmap; */ JNIEXPORT jobject JNICALL Java_com_rk_myapp_MainActivity_nativeGetFrameBitmap (JNIEnv *, jobject); /* * Class: com_rk_myapp_MainActivity * Method: nativePrepare * Signature: ()V */ JNIEXPORT void JNICALL Java_com_rk_myapp_MainActivity_nativePrepare (JNIEnv *, jobject); /* * Class: com_rk_myapp_MainActivity * Method: nativeStart * Signature: ()V */ JNIEXPORT void JNICALL Java_com_rk_myapp_MainActivity_nativeStart (JNIEnv *, jobject); /* * Class: com_rk_myapp_MainActivity * Method: nativeDeinit * Signature: ()V */ JNIEXPORT void JNICALL Java_com_rk_myapp_MainActivity_nativeDeinit (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif
这样JNI接口的头文件就自动生成了,下一步将实现JNI头文件所定义的接口,将在4文中介绍。