android studio 移植 ffmpeg

android进行ffmpeg的移植操作,,首先准备工作是编译ffmpeg的源码生成静态库。这里在linux环境下编译,下载以后解压,然后准备ndk就行了。
ffmpeg 下载地址https://ffmpeg.org/download.html
下载FFmpeg源代码之后,首先需要对源代码中的configure文件进行修改。。在configure文件中找到下面几行代码:

SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'  
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'  
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'  
SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR)$(SLIBNAME)' 

替换为

    SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'  
    LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'  
    SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'  
    SLIB_INSTALL_LINKS='$(SLIBNAME)'  

这个是因为编译完版本号格式的问题,之前的是studio不能识别的。然后就是建立编译的脚本,编译即可。脚本如下

cd ffmpeg-3.1.5

make clean

export NDK=/home/spc/android-ndk-r10b
export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt
export PLATFORM=$NDK/platforms/android-8/arch-arm
export PREFIX=../lib
build_one(){
  ./configure --target-os=linux --prefix=$PREFIX \
--enable-cross-compile \
--enable-runtime-cpudetect \
--disable-asm \
--arch=arm \
--cc=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-gcc \
--cross-prefix=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi- \
--disable-stripping \
--nm=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-nm \
--sysroot=$PLATFORM \
--enable-gpl --enable-shared --disable-static --enable-small \
--disable-ffprobe --disable-ffplay --disable-ffmpeg --disable-ffserver --disable-debug \
--extra-cflags="-fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -march=armv7-a" 
}

build_one

make
make install

cd ..

这里写图片描述

需要注意的是ndk的目录替换成自己的,检查下目录是否存在就行。
在源码外层文件夹建立脚本文件,然后简单暴力点就chmod 777 直接赋予可写可执行就行。然后./ 执行脚本。就可以编译。
完成编译以后,在源码外层lib文件下就是编译so 准备工作到此完成

下面正式开始
新建一个工程,在gradle的defaultConfig里面 配置上

  sourceSets.main {
            jni.srcDirs = []
            jniLibs.srcDir 'src/main/libs'
        }

这样,指定磨人的jni目录为空,在main目录下新建一个jni文件夹,这就是ndk编译的目录。

这里写图片描述
首先先写几个native方法, 获取下ffmpeg的信息,

public class NativeMethods  {

    native  String helloWorld();
    public native String urlprotocolinfo();
    public native String avformatinfo();
    public native String avcodecinfo();
    public native String avfilterinfo();
}

然后javah 生成签名。在main下面的java目录执行 javah

 javah -d  ../jni com.example.xxx.NativeMethods

生成头文件自己在jni目录新建一个c文件,实现刚才头文件里面的方法。
c代码如下

#include "com_example_spc_mmfpeghello_NativeMethods.h"
#include <android/log.h>
#include <string.h>
#include <jni.h>
#include <stdio.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"


#define TAG "myDemo-jni" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型


JNIEXPORT jstring JNICALL Java_com_example_spc_mmfpeghello_NativeMethods_helloWorld        (JNIEnv *env, jobject obj)
{
    char info[10000] = {0};
    sprintf(info, "%s\n", avcodec_configuration());
    return (*env)->NewStringUTF(env, info);
}


JNIEXPORT jstring JNICALL Java_com_example_spc_mmfpeghello_NativeMethods_urlprotocolinfo
        (JNIEnv *env, jobject obj)
{
    char info[40000]={0};
    av_register_all();

    struct URLProtocol *pup = NULL;
    //Input
    struct URLProtocol **p_temp = &pup;
    avio_enum_protocols((void **)p_temp, 0);
    while ((*p_temp) != NULL){
        sprintf(info, "%s[In][%10s]\n", info, avio_enum_protocols((void **)p_temp, 0));
    }
    pup = NULL;
    //Output
    avio_enum_protocols((void **)p_temp, 1);
    while ((*p_temp) != NULL){
        sprintf(info, "%s[Out][%10s]\n", info, avio_enum_protocols((void **)p_temp, 1));
    }

    //LOGE("%s", info);
    return (*env)->NewStringUTF(env, info);

}


JNIEXPORT jstring JNICALL Java_com_example_spc_mmfpeghello_NativeMethods_avformatinfo
        (JNIEnv *env, jobject obj)
{

    char info[40000] = { 0 };

    av_register_all();

    AVInputFormat *if_temp = av_iformat_next(NULL);
    AVOutputFormat *of_temp = av_oformat_next(NULL);
    //Input
    while(if_temp!=NULL){
        sprintf(info, "%s[In ][%10s]\n", info, if_temp->name);
        if_temp=if_temp->next;
    }
    //Output
    while (of_temp != NULL){
        sprintf(info, "%s[Out][%10s]\n", info, of_temp->name);
        of_temp = of_temp->next;
    }
    //LOGE("%s", info);
    return (*env)->NewStringUTF(env, info);

}


 JNIEXPORT jstring     JNICALL Java_com_example_spc_mmfpeghello_NativeMethods_avcodecinfo
                         (JNIEnv *env, jobject obj)
  {
      char info[40000] = { 0 };

       av_register_all();

      AVCodec *c_temp = av_codec_next(NULL);

       while(c_temp!=NULL){
        if (c_temp->decode!=NULL){
                 sprintf(info, "%s[Dec]", info);
             }
             else{
                 sprintf(info, "%s[Enc]", info);
             }
             switch (c_temp->type){
                 case AVMEDIA_TYPE_VIDEO:
                     sprintf(info, "%s[Video]", info);
                     break;
                 case AVMEDIA_TYPE_AUDIO:
                     sprintf(info, "%s[Audio]", info);
                     break;
                 default:
                     sprintf(info, "%s[Other]", info);
                     break;
             }
             sprintf(info, "%s[%10s]\n", info, c_temp->name);


             c_temp=c_temp->next;
         }
         //LOGE("%s", info);

         return (*env)->NewStringUTF(env, info);

     }


 JNIEXPORT jstring     JNICALL Java_com_example_spc_mmfpeghello_NativeMethods_avfilterinfo
                         (JNIEnv *env, jobject obj)
 {
  char info[40000] = { 0 };
         avfilter_register_all();
         AVFilter *f_temp = (AVFilter *)avfilter_next(NULL);
         while (f_temp != NULL){
           sprintf(info, "%s[%10s]\n", info, f_temp->name);
             f_temp = f_temp->next;
                }
        return (*env)->NewStringUTF(env, info);

 }

这里是参照的雷神的文章,写了几个测试的方法。

最后就是调用了。

public class MainActivity extends AppCompatActivity {
    static {
        System.loadLibrary("avutil-55");
        System.loadLibrary("avcodec-57");
        System.loadLibrary("avformat-57");
        System.loadLibrary("avdevice-57");
        System.loadLibrary("swresample-2");
        System.loadLibrary("swscale-4");
        System.loadLibrary("postproc-54");
        System.loadLibrary("avfilter-6");
        System.loadLibrary("hello_jni");
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void click1(View v) {
        Toast.makeText(this, new NativeMethods().helloWorld(), Toast.LENGTH_SHORT).show();
    }
    public void click2(View v) {
        Toast.makeText(this, new NativeMethods().urlprotocolinfo(), Toast.LENGTH_SHORT).show();
    }
    public void click3(View v) {
        Toast.makeText(this, new NativeMethods().avformatinfo(), Toast.LENGTH_SHORT).show();
    }
    public void click4(View v) {
        Toast.makeText(this, new NativeMethods().avcodecinfo(), Toast.LENGTH_SHORT).show();
    }
    public void click5(View v) {
        Toast.makeText(this, new NativeMethods().avfilterinfo(), Toast.LENGTH_SHORT).show();
    }
}

然后是生成so 。自己写Android.mk文件,放在jni文件夹里面。

 # FFmpeg library的
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := avcodec
LOCAL_SRC_FILES := libavcodec-57.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := avdevice
LOCAL_SRC_FILES := libavdevice-57.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := avfilter
LOCAL_SRC_FILES := libavfilter-6.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := avformat
LOCAL_SRC_FILES := libavformat-57.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := avutil
LOCAL_SRC_FILES := libavutil-55.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := postproc
LOCAL_SRC_FILES := libpostproc-54.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := swresample
LOCAL_SRC_FILES := libswresample-2.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := swscale
LOCAL_SRC_FILES := libswscale-4.so
include $(PREBUILT_SHARED_LIBRARY)

     #自己的so文件
    include $(CLEAR_VARS)
    LOCAL_MODULE := hello_jni
    LOCAL_SRC_FILES := hello_jni.c
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_LDLIBS := -llog -lz
LOCAL_SHARED_LIBRARIES := avcodec avdevice avfilter avformat avutil postproc swresample swscale
include $(BUILD_SHARED_LIBRARY)

到jni目录执行ndk-build命令即可。在生成so过程中可能出现错误,

Unable to recognise the format of the input file x86_64/libavcodec-57.so
make: * libavcodec-57.so] Error 1
make: * Deleting file libavcodec-57.so’
我是参考http://blog.csdn.net/darwinlong/article/details/48178131
由于我是在linux虚拟下编译的ffmpeg,x86 x86-64版本架构不匹配。这里就先跳过了,不生成x86架构的so了,做法是新建立application.mk指定只编译 armeabi。 Application.mk 也是在jni目录下。
Application.mk

    APP_ABI := armeabi armeabi-v7a

Application.mk 仅此一行就可以了。
然后在将 在linux环境编译的so 和头文件复制到jni目录,即可。
最后执行ndk-build就会自动生成so 。
在手机上实验就行了, 模拟器因为没有生成x86的so,所以说是不行了。
这里写图片描述

demo地址:https://github.com/836154942/androidffmpegdemo

猜你喜欢

转载自blog.csdn.net/spinchao/article/details/52953608