Android

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

2020-11-22  本文已影响0人  Erich_Godsen

项目背景

最近因公司业务需要,需要写一个视频播放器,目前还属于前期调研阶段,综合看了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})
#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
    ...

那么恭喜你,成功了!

上一篇下一篇

猜你喜欢

热点阅读