3. Android工程中使用FFmpeg的so库 -- JNI头文件的生成

白话:

    由于习惯了使用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文中介绍。





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值