在Mac下使用NDK编译FFmpeg3.3.4

作者: Lyuanhui_ | 来源:发表于2017-10-25 15:07 被阅读189次

概述

FFmpeg是一套非常强大的音视频处理工具,许多开发过多媒体的朋友都绕不开它,围绕着FFmpeg可以进行诸如音视频解码,裁剪,拼接,音视频合并,以及支持多种流媒体的协议等等

今天就用目前最新的ffmpeg3.3.4源码,使用NDK进行交叉编译,生成Android项目上可以使用的库,然后在APP上输出当前FFmpeg的配置

我的编译环境和IDE如下:

  • ffmpeg 3.3.4 版本源码
  • macOS 10.12.5
  • NDK 13.1.3345770
  • Android Studio 2.3.3

编译FFmpeg类库

下载FFmpeg源码

下载源码的方式有两种:

我这里是通过官网下载的,解压后如下:

build_ffmpeg_1.png

配置脚本

配置configure

由于默认configure脚本编译出来的动态库版本号在文件名后缀.so之后,在Android上是识别不了的,比如长下面这样:

build_ffmpeg_2.png

这时候需要对源码根目录下的configure进行一下小修改,我这个FFmpeg版本是在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)'

修改后的配置如下图,我这里选择的是注释掉:

build_ffmpeg_3.png

配置my_build_android.sh

这个文件源码是没有的,需要我们在源码根目录下手动新建一个。此脚本配置网上有很多,但是因为不同的人编译时用的系统,用的NDK版本,都不可能完全一样,所以这里一定要根据自己的实际情况,一步步找到自己系统ndk所在的目录和arm-linux-androideabi-xx的版本,写到配置上面

另外如果你复制网上的脚本时候有带上空格等情况,运行脚本的时候也有可能报错,这时候需要好好检查

这里提供一份我自己编译时用到的脚本供参考:

# ndk环境    
export NDK=/Users/Lyh/Library/Android/sdk/ndk-bundle
export SYSROOT=$NDK/platforms/android-21/arch-arm
export TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
CPU=armv7-a

# 要保存动态库的目录,这里保存在源码根目录下的android/armv7-a
export PREFIX=$(pwd)/android/$CPU
ADDI_CFLAGS="-marm"
function build_android
{
./configure --target-os=linux --prefix=$PREFIX \
    --enable-cross-compile \
    --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- \
    --arch=arm \
    --sysroot=$SYSROOT \
    --extra-cflags="-Os -fpic $ADDI_CFLAGS" \
    --extra-ldflags="$ADDI_LDFLAGS" \
    $ADDITIONAL_CONFIGURE_FLAG
make clean
# 不确定自己上面的目录或者环境有没有错误时
# 可以先注释一下下面两个命令
make
make install
}
build_android

执行脚本生成so和头文件

进入源码所在目录,建好my_build_android.sh后,第一次执行会提示权限不够,分配权限后重新执行即可,如下图:

build_ffmpeg_4.png

NDK环境和目录执行完正常如下(此时因为不确定环境有无错误,还未执行make):

build_ffmpeg_5.png

这里有个警告可以先忽略,我们在终端继续先执行make和再make install

大约二十分钟左右以后,可以看到已经生成动态库(lib目录)和头文件(include目录):

build_ffmpeg_6.png

注意

这里有个小技巧,就是在第一次运行你的脚本还不确定有没有错时,可以先把makemake install注释掉,这样就不会因为你脚本配置的目录或者环境有问题时,仍然去继续makemake install的执行,然后把你环境的错误给冲掉,给查找问题带来困难

此外make执行的时间比较长,如果是到生成.o文件时出错被中断了,或者make执行完了,才发现没有正确生成so的时候再回去检查原因,就浪费非常多时间了

总结

先确保NDK环境和目录没报错,再去执行make命令

在Android项目上使用

创建Android项目

跟创建一般的项目稍有不同的是下面两个勾选
添加C++支持

build_ffmpeg_7.png build_ffmpeg_8.png

把生成的头文件和so导入到Android项目

这里我们参照NDK-Sample的hello-libs写法,smaple的下载地址是:https://github.com/googlesamples/android-ndk ,已经有的可以不用再下
把第三方的so放到项目根目录中,结构如下:

build_ffmpeg_9.png

编写CmakeList和Gradle

把hello-libs项目里的CmakeList.txt文件复制到我们的项目中进行改造,原sample的cmakelist内容如下:


#
# Copyright (C) The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

cmake_minimum_required(VERSION 3.4.1)

# configure import libs
set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../distribution)

add_library(lib_gmath STATIC IMPORTED)
set_target_properties(lib_gmath PROPERTIES IMPORTED_LOCATION
    ${distribution_DIR}/gmath/lib/${ANDROID_ABI}/libgmath.a)

# shared lib will also be tucked into APK and sent to target
# refer to app/build.gradle, jniLibs section for that purpose.
# ${ANDROID_ABI} is handy for our purpose here. Probably this ${ANDROID_ABI} is
# the most valuable thing of this sample, the rest are pretty much normal cmake
add_library(lib_gperf SHARED IMPORTED)
set_target_properties(lib_gperf PROPERTIES IMPORTED_LOCATION
    ${distribution_DIR}/gperf/lib/${ANDROID_ABI}/libgperf.so)

# build application's shared lib
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")

add_library(hello-libs SHARED
            hello-libs.cpp)

target_include_directories(hello-libs PRIVATE
                           ${distribution_DIR}/gmath/include
                           ${distribution_DIR}/gperf/include)

target_link_libraries(hello-libs
                      android
                      lib_gmath
                      lib_gperf
                      log)

把大段的注释删掉,再改写后变成了下面这样


cmake_minimum_required(VERSION 3.4.1)

# configure import libs
# 这里跟原Demo写法不一样要注意一下,因为Demo的CMakelist文件是在app下面的cpp目录中
set(distribution_DIR ${CMAKE_SOURCE_DIR}/../ffmpeg)

add_library(avcodec-57 SHARED IMPORTED)
set_target_properties(avcodec-57  PROPERTIES IMPORTED_LOCATION
    ${distribution_DIR}/lib/armeabi-v7a/libavcodec-57.so)

# build application's shared lib
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")

add_library(native-lib SHARED
            src/main/cpp/native-lib.cpp)

include_directories(native-lib PRIVATE
                    ${distribution_DIR}/include)

target_link_libraries(native-lib
                      android
                      avcodec-57
                      log)

说明:

  • 有个set方法设置so路径的地方需要格外注意一下,这个错了下面都会影响到
  • Demo的hello-libs是同时添加静态库和动态库,这里不需要,就把添加静态库部分删掉了

这里我们暂且只加入avcodec-57这个动态库

另外app目录下的gradle文件也要配置一下:

android {
  compileSdkVersion 26
  buildToolsVersion "26.0.0"
  defaultConfig {
    applicationId "org.lyh.ffmpegdemo"
    minSdkVersion 14
    targetSdkVersion 26
    versionCode 1
    versionName "1.0"
    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    ndk {
      abiFilters 'armeabi-v7a'
    }
    externalNativeBuild {
      cmake {
        cppFlags "-frtti -fexceptions"
      }
    }
  }
  buildTypes {
    release {
      minifyEnabled false
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
  }

  sourceSets {
    main {
      // 注意路径要写对
      jniLibs.srcDirs = ['../ffmpeg/lib']
    }
  }

  externalNativeBuild {
    cmake {
      path 'CMakeLists.txt'
    }
  }
}

重点注意的是jniLibs.srcDirs的目录要配置正确,架构这里只生成armv7-a

编写C代码和Java代码

C代码如下:

#include <jni.h>
#include <string>

extern "C" {
#include "libavcodec/avcodec.h"
    JNIEXPORT jstring JNICALL
    Java_org_lyh_ffmpegdemo_MainActivity_stringFromJNI(
            JNIEnv *env,
            jobject /* this */) {
    //    std::string hello = "Hello from C++";
        char info[10000] = {0};
        sprintf(info, "%s\n", avcodec_configuration());
        return env->NewStringUTF(info);
    }
}

原先自动生成的项目是运行后屏幕显示”Hello from C++“。这里修改一下返回的字符串,改成得到FFmpeg的配置信息返回到Java层,然后显示到Textview中。

Java代码没做修改,如下:

public class MainActivity extends AppCompatActivity {

  // Used to load the 'native-lib' library on application startup.
  static {
    System.loadLibrary("native-lib");
  }

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

    // Example of a call to a native method
    TextView tv = (TextView) findViewById(R.id.sample_text);
    tv.setText(stringFromJNI());
  }

  /**
   * A native method that is implemented by the 'native-lib' native library,
   * which is packaged with this application.
   */
  public native String stringFromJNI();
}

运行结果

最后运行如下:

build_ffmpeg_10.png

参考资料:

相关文章

网友评论

    本文标题:在Mac下使用NDK编译FFmpeg3.3.4

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