白话:
由于习惯了使用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 --name MyFirstApp --path /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. */@Overridepublic 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.mp42a8s.mkvdb.tsshiziwang.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:
- 在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
/* 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文中介绍。