前言:百度说,FFmpeg是一个开源免费跨平台的视频和音频流方案,属于自由软件,采用LGPL或GPL许可证(依据你选择的组件)。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多codec都是从头开发的。
说白了ffmpeg就是一个编解码库,我们可以使用android 提供的ndk编译其源码,网上很多资料,这里不是我们考虑的重点。我们的任务是将使用ndk编译好的so文件加入到我们的andriod apk中,让android的apk能够使用ffmpeg提供的音视频编解码功能。
闲话少说,我们开始吧。
一、在你的android项目src下创建包com.xx. 并在其中新建一个类叫做FFmpegOperater.java. 内容如下:
package com.xx;
public class FFmpegOperater {
private static FFmpegOperater mFFmpegOperater;
private FFmpegOperater(){
}
public static FFmpegOperater newInstance(){
if(mFFmpegOperater == null){
mFFmpegOperater = new FFmpegOperater();
}
return mFFmpegOperater;
}
public native int readVersion();//本地方法,在此使用native 关键字声明,在C中实现
}
二、生成头文件,此文件包含FFmpegOperater.java中 native 方法的声明(C中)
打开命令行,切换目录到项目的bin/classes/目录下。输入javah -classpath . -jni com.xx.FFmpegOperater 然后回车。会在当前目录下生成一个com_xx_FFmpegOperater.h文件。这个文件要copy到jni目录下,下文讲解。
打开此文件是如下内容
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_xx_FFmpegOperater */
#ifndef _Included_com_xx_FFmpegOperater
#define _Included_com_xx_FFmpegOperater
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_xx_FFmpegOperater
* Method: readVersion
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_xx_FFmpegOperater_readVersion
(JNIEnv *, jobject); //java 中声明的native方法。
#ifdef __cplusplus
}
#endif
#endif
三、创建jni(java native interface)
在项目根目录下创建jni文件夹。此文件夹用于存放本地代码,和我们要加入的第三方库libffmpeg.so.将如下内容加入到jni目录下
com_xx_FFmpegOperater.c -----------> 位于java 与C之间,功能是调用ffmpeg提供的函数,并将反回结果通过jni传给java
com_xx_FFmpegOperater.h --------->上文中生成的com_xx_FFmpegOperater.h,将此文件放在jni目录下
Android.mk -----------> android 提供的makefile。定义编译规则,根据这个规则可以将libffmpeg.so编译到我们的.c 文件中,并生成我们自己的 .so文件供java调用。
libs 文件夹 -----------------> 下面放置libffmpeg.so 。
include文件夹 --------------------> 此文件夹下面是ffmpeg的所有头文件,要调用libffmpeg.so中的函数,必须包含相应的头文件。去ffmpeg的官网可以下载到。
结构图:
依次详细讲解这些内容:
com_xx_FFmpegOperater.c 承上启下的作用。
#include "com_xx_FFmpegOperater.h"
#include "libavutil/avutil.h"
#include <android/log.h>
#define LOG_TAG "ffmpeg"
#define LOGW(a) __android_log_write(ANDROID_LOG_INFO,LOG_TAG,a)
JNIEXPORT jint JNICALL Java_com_xx_FFmpegOperater_readVersion (JNIEnv *env, jobject obj)
{
LOGW(avutil_license());
return (int)avutil_version();
}
Android.mk 定义ndk的编译规则,内容如下:
LOCAL_PATH := $(call my-dir) #表示将当前目录的文件路径,即此Android.mk所在的路径赋值给LOCAL_PATH变量
#编译libffmpeg.so 其实就是告诉ndk,它是一个第三方库
include $(CLEAR_VARS)
LOCAL_SRC_FILES := libs/libffmpeg.so #指定libffmpeg.so的路径
LOCAL_MODULE :=ffmpeg
include $(PREBUILT_SHARED_LIBRARY)
#编译我们的C文件,并将libffmpeg链接过来
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_MODULE := myffmpeg
LOCAL_SRC_FILES := com_xx_FFmpegOperater.c
LOCAL_C_INCLUDES := \ #这一行定义所有使用的头文件的路径
$(JNI_H_INCLUDE) \
$(LOCAL_PATH)/include/
LOCAL_LDLIBS +=-L$(LOCAL_PATH) -lm -llog
#要使用的其他库,其中包括libffmpeg
LOCAL_SHARED_LIBRARIES := liblog libcutils libffmpeg
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
四、编译
打开终端,切换目录到jni下。输入 ndk-build,回车编译成功。前提你要配置好ndk. 具体配置,请自行百度
编译完成如果没有错误,在刷新一下项目,会自动生成两个文件夹,obj和libs。
obj目录存放的是在编译过程中生成的中间文件。libs文件夹下面是我们要在java里面lode的库文件,这些文件将会在apk被安装是放在系统路径的/data/data/com.xx/libs/目录下。java在载入时就会从这里找。
五、回到FFmpegOperater.java中,在此类中添加静态代码块来load 生成的那两个库文件,libffmpeg.so和libmyffmpeg.so
static {
System.loadLibrary("ffmpeg"); //注意,此处要使用“ffmpeg”,而不是“libffmpeg”。这是规定。
System.loadLibrary("myffmpeg");
}
六、调用
至此,就可以调用你的native 方法了,在java中。
package com.xx;
import android.app.Activity;
import android.os.Bundle;
public class FFmpegDemoActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
int v = FFmpegOperater.newInstance().readVersion();
System.out.println(v);
}
}
七、总结:
我们的目的是在java中调用第三方C库里的函数,所以必须通过jni,将C库链接到我们自己的C代码中,在我自己的C代码中调用第三方C库提供的函数。然后通过jni,将我们的C代码与java代码串起来。
另外,libffmpeg.so是从源码中编译而来的,所以要使用与其相同版本的头文件。