一个小玩具:NDK编译FFmpeg的例子

FFmpeg NDK编译 和最简单的APK


准备

硬件:

一台电脑,实验在Lenovo T430

一个Android设备,实验在 三星S3/A7

编译环境:

Ubuntu 14.04 (ant\java等命令必须支持)


工具包:

NDKhttps://dl.google.com/android/ndk/android-ndk32-r10b-linux-x86_64.tar.bz2

SDKhttps://dl.google.com/android/adt/adt-bundle-linux-x86_64-20140702.zip

Ffmpeg: http://ffmpeg.org/releases/ffmpeg-2.7.2.tar.bz2


步骤

1. 配置编译FFmpeg, 生成库文件libxxx.soinclude头文件

2. eclipse生成一个最简单APKload静态卡,调用native方法

3.将第1步的libxxx.so文件,由ndk-build工具重新编译

4. 引用第2步的库文件,编写自己的jni call native函数




1. 配置编译FFmpeg, 生成库文件libxxx.soinclude头文件


tar -xvjf ffmpeg-2.7.2.tar.bz2

cd ffmpeg-2.7.2

gedit my-make.sh


将下面的代码拷贝到my-make.sh

#!/bin/bash

NDK=/opt/adrd-stuff/android-ndk-r10b

SYSROOT=$NDK/platforms/android-19/arch-arm

TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64


function build_one

{

./configure \

--prefix=$PREFIX \

--enable-shared \

--disable-static \

--disable-doc \

--disable-ffserver \

--enable-cross-compile \

--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \

--target-os=linux \

--arch=arm \

--sysroot=$SYSROOT \

--extra-cflags="-Os -fpic $ADDI_CFLAGS" \

--extra-ldflags="$ADDI_LDFLAGS" \

$ADDITIONAL_CONFIGURE_FLAG

}

CPU=arm

PREFIX=$(pwd)/android/$CPU

ADDI_CFLAGS="-marm"

build_one

make -j8

make install

## end of my-make.sh

 

然后:

chmod +x my-make.sh

mkdir android/arm -p

./my-make.sh


最后就可以在android/arm下面:

~~~~~~~~~~~/android/arm$ ls

bin include lib share


2. eclipse生成一个最简单APKload静态库,调用native方法

android eclipse开发环境中,新建一个最简单的android application

此处关节是添加文件FFmpegNative.java

└── src

└── com

└── az

└── ffmpegapp

├── FFmpegNative.java

└── MainActivity.java


package com.az.ffmpegapp;

publicclass FFmpegNative {

static{

System.loadLibrary("avutil-54");

System.loadLibrary("avcodec-56");

System.loadLibrary("swresample-1");

System.loadLibrary("avformat-56");

System.loadLibrary("swscale-3");

System.loadLibrary("avfilter-5");

System.loadLibrary("ffmpeg_codec");

}

publicstaticnativeint avcodec_find_decoder(int codecID);

}


确保在bin目录下生成了

bin/classes/com/az/ffmpegapp/FFmpegNative.class


3.将第1步的libxxx.so文件,由ndk-build工具重新编译

1) 在FFmpegApp下面,新建目录jni

2jni下面新建目录ffmpeg,把第1大步生成的文件拷贝到此目录下

ls ffmpeg/

bin include lib share

3)编写Android.mk文件

LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)

LOCAL_MODULE := avcodec-56-prebuilt

LOCAL_SRC_FILES := ffmpeg/lib/libavcodec-56.so

include $(PREBUILT_SHARED_LIBRARY)


include $(CLEAR_VARS)

LOCAL_MODULE :=avdevice-56-prebuilt

LOCAL_SRC_FILES :=ffmpeg/lib/libavdevice-56.so

include $(PREBUILT_SHARED_LIBRARY)


include $(CLEAR_VARS)

LOCAL_MODULE :=avfilter-5-prebuilt

LOCAL_SRC_FILES :=ffmpeg/lib/libavfilter-5.so

include $(PREBUILT_SHARED_LIBRARY)


include $(CLEAR_VARS)

LOCAL_MODULE :=avformat-56-prebuilt

LOCAL_SRC_FILES :=ffmpeg/lib/libavformat-56.so

include $(PREBUILT_SHARED_LIBRARY)


include $(CLEAR_VARS)

LOCAL_MODULE := avutil-54-prebuilt

LOCAL_SRC_FILES :=ffmpeg/lib/libavutil-54.so

include $(PREBUILT_SHARED_LIBRARY)


include $(CLEAR_VARS)

LOCAL_MODULE := avswresample-1-prebuilt

LOCAL_SRC_FILES :=ffmpeg/lib/libswresample-1.so

include $(PREBUILT_SHARED_LIBRARY)


include $(CLEAR_VARS)

LOCAL_MODULE := swscale-3-prebuilt

LOCAL_SRC_FILES :=ffmpeg/lib/libswscale-3.so

include $(PREBUILT_SHARED_LIBRARY)

#end of Andoid.mk

4)运行ndk-build

此时生成了文件在../libs/armeabi下面:

tree ../libs/

../libs/

├── android-support-v4.jar

├── android-support-v7-appcompat.jar

└── armeabi

├── libavcodec-56.so

├── libavdevice-56.so

├── libavfilter-5.so

├── libavformat-56.so

├── libavutil-54.so

├── libswresample-1.so

└── libswscale-3.so

 

4. 引用第2步的库文件,编写自己的jni call native函数

1)注意到第2大步里面生成的bin/classes/com/az/ffmpegapp/FFmpegNative.class

cd bin

javah -classpath classes/ com.az.ffmpegapp.FFmpegNative

生成文件:com_az_ffmpegapp_FFmpegNative.h

2) 拷贝到jni下,实现这个头文件里面的方法,更换一个名字native_avcodec_find_decoder(文字最后附录一个完整c文件FFmpegNative.c)

#include <libavcodec/avcodec.h>

JNIEXPORT jint JNICALL native_avcodec_find_decoder(JNIEnv * e, jclass jc, jint codecID)

{

LOGI("%s called\n",__func__);

AVCodec *codec = NULL;


/*register all formats and codecs */

av_register_all();

codec= avcodec_find_decoder(codecID);


if(codec != NULL) {

return 0;

} else {

return -1;

}

}


3)编辑Android.mk,在前面的基础上,最后面添加:

include $(CLEAR_VARS)

LOCAL_MODULE :=ffmpeg_codec

LOCAL_SRC_FILES :=FFmpegNative.c

LOCAL_LDLIBS := -llog #-ljnigraphics -lz -landroid

LOCAL_C_INCLUDES += $(LOCAL_PATH)/ffmpeg/include

LOCAL_SHARED_LIBRARIES:= \

avcodec-56-prebuilt \

avdevice-56-prebuilt \

avfilter-5-prebuilt \

avformat-56-prebuilt \

avutil-54-prebuilt

include $(BUILD_SHARED_LIBRARY)


4ndk-build


tree ../libs/

../libs/

├── android-support-v4.jar

├── android-support-v7-appcompat.jar

└── armeabi

├── libavcodec-56.so

├── libavdevice-56.so

├── libavfilter-5.so

├── libavformat-56.so

├── libavutil-54.so

├── libffmpeg_codec.so

├── libswresample-1.so

└── libswscale-3.so

libffmpeg_codec.so 就是新生成的库。


5Eclipse重新编译生成APK

由于在MainActivity.java有下面的打印语句:

protectedvoid onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

if (FFmpegNative.avcodec_find_decoder(28)==0)

Log.d("MainActivity","Find decoder 28");

else

Log.d("MainActivity","Not Find");

}

JNI native里面有调试语句:

JNIEXPORT jint JNICALL native_avcodec_find_decoder(JNIEnv * e, jclass jc, jint codecID)

{

#if

LOGI("%s called\n",__func__);

 

logcat可以有下面的打印:

D/MainActivity(12786): Find decoder 28

I/SubCommand(12786): native_avcodec_find_decoder called

 

注意:

NDK版本:

32bits小机用:android-ndk32-r10b-linux-x86_64.tar.bz2

64bits小机用:android-ndk64-r10b-linux-x86_64.tar.bz2

64bits编译出来的APKS3上面运行,会有闪退的现象,


E/dalvikvm(31393): dlopen("/data/app-lib/com.az.ffmpegapp-1/libavformat-56.so") failed: dlopen failed: cannot locate symbol "atof" referenced by "libavformat-56.so"...

W/dalvikvm(31393): Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Lcom/az/ffmpegapp/FFmpegNative;

E/AndroidRuntime(31393): Process: com.az.ffmpegapp, PID: 31393

E/AndroidRuntime(31393): at com.az.ffmpegapp.FFmpegNative.<clinit>(FFmpegNative.java:7)

E/AndroidRuntime(31393): at com.az.ffmpegapp.MainActivity.onCreate(MainActivity.java:15)

W/ActivityManager( 834): Force finishing activity com.az.ffmpegapp/.MainActivity

 

附录文件:

FFmpegNative.c

 

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <android/log.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>

/* Header for class com_az_ffmpegapk_FFmpegNative */

#ifndef _Included_com_az_ffmpegapk_FFmpegNative
#define _Included_com_az_ffmpegapk_FFmpegNative
#define LOG_TAG "SubCommand"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOG_ERROR(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOG_DEBUG(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)


#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_az_ffmpegapk_FFmpegNative
 * Method:    avcodec_find_decoder
 * Signature: (I)I
 */
#include <libavcodec/avcodec.h>
JNIEXPORT jint JNICALL native_avcodec_find_decoder(JNIEnv * e, jclass jc, jint codecID)
{
#if 1
    LOGI("%s called\n",__func__);
    AVCodec *codec = NULL;

    /*register all formats and codecs */  
    av_register_all();
    codec= avcodec_find_decoder(codecID);

    if(codec != NULL) {  
        return 0;  
    } else  {  
        return -1;  
    }
#endif
}
#define JNIREG_CLASS "com/az/ffmpegapp/FFmpegNative"  //class name to be registered
/**
* Table of methods associated with a single class.
*/
static JNINativeMethod gMethods[] = {
        { "avcodec_find_decoder", "(I)I", (void*)native_avcodec_find_decoder }
};

/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
        JNINativeMethod* gMethods, int numMethods)
{
        jclass clazz;
        clazz = (*env)->FindClass(env, className);
        if (clazz == NULL) {
                return JNI_FALSE;
        }
        if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
                return JNI_FALSE;
        }

        return JNI_TRUE;
}


/*
* Register native methods for all classes we know about.
*/
static int registerNatives(JNIEnv* env)
{
        if (!registerNativeMethods(env, JNIREG_CLASS, gMethods,
                                 sizeof(gMethods) / sizeof(gMethods[0])))
                return JNI_FALSE;

        return JNI_TRUE;
}

/*
* Set some test stuff up.
*
* Returns the JNI version on success, -1 on failure.
*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
        JNIEnv* env = NULL;
        jint result = -1;
    LOGI("--------------------------------------------------------------------\n");
    LOGI("OnLoad: %s:%s\n", __DATE__, __TIME__);
    LOGI("--------------------------------------------------------------------\n");

        if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {
                return -1;
        }
        assert(env != NULL);

        if (!registerNatives(env)) {
                return -1;
        }
        /* success -- return valid version number */
        result = JNI_VERSION_1_6;

        return result;
}
#ifdef __cplusplus
}
#endif
#endif

 

jni/Android.mk

jni$ cat Android.mk 
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := avcodec-56-prebuilt
LOCAL_SRC_FILES := ffmpeg/lib/libavcodec-56.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=avdevice-56-prebuilt
LOCAL_SRC_FILES :=ffmpeg/lib/libavdevice-56.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=avfilter-5-prebuilt
LOCAL_SRC_FILES :=ffmpeg/lib/libavfilter-5.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=avformat-56-prebuilt
LOCAL_SRC_FILES :=ffmpeg/lib/libavformat-56.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=  avutil-54-prebuilt
LOCAL_SRC_FILES :=ffmpeg/lib/libavutil-54.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=  avswresample-1-prebuilt
LOCAL_SRC_FILES :=ffmpeg/lib/libswresample-1.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=  swscale-3-prebuilt
LOCAL_SRC_FILES :=ffmpeg/lib/libswscale-3.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=ffmpeg_codec
LOCAL_SRC_FILES :=FFmpegNative.c
LOCAL_LDLIBS := -llog #-ljnigraphics -lz -landroid
LOCAL_C_INCLUDES += $(LOCAL_PATH)/ffmpeg/include
LOCAL_SHARED_LIBRARIES:= \
	avcodec-56-prebuilt \
	avdevice-56-prebuilt \
	avfilter-5-prebuilt \
	avformat-56-prebuilt \
	avutil-54-prebuilt
include $(BUILD_SHARED_LIBRARY)

 src/com/az/ffmpegapp/FFmpegNative.java

package com.az.ffmpegapp;  
public class FFmpegNative {  
    static{  
        System.loadLibrary("avutil-54");  
        System.loadLibrary("avcodec-56");  
        System.loadLibrary("swresample-1");  
        System.loadLibrary("avformat-56");  
        System.loadLibrary("swscale-3");  
        System.loadLibrary("avfilter-5");  
        System.loadLibrary("ffmpeg_codec");  
    }  
    public static native int avcodec_find_decoder(int codecID);  
}  

 src/com/az/ffmpegapp/MainActivity.java

package com.az.ffmpegapp;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends ActionBarActivity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
        if (FFmpegNative.avcodec_find_decoder(28)==0)
            Log.d("MainActivity","Find decoder 28");
        else
            Log.d("MainActivity","Not Find");
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
}

 

 

posted @ 2015-08-15 21:34  baopu  阅读(1191)  评论(0编辑  收藏  举报