在Mac下使用NDK编译FFmpeg3.3.4

概述

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

参考资料:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,670评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,928评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,926评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,238评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,112评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,138评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,545评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,232评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,496评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,596评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,369评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,226评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,600评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,906评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,185评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,516评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,721评论 2 335

推荐阅读更多精彩内容