美文网首页Android
一、基于Cmake编译工具Android客户端集成FFmpeg

一、基于Cmake编译工具Android客户端集成FFmpeg

作者: Erich_Godsen | 来源:发表于2020-11-22 12:45 被阅读0次

    项目背景

    最近因公司业务需要,需要写一个视频播放器,目前还属于前期调研阶段,综合看了GoogleExoPlayerBilibiliIJKplayer,决定还是基于FFmpeg来写一个轻量级的播放器,目前稍微有了一点进展,现在记录一下,其他人如果有需要也可以参考一下

    下载好了以后记得把版本切换到n3.6.6,最新版本在mac上编译会报下面这个错误

    /bin/sh: ranlib/usr/local/lib/libavdevice.a: No such file or directory
    
    1. 修改配置文件
      大概在3305行左右,修改成如下配置
    #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)'  
    
    1. 添加Android编译脚本
      新建一个文件名为build_android.sh的文件,然后用文本编辑器打开,添加如下内容
    #!/bin/bash
    export NDK_HOME=/Users/zhangsan/Documents/android-ndk-r10e
    export PLATFORM_VERSION=android-9
    function build
    {
        echo "start build ffmpeg for $ARCH"
        ./configure --target-os=linux \
        --prefix=$PREFIX --arch=$ARCH \
        --disable-doc \
        --enable-shared \
        --disable-static \
        --disable-yasm \
        --disable-asm \
        --disable-symver \
        --enable-gpl \
        --disable-ffmpeg \
        --disable-ffplay \
        --disable-ffprobe \
        --disable-ffserver \
        --cross-prefix=$CROSS_COMPILE \
        --enable-cross-compile \
        --sysroot=$SYSROOT \
        --enable-small \
        --extra-cflags="-Os -fpic $ADDI_CFLAGS" \
        --extra-ldflags="$ADDI_LDFLAGS" \
        $ADDITIONAL_CONFIGURE_FLAG
        make clean
        make
        make install
        echo "build ffmpeg for $ARCH finished"
    }
    
    #arm
    ARCH=arm
    CPU=arm
    PREFIX=$(pwd)/android/$ARCH
    TOOLCHAIN=$NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
    CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi-
    ADDI_CFLAGS="-marm"
    SYSROOT=$NDK_HOME/platforms/$PLATFORM_VERSION/arch-$ARCH/
    build
    
    #x86
    ARCH=x86
    CPU=x86
    PREFIX=$(pwd)/android/$ARCH
    TOOLCHAIN=$NDK_HOME/toolchains/x86-4.9/prebuilt/darwin-x86_64
    CROSS_COMPILE=$TOOLCHAIN/bin/i686-linux-android-
    ADDI_CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32"
    SYSROOT=$NDK_HOME/platforms/$PLATFORM_VERSION/arch-$ARCH/
    build
    

    其中需要注意将文件上方NDK_HOME的路径换成自己的路径
    然后记得给这个脚本添加执行权限,命令可参考
    chmod 777 ./build_android.sh

    1. 执行编译命令
      在终端里执行
      ./build_android.sh
      开始编译,如果编译顺利,最后你应该会看到下面这句话
    build ffmpeg for x86 finished
    

    然后在当前项目下面应该会看到android文件夹,下面有两种架构对应的不同文件夹arm, x86

    新建一个支持C++开发的Android工程

    工程准备就绪后,在app/libs文件下新建x86,arm-v7a文件夹,然后将上面编译生成的文件夹下的
    libavcodec-57.so
    libavdevice-57.so
    libavfilter-6.so
    libavformat-57.so
    libavutil-55.so
    libpostproc-54.so
    libswresample-2.so
    libswscale-4.so
    拷贝到对应路径下(arm拷贝到arm-v7a下),头文件随便拷贝一个就行

    然后将app/build.gradle添加如下配置

    defaultConfig {
            applicationId "com.test.meida"
            minSdkVersion 23
            targetSdkVersion 30
            versionCode 1
            versionName "1.0"
    
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
            externalNativeBuild {
                cmake {
                    arguments "-DANDROID_APP_PLATFORM=android-16", "-DANDROID_STL=c++_static"
                    cppFlags ""
                    abiFilters "armeabi-v7a", "x86"
                }
    
    
            }
    //
    //        ndk{
    //            abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
    //        }
    
        }
    
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
            debug {
                debuggable true
                jniDebuggable true
                ndk {
                    debuggable true
                }
            }
        }
        externalNativeBuild {
    //        ndkBuild {
    //            path file("src\\main\\jni\\Android.mk")
    //        }
            cmake {
                path "src/main/cpp/CMakeLists.txt"
                version "3.10.2"
            }
        }
    
        lintOptions {
            abortOnError false
        }
    
    

    再修改相应cpp文件夹下的CMakeLists.txt为如下

    # For more information about using CMake with Android Studio, read the
    # documentation: https://d.android.com/studio/projects/add-native-code.html
    
    # Sets the minimum version of CMake required to build the native library.
    
    cmake_minimum_required(VERSION 3.4.1)
    
    #include_directories(${CMAKE_SOURCE_DIR}/libsdl/)
    #add_subdirectory(${CMAKE_SOURCE_DIR}/libsdl)
    
    
    # 定义变量
    set(distribution_DIR /Users/Zhangsan/Documents/Work/MaMedia/app/libs)
    
    # Creates and names a library, sets it as either STATIC
    # or SHARED, and provides the relative paths to its source code.
    # You can define multiple libraries, and CMake builds them for you.
    # Gradle automatically packages shared libraries with your APK.
    
    add_library( # Sets the name of the library.
                myplayer
    
                 # Sets the library as a shared library.
                 SHARED
    
                 # Provides a relative path to your source file(s).
                 native-lib.cpp
            )
    
    add_library( avcodec-57
            SHARED
            IMPORTED )
    
    set_target_properties( avcodec-57
            PROPERTIES IMPORTED_LOCATION
            ${distribution_DIR}/${ANDROID_ABI}/libavcodec-57.so )
    
    add_library( avdevice-57
            SHARED
            IMPORTED )
    
    set_target_properties( avdevice-57
            PROPERTIES IMPORTED_LOCATION
            ${distribution_DIR}/${ANDROID_ABI}/libavdevice-57.so )
    
    
    add_library( avfilter-6
            SHARED
            IMPORTED )
    
    set_target_properties( avfilter-6
            PROPERTIES IMPORTED_LOCATION
            ${distribution_DIR}/${ANDROID_ABI}/libavfilter-6.so )
    
    
    add_library( avformat-57
            SHARED
            IMPORTED )
    
    set_target_properties( avformat-57
            PROPERTIES IMPORTED_LOCATION
            ${distribution_DIR}/${ANDROID_ABI}/libavformat-57.so )
    
    
    add_library( avutil-55
            SHARED
            IMPORTED )
    
    set_target_properties( avutil-55
            PROPERTIES IMPORTED_LOCATION
            ${distribution_DIR}/${ANDROID_ABI}/libavutil-55.so )
    
    
    add_library( postproc-54
            SHARED
            IMPORTED )
    
    set_target_properties( postproc-54
            PROPERTIES IMPORTED_LOCATION
            ${distribution_DIR}/${ANDROID_ABI}/libpostproc-54.so )
    
    
    add_library( swresample-2
            SHARED
            IMPORTED )
    set_target_properties( swresample-2
            PROPERTIES IMPORTED_LOCATION
            ${distribution_DIR}/${ANDROID_ABI}/libswresample-2.so )
    
    
    add_library( swscale-4
            SHARED
            IMPORTED )
    
    set_target_properties( swscale-4
            PROPERTIES IMPORTED_LOCATION
            ${distribution_DIR}/${ANDROID_ABI}/libswscale-4.so )
    
    # Searches for a specified prebuilt library and stores the path as a
    # variable. Because CMake includes system libraries in the search path by
    # default, you only need to specify the name of the public NDK library
    # you want to add. CMake verifies that the library exists before
    # completing its build.
    
    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 )
    
    find_library(
            android-lib
    
            android)
    
    include_directories(${distribution_DIR}/../src/main/cpp/)
    
    # Specifies libraries CMake should link to your target library. You
    # can link multiple libraries, such as libraries you define in this
    # build script, prebuilt third-party libraries, or system libraries.
    
    target_link_libraries( # Specifies the target library.
                           myplayer
            avcodec-57
            avdevice-57
            avfilter-6
            avformat-57
            avutil-55
            postproc-54
            swresample-2
            swscale-4
    
                           # Links the target library to the log library
                           # included in the NDK.
                           ${android-lib}
                           ${log-lib})
    
    • 编写测试代码
      native-lib.cpp文件中添加如下代码
    #define JNI_CLASS_PLAYER  "com/test/media/MediaPlayer"
    
    static jstring
    MediaPlayer_getInfo(JNIEnv *env, jobject thiz) {
        char info[40000] = {0};
        avfilter_register_all();
    
        AVFilter *f_temp = (AVFilter *) avfilter_next(NULL);
        while (f_temp != NULL) {
            sprintf(info, "%s%s\n", info, f_temp->name);
            f_temp = f_temp->next;
        }
        return env->NewStringUTF(info);
    }
    
    static JNINativeMethod g_methods[] = {
            {"_getInfo",  "()Ljava/lang/String;",                          (void *)(MediaPlayer_getInfo)}
    };
    
    JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
    {
        JNIEnv* env = NULL;
        jint result = -1;
    
        jclass clazz = NULL;
    
        if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
            LOGE("ERROR: GetEnv failed\n");
            goto fail;
        }
    
    
        clazz = env->FindClass(JNI_CLASS_PLAYER);
    
        if (clazz) {
            env->RegisterNatives(clazz, g_methods, NELEM(g_methods));
        } else {
            LOGE("ERROR: %s is Not Found", JNI_CLASS_PLAYER);
            goto fail;
        }
    
    
        result = JNI_VERSION_1_4;
    
        fail:
        return result;
    }
    
    JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved)
    {
        LOGE("JNI_OnUnload");
    }
    

    上层对应的java代码如下:

    public class MediaPlayer {
        static {
            System.loadLibrary("myplayer");
        }
    
    
        public native String _getInfo();
    
    

    MainActivity中调用此方法

       tv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    MediaPlayer mediaPlayer = new MediaPlayer();
                    Log.d("MainActivity", "getInfo:" + mediaPlayer._getInfo());
                }
            });
    

    如果在logcat中打印如下信息

    2020-11-22 12:37:58.249 4416-4416/com.test.meida D/MainActivity: getInfo:abench
        acompressor
        acrossfade
        acrusher
        adelay
        aecho
        aemphasis
        aeval
        afade
        afftfilt
        ...
    

    那么恭喜你,成功了!

    相关文章

      网友评论

        本文标题:一、基于Cmake编译工具Android客户端集成FFmpeg

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