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

简介

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

推荐阅读更多精彩内容