美文网首页android_ffmpeg
[1].FFMpeg入门之 - Android集成FFMpeg

[1].FFMpeg入门之 - Android集成FFMpeg

作者: 73a8b2d73e7d | 来源:发表于2017-09-08 11:34 被阅读184次

    我的开发环境是ubuntu17.04 + as2.3.3 + android-ndk-r11c. 不同的环境配置不一样,请根据实际环境做一定的调整。
    1.首先去github上下载ffmpeg的源码:https://github.com/FFmpeg/FFmpeg
    2.打开源码工程,找到configure文件,vim打开他。找到这几行

    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)'
    

    目的是将生成.so名称规范化,用以满足android build。

    image.png

    3.在源码目录下新建一个build_android.sh文件。
    内容:

    #!/bin/bash
    NDK=/home/dengxuan/Android/Sdk/ndk-bundle
    SYSROOT=$NDK/platforms/android-9/arch-arm/
    TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
    export TMPDIR=/home/dengxuan/workspace/github/FFmpeg/jni/temp
    function build_one
    {
    ./configure \
     --prefix=$PREFIX \   //--prefix安装路径,如果发现设置了不生效,它还是给你安装到/usr/local目录下去了。可以修改.configure文件,找到prefix_default 将其改成你要的目录即可
     --enable-shared \
     --disable-static \
     --disable-doc \
     --disable-ffmpeg \
     --disable-ffplay \
     --disable-ffprobe \
     --disable-ffserver \
     --disable-avdevice \
     --disable-doc \
     --disable-symver \
     --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
     --target-os=linux \
     --arch=arm \
     --enable-cross-compile \
     --sysroot=$SYSROOT \
     --extra-cflags="-Os -fpic $ADDI_CFLAGS" \
     --extra-ldflags="$ADDI_LDFLAGS" \
     $ADDITIONAL_CONFIGURE_FLAG
    make clean
    make
    make install
    }
    CPU=armeabi-v7a
    PREFIX=$(pwd)/android/$CPU
    ADDI_CFLAGS="-marm"
    build_one
    

    解释下:
    [1].NDK 填你本地的ndk安装路径。执行命令 which ndk-build 即可看到目录在哪。
    [2].TOOLCHAIN 找到你自己ndk目录下的toolchains,然后后面的路径都要改成自己的,如果是再windows上,可能toolchains就是TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/windows。需要依据实际环境调整。
    [3].export TMPDIR=/home/dengxuan/workspace/github/FFmpeg/jni/temp。这一行是因为刚开始build的时候没加这一行,有可能会报arm-linux-androideabi-gcc-4.4.3 is unable to create an executable file 这个错。基本上就是执行这个sh文件的时候,创建目录的时候没有权限。需要手动先把文件夹创建好。因为执行命令的过程中会一直有零时文件生成,这里相当于是建了个文件夹,让他在这个文件夹下生成零时文件。
    [4].CPU 处理器类型 armeabi-v7a

    4.打开命令行 执行 ./build_android.sh 命令 就开始构建了。


    image.png
    image.png

    5.构建成功后,会在当前目录下生成一个android文件夹,也就是上面的脚本中指定的PREFIX=$(pwd)/android/$CPU


    image.png

    6.将FFMpeg集成进现有的工程。我这里是集成到一个lib工程下,集成到主工程也可参照下面的方法
    首先在lib工程下新建libs文件夹,讲第五步生成的文件拷贝进来,目录树结构如下:


    image.png

    然后修改lib工程的build.gradle文件:
    添加如下配置:

    image.png
    externalNativeBuild {
                cmake {
                    cppFlags "-frtti -fexceptions"
                }
            }
            ndk {
                abiFilters 'armeabi-v7a'
            }
    externalNativeBuild {
            cmake {
                path "CMakeLists.txt"
            }
        }
    sourceSets {
            main {
                jniLibs.srcDirs = ['libs']
            }
        }
    

    再然后在lib工程下新建 上面写的CMakeLists.txt文件

    # Sets the minimum version of CMake required to build the native
    # library. You should either keep the default value or only pass a
    # value of 3.4.0 or lower.
    
    cmake_minimum_required(VERSION 3.4.1)
    
    find_library( # Sets the name of the path variable.
                  log-lib
    
                  # Specifies the name of the NDK library that
                  # you want CMake to locate.
                  log )
    
    set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../libs)
    
    add_library( avutil-55
                 SHARED
                 IMPORTED )
    set_target_properties( avutil-55
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libavutil-55.so )
    
    add_library( swresample-2
                 SHARED
                 IMPORTED )
    set_target_properties( swresample-2
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libswresample-2.so )
    add_library( avcodec-57
                 SHARED
                 IMPORTED )
    set_target_properties( avcodec-57
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libavcodec-57.so )
    add_library( avfilter-6
                 SHARED
                 IMPORTED)
    set_target_properties( avfilter-6
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libavfilter-6.so )
    add_library( swscale-4
                 SHARED
                 IMPORTED)
    set_target_properties( swscale-4
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libswscale-4.so )
    
    add_library( avformat-57
                 SHARED
                 IMPORTED)
    set_target_properties( avformat-57
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libavformat-57.so )
    
    
    
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
    
    add_library( native-lib
                 SHARED
                 src/main/cpp/native-lib.cpp
                  )
    
    include_directories(libs/include)
    
    #target_include_directories(native-lib PRIVATE libs/include)
    
    target_link_libraries( native-lib swresample-2 avcodec-57 avfilter-6 swscale-4 avformat-57
                           ${log-lib} )
    

    再在main目录下新建cpp目录,添加如下cpp文件:native-lib.cpp

    #include <jni.h>
    #include <string>
    extern "C" {
    
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavfilter/avfilter.h>
    
    JNIEXPORT jstring JNICALL
    Java_com_xxx_svsdk_tools_FFMpegUtils_avcodecinfo(JNIEnv *env, jobject instance) {
    
        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, "%sdecode:", info);
            } else {
                sprintf(info, "%sencode:", 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;
        }
        return env->NewStringUTF(info);
    }
    
    extern "C"
    jstring
    Java_com_xxx_svsdk_tools_FFMpegUtils_stringFromJNI(
            JNIEnv *env,
            jobject /* this */) {
        std::string hello = "Hello from C++";
        return env->NewStringUTF(hello.c_str());
    }
    
    }
    

    然后就是新建一个java类,来导入native方法了

    image.png
    package com.xxx.svsdk.tools;
    
    /**
     * Created by dengxuan on 17-9-8.
     */
    
    public class FFMpegUtils {
        public static native String stringFromJNI();
    
        public static native String avcodecinfo();
        // Used to load the 'native-lib' library on application startup.
        static {
            System.loadLibrary("native-lib");
        }
    }
    

    然后就可以新建个activity来测试导入的方法了

    @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.sample_activity);
            textView = (TextView) findViewById(R.id.textview);
            textView.append(FFMpegUtils.stringFromJNI()+"\n"+FFMpegUtils.avcodecinfo());
        }
    

    运行:


    image.png

    7.将FFMpeg集成到新的android工程。新建android工程,选中include c++ support

    image.png

    在工程目录下新建libs文件夹把刚才第五步中生成的android目录下的文件拷贝过来。

    image.png

    8.打开app目录下的 CMakeLists.txt 写入如下配置:

    # Sets the minimum version of CMake required to build the native
    # library. You should either keep the default value or only pass a
    # value of 3.4.0 or lower.
    
    cmake_minimum_required(VERSION 3.4.1)
    
    find_library( # Sets the name of the path variable.
                  log-lib
    
                  # Specifies the name of the NDK library that
                  # you want CMake to locate.
                  log )
    
    set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../libs)
    
    add_library( avutil-55
                 SHARED
                 IMPORTED )
    set_target_properties( avutil-55
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libavutil-55.so )
    
    add_library( swresample-2
                 SHARED
                 IMPORTED )
    set_target_properties( swresample-2
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libswresample-2.so )
    add_library( avcodec-57
                 SHARED
                 IMPORTED )
    set_target_properties( avcodec-57
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libavcodec-57.so )
    add_library( avfilter-6
                 SHARED
                 IMPORTED)
    set_target_properties( avfilter-6
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libavfilter-6.so )
    add_library( swscale-4
                 SHARED
                 IMPORTED)
    set_target_properties( swscale-4
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libswscale-4.so )
    
    add_library( avformat-57
                 SHARED
                 IMPORTED)
    set_target_properties( avformat-57
                           PROPERTIES IMPORTED_LOCATION
                           ../../../../libs/armeabi-v7a/libavformat-57.so )
    
    
    
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
    
    add_library( native-lib
                 SHARED
                 src/main/cpp/native-lib.cpp
                  )
    
    include_directories(libs/include)
    
    #target_include_directories(native-lib PRIVATE libs/include)
    
    target_link_libraries( native-lib swresample-2 avcodec-57 avfilter-6 swscale-4 avformat-57
                           ${log-lib} )
    

    编辑配置build.gralde文件

    android {
     ......
        defaultConfig {
            ....
            ndk {
                abiFilters 'armeabi-v7a'
            }
        }
    
        sourceSets {
            main {
                jniLibs.srcDirs = ['libs']
            }
        }
    }
    

    编写Activity

    public class MainActivity extends Activity {
    
        private TextView textView;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            textView = new TextView(this);
            setContentView(textView);
            textView.setText(stringFromJNI());
        }
    
        public native String stringFromJNI();
    
        public native String avcodecinfo();
        // Used to load the 'native-lib' library on application startup.
        static {
            System.loadLibrary("native-lib");
        }
    
    }
    

    在src/main/cpp/native-lib.cpp编写C++代码:

    #include <jni.h>
    #include <string>
    extern "C" {
    
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    #include <libavfilter/avfilter.h>
    
    JNIEXPORT jstring JNICALL
    Java_com_dx_myffmpeg_MainActivity_avcodecinfo(JNIEnv *env, jobject instance) {
    
        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, "%sdecode:", info);
            } else {
                sprintf(info, "%sencode:", 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;
        }
        return env->NewStringUTF(info);
    }
    
    extern "C"
    jstring
    Java_com_dx_myffmpeg_MainActivity_stringFromJNI(
            JNIEnv *env,
            jobject /* this */) {
        std::string hello = "Hello from C++";
        return env->NewStringUTF(hello.c_str());
    }
    }
    

    注意两个native方法前面的路径 “com_dx_myffmpeg” 需要改成MainActivity对应的package下!
    然后就可以运行程序了:

    image.png

    相关文章

      网友评论

        本文标题:[1].FFMpeg入门之 - Android集成FFMpeg

        本文链接:https://www.haomeiwen.com/subject/spwijxtx.html