美文网首页
Linux下ndk编译移植FFmpeg到Android平台

Linux下ndk编译移植FFmpeg到Android平台

作者: 第八区 | 来源:发表于2017-10-31 17:13 被阅读1363次

    简介

    • linux下编译FFmpeg
    • Android项目使用FFmpeg
    • 编译运行
    • 常见问题

    linux下编译FFmpeg

    开发环境配置
    • FFmpeg编译环境 centos6.8 64位
    [root@iZ94g6hanmqZ include]# lsb_release -a
    LSB Version:    :base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-noarch
    Distributor ID: CentOS
    Description:    CentOS release 6.8 (Final)
    Release:    6.8
    Codename:   Final
    
    • linux下安装sdkmanager以及ndk
      参考Linux下Android构建环境,可以sdk加入的环境变量中,方便执行sdkmanager命令。
    export ANDROID_HOME=/usr/local/android
    export PATH=${PATH}:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools/
    

    查看可下载的内容sdkmanager --list

    6.png

    然后下载ndk。执行命令

    sdkmanager 'ndk-bundle'
    

    然后等待下载完成。

    7.png
    • 配置ndk环境变量
    export NDK_HOME=/usr/local/android/ndk-bundle
    export PATH=${PATH}:$NDK_HOME
    
    下载编译FFmpeg

    这里我们选择3.2.4版本(注意:这里使用的3.2.4版本,如果用最新的版本,编译可能出现问题,为了想让大家上手,建议版本先保持一致)。直接github上选择下载解压即可。为了方便编译,我们在解压后的目录中写一个shell脚本来进行配置。build_ffmpeg.sh

    #!/bin/bash
    NDK=/usr/local/android/ndk-bundle
    SYSROOT=$NDK/platforms/android-16/arch-arm/
    TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
    
    function build_one
    {
        ./configure \
            --prefix=$PREFIX \
            --enable-shared \
            --disable-static \
            --disable-doc \
            --disable-ffserver \
            --enable-cross-compile \
            --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
            --target-os=linux \
            --arch=arm \
            --sysroot=$SYSROOT \
            --extra-cflags="-Os -fpic $ADDI_CFLAGS" \
            --extra-ldflags="$ADDI_LDFLAGS" \
            $ADDITIONAL_CONFIGURE_FLAG
    }
    CPU=arm
    PREFIX=$(pwd)/android/$CPU
    ADDI_CFLAGS="-marm"
    build_one
    

    注意:这里的platforms使用的android-16,如果使用更高,可能在app运行时会出现atof相关错误,同样至于为什么,现在先不做解释

    NDK替换成自己的路径即可。接下来给脚本添加运行属性chmod +x build_ffmpeg.sh。现在就可以开始运行build_ffmpeg.sh来生成Makefile。./build_ffmpeg.sh

    1.png
    执行完可能会有一个警告如下:
    WARNING: /usr/local/android/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-pkg-config not found, library detection may fail.
    

    这里不会有影响,直接忽略。接下来就开始编译

    make -j4
    

    编译完成后,安装

    make install
    

    在当前目录下会生成一个android目录。里面有个arm目录

    [root@iZ94g6hanmqZ android]# ll
    total 4
    drwxr-xr-x 6 root root 4096 Oct 31 07:12 arm
    [root@iZ94g6hanmqZ android]# cd arm/
    [root@iZ94g6hanmqZ arm]# ll
    total 16
    drwxr-xr-x 2 root root 4096 Oct 31 07:12 bin
    drwxr-xr-x 9 root root 4096 Oct 31 07:12 include
    drwxr-xr-x 3 root root 4096 Oct 31 07:12 lib
    drwxr-xr-x 3 root root 4096 Oct 31 07:12 share
    

    我们看到有bin、include、lib、share
    lib包含了生成的动态库文件

    -rwxr-xr-x 1 root root 11414264 Oct 31 07:12 libavcodec-57.so
    lrwxrwxrwx 1 root root       16 Oct 31 07:12 libavcodec.so -> libavcodec-57.so
    -rwxr-xr-x 1 root root    59384 Oct 31 07:12 libavdevice-57.so
    lrwxrwxrwx 1 root root       17 Oct 31 07:12 libavdevice.so -> libavdevice-57.so
    -rwxr-xr-x 1 root root  1638076 Oct 31 07:12 libavfilter-6.so
    lrwxrwxrwx 1 root root       16 Oct 31 07:12 libavfilter.so -> libavfilter-6.so
    -rwxr-xr-x 1 root root  1952368 Oct 31 07:12 libavformat-57.so
    lrwxrwxrwx 1 root root       17 Oct 31 07:12 libavformat.so -> libavformat-57.so
    -rwxr-xr-x 1 root root   443860 Oct 31 07:12 libavutil-55.so
    lrwxrwxrwx 1 root root       15 Oct 31 07:12 libavutil.so -> libavutil-55.so
    -rwxr-xr-x 1 root root    91532 Oct 31 07:12 libswresample-2.so
    lrwxrwxrwx 1 root root       18 Oct 31 07:12 libswresample.so -> libswresample-2.so
    -rwxr-xr-x 1 root root   406924 Oct 31 07:12 libswscale-4.so
    lrwxrwxrwx 1 root root       15 Oct 31 07:12 libswscale.so -> libswscale-4.so
    drwxr-xr-x 2 root root     4096 Oct 31 07:12 pkgconfig
    

    include 包含了头文件

    [root@iZ94g6hanmqZ arm]# cd include/
    [root@iZ94g6hanmqZ include]# ll
    total 28
    drwxr-xr-x 2 root root 4096 Oct 31 07:12 libavcodec
    drwxr-xr-x 2 root root 4096 Oct 31 07:12 libavdevice
    drwxr-xr-x 2 root root 4096 Oct 31 07:12 libavfilter
    drwxr-xr-x 2 root root 4096 Oct 31 07:12 libavformat
    drwxr-xr-x 2 root root 4096 Oct 31 07:12 libavutil
    drwxr-xr-x 2 root root 4096 Oct 31 07:12 libswresample
    drwxr-xr-x 2 root root 4096 Oct 31 07:12 libswscale
    

    到此为止,FFmpeg的动态库就编译完成了。

    Android项目使用FFmpeg

    首先android studio及其ndk需要下载配置好.红色都是需要安装的。

    3.png

    新建工程,注意勾选include C++ support(这里我们不再使用老的模式进行nkd开发,我们使用android studio新支持的模式)


    2.png

    看下项目工程目录:


    4.png
    将FFmpeg生成的include和so库放入到图中对应的位置中。
    下面看下gradle文件:
    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 26
        defaultConfig {
            applicationId "com.wangheart.ffmpegdemo"
            minSdkVersion 16
            targetSdkVersion 26
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
            externalNativeBuild {
                cmake {
                    cppFlags ""
                    abiFilters "armeabi"
                }
            }
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    
        externalNativeBuild {
            cmake {
                path "CMakeLists.txt"
            }
        }
    }
    
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'com.android.support:appcompat-v7:26.0.0-beta1'
        implementation 'com.android.support.constraint:constraint-layout:1.0.2'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'com.android.support.test:runner:0.5'
        androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.2.2'
    }
    

    主要增加abiFilters "armeabi",因为我们刚才编译的FFmpeg是arm平台。所以这里就选择arm平台。

    重点看到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)
    
    
    #set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI})
    # 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.
                 native-lib
    
                 # Sets the library as a shared library.
                 SHARED
    
                 # Provides a relative path to your source file(s).
                 src/main/cpp/native-lib.cpp )
    
    #引用的头文件
    include_directories(
                        src/main/cpp/include
                        )
    
    #添加库 动态库为SHARED  静态库就是STATIC
    add_library(avcodec SHARED IMPORTED)
    set_target_properties(avcodec
      PROPERTIES IMPORTED_LOCATION
      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavcodec-57.so)
    
    add_library(avdevice SHARED IMPORTED)
    set_target_properties(avdevice
      PROPERTIES IMPORTED_LOCATION
      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavdevice-57.so)
    
    add_library(avfilter SHARED IMPORTED)
    set_target_properties(avfilter
      PROPERTIES IMPORTED_LOCATION
      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavfilter-6.so)
    
    add_library(avformat SHARED IMPORTED)
    set_target_properties(avformat
      PROPERTIES IMPORTED_LOCATION
      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavformat-57.so)
    
    add_library(avutil SHARED IMPORTED)
    set_target_properties(avutil
      PROPERTIES IMPORTED_LOCATION
      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavutil-55.so)
    
    add_library(swresample SHARED IMPORTED)
    set_target_properties(swresample
      PROPERTIES IMPORTED_LOCATION
      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libswresample-2.so)
    
    add_library(swscale SHARED IMPORTED)
    set_target_properties(swscale
      PROPERTIES IMPORTED_LOCATION
      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${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 )
    
    # 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.
                           native-lib
    
                           avcodec
                           avdevice
                           avfilter
                           avformat
                           avutil
                           swresample
                           swscale
                           # Links the target library to the log library
                           # included in the NDK.
                           ${log-lib} )
    

    add_library表示添加库,可以添加动态库或静态库。如:
    add_library(avcodec SHARED IMPORTED)如果是静态库就是STATIC。下面就是设置动态库的路径:

    set_target_properties(avcodec
      PROPERTIES IMPORTED_LOCATION
     ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavcodec-57.so)
    

    然后添加链接库,在target_link_libraries中加入我们需要连接的库。到这里基本CMakeLists.txt就配置完成了。

    编辑native-lib.cpp文件

    这里就是我们需要开发的c++文件,需要做什么功能都可以在这里编辑,本节只是做个演示,我就只是获取FFmpeg的一些信息并返回。

    #include <jni.h>
    #include <string>
    
    //这里很重要,FFmpeg是C语言写的,如果不使用extern "C"则
    //会出现链接出错
    extern "C" {
    #include "libavcodec/avcodec.h"
    }
    
    extern "C"
    JNIEXPORT jstring
    
    JNICALL
    Java_com_wangheart_ffmpegdemo_MainActivity_stringFromJNI(
            JNIEnv *env,
            jobject /* this */thiz) {
        char info[10000] = {0};
    //    avcodec_version();
        sprintf(info, "%s\n", avcodec_configuration());
        return env->NewStringUTF(info);
    }
    
    MainActivity
    package com.wangheart.ffmpegdemo;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.widget.TextView;
    
    public class MainActivity extends AppCompatActivity {
    
        // Used to load the 'native-lib' library on application startup.
        static {
            System.loadLibrary("avutil-55");
            System.loadLibrary("swresample-2");
            System.loadLibrary("avcodec-57");
            System.loadLibrary("avformat-57");
            System.loadLibrary("swscale-4");
            System.loadLibrary("avfilter-6");
            System.loadLibrary("avdevice-57");
            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();
    }
    

    这里注意要把其他所有的so都要加入。

    8.png

    Github示例项目源码注意版本

    10.png

    常见问题

    jni编译链接失败
    5.png
    很多朋友在网上看到代码,在引入头文件时候直接引用比如:#include "libavcodec/avcodec.h"
    如果是C语言则没什么问题。如果是C++,则需要使用extern "C"
    extern "C" {
    #include "libavcodec/avcodec.h"
    }
    
    atof问题
    cannot locate symbol "atof" referenced by "libavformat-57.so"...
    

    这里有个解决办法就是在编译FFmpeg的时候使用android-16。也就是build_ffmpeg.sh的时候修改为:

    SYSROOT=$NDK/platforms/android-16/arch-arm/
    

    相关文章

      网友评论

          本文标题:Linux下ndk编译移植FFmpeg到Android平台

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