Ubuntu 14.04下编译FFmpeg及在Android Studio中调用

Ubuntu 14.04下编译FFmpeg及在Android Studio中调用

本文记录如何在Ubuntu 14.04下编译FFmpeg源码以及在Android Studio中调用FFmpeg动态库。

准备工作

  1. 下载FFmpeg源码:https://www.ffmpeg.org/download.html#releases
  2. 下载ndk:https://developer.android.com/ndk/downloads/older_releases
    注意:FFmpeg源码版本和ndk版本要选择合适,否则会出现很多兼容性问题导致编译不通过,这里我经过反复尝试选择的版本为:FFmpeg-3.3.8以及ndk-r14b:
    Ubuntu 14.04下编译FFmpeg及在Android Studio中调用_第1张图片
    Ubuntu 14.04下编译FFmpeg及在Android Studio中调用_第2张图片

将源码下载下来后,都解压至FFmpeg文件夹:
源码目录

编译前的配置

  1. 首先配置ndk环境,在.bashrc中添加:
    ndk环境
  2. 修改FFmpeg的configure:
    找到configure文件的如下内容:
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)'

编译

编写ffmpeg_android.sh 脚本,这个脚本用于生成FFmpeg头文件及类库文件。脚本参考这里。网上相关的配置脚本比较多,这个脚本可以比较方便生成各个cpu架构的库文件。
在FFmpeg目录下创建 ffmpeg_android.sh:

#!/bin/bash

# Don't forget to install yasm.

#set -e

# Set your own NDK here
NDK=/home/xiangyi/FFmpeg/android-ndk-r14b

ARM_PLATFORM=$NDK/platforms/android-23/arch-arm/
ARM_PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64

ARM64_PLATFORM=$NDK/platforms/android-23/arch-arm64/
ARM64_PREBUILT=$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64

X86_PLATFORM=$NDK/platforms/android-23/arch-x86/
X86_PREBUILT=$NDK/toolchains/x86-4.9/prebuilt/linux-x86_64

X86_64_PLATFORM=$NDK/platforms/android-23/arch-x86_64/
X86_64_PREBUILT=$NDK/toolchains/x86_64-4.9/prebuilt/linux-x86_64

MIPS_PLATFORM=$NDK/platforms/android-23/arch-mips/
MIPS_PREBUILT=$NDK/toolchains/mipsel-linux-android-4.9/prebuilt/linux-x86_64

BUILD_DIR=`pwd`/ffmpeg-android

FFMPEG_VERSION="3.3.8"

#if [ ! -e "ffmpeg-${FFMPEG_VERSION}.tar.bz2" ]; then
#    echo "Downloading ffmpeg-${FFMPEG_VERSION}.tar.bz2"
#    curl -LO http://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2
#else
#    echo "Using ffmpeg-${FFMPEG_VERSION}.tar.bz2"
#fi
#tar -xvf ffmpeg-${FFMPEG_VERSION}.tar.bz2


function build_one
{
if [ $ARCH == "arm" ]
then
    PLATFORM=$ARM_PLATFORM
    PREBUILT=$ARM_PREBUILT
    HOST=arm-linux-androideabi
elif [ $ARCH == "arm64" ]
then
    PLATFORM=$ARM64_PLATFORM
    PREBUILT=$ARM64_PREBUILT
    HOST=aarch64-linux-android
elif [ $ARCH == "mips" ]
then
    PLATFORM=$MIPS_PLATFORM
    PREBUILT=$MIPS_PREBUILT
    HOST=mipsel-linux-android
elif [ $ARCH == "x86_64" ]
then
    PLATFORM=$X86_64_PLATFORM
    PREBUILT=$X86_64_PREBUILT
    HOST=x86_64-linux-android
else
    PLATFORM=$X86_PLATFORM
    PREBUILT=$X86_PREBUILT
    HOST=i686-linux-android
fi

pushd ffmpeg-$FFMPEG_VERSION
./configure --target-os=linux \
    --incdir=$BUILD_DIR/include \
    --libdir=$BUILD_DIR/lib/$CPU \
    --enable-cross-compile \
    --arch=$ARCH \
    --cc=$PREBUILT/bin/$HOST-gcc \
    --cross-prefix=$PREBUILT/bin/$HOST- \
    --nm=$PREBUILT/bin/$HOST-nm \
    --sysroot=$PLATFORM \
    --extra-cflags="-I$BUILD_DIR/include -fPIC -DANDROID -Wfatal-errors -Wno-deprecated $OPTIMIZE_CFLAGS" \
    --enable-gpl \
    --enable-small \
    --extra-ldflags="-L$BUILD_DIR/lib/$CPU" \
    --enable-shared \
    --disable-doc \
    --disable-programs \
    --disable-stripping \
    --disable-symver \
    --disable-static \
    --pkg-config=/usr/local/bin/pkg-config \
    $ADDITIONAL_CONFIGURE_FLAG

make clean
make -j80
make install
$PREBUILT/bin/$HOST-ar d libavcodec/libavcodec.a inverse.o
$PREBUILT/bin/$HOST-ld -rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib  -soname libffmpeg.so -shared -nostdlib  -z noexecstack -Bsymbolic --whole-archive --no-undefined -o libffmpeg.so libavcodec/libavcodec.a libavformat/libavformat.a libavutil/libavutil.a libswscale/libswscale.a -lc -lm -lz -ldl -llog  --dynamic-linker=/system/bin/linker $PREBUILT/lib/gcc/$HOST/4.9.x/libgcc.a
popd
}

#arm v5te
#CPU=armv5te
#ARCH=arm
#OPTIMIZE_CFLAGS="-marm -march=$CPU"
#PREFIX=$BUILD_DIR/$CPU
#ADDITIONAL_CONFIGURE_FLAG=
#build_one

#arm v6
#CPU=armv6
#ARCH=arm
#OPTIMIZE_CFLAGS="-marm -march=$CPU"
#PREFIX=`pwd`/ffmpeg-android/$CPU 
#ADDITIONAL_CONFIGURE_FLAG=
#build_one

#arm v7vfpv3
#CPU=armv7-a
#ARCH=arm
#OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfpv3-d16 -marm -march=$CPU -D__thumb__ -mthumb"
#PREFIX=$BUILD_DIR/$CPU
#ADDITIONAL_CONFIGURE_FLAG=
#build_one

#arm v7vfp
#CPU=armv7-a
#ARCH=arm
#OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "
#PREFIX=`pwd`/ffmpeg-android/$CPU-vfp
#ADDITIONAL_CONFIGURE_FLAG=
#build_one

#arm v7n
#CPU=armv7-a
#ARCH=arm
#OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=neon -marm -march=$CPU -mtune=cortex-a8"
#PREFIX=$BUILD_DIR/$CPU
#ADDITIONAL_CONFIGURE_FLAG=
#build_one

#arm v6+vfp
#CPU=armv6
#ARCH=arm
#OPTIMIZE_CFLAGS="-DCMP_HAVE_VFP -mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU"
#PREFIX=`pwd`/ffmpeg-android/${CPU}_vfp
#ADDITIONAL_CONFIGURE_FLAG=
#build_one

#arm64-v8a
#CPU=arm64-v8a
#ARCH=arm64
#OPTIMIZE_CFLAGS=
#PREFIX=$BUILD_DIR/$CPU
#ADDITIONAL_CONFIGURE_FLAG=
#build_one

#x86_64
#CPU=x86_64
#ARCH=x86_64
#OPTIMIZE_CFLAGS="-fomit-frame-pointer"
#PREFIX=$BUILD_DIR/$CPU
#ADDITIONAL_CONFIGURE_FLAG=
#build_one

#x86
#CPU=i686
#ARCH=i686
#OPTIMIZE_CFLAGS="-fomit-frame-pointer"
#PREFIX=$BUILD_DIR/$CPU
#ADDITIONAL_CONFIGURE_FLAG=
#build_one

上面代码中,打开arm64-v8a、x86等下的注释即可编译不同cpu架构的库文件了。执行./ffmpeg_android.sh,成功执行后会生成ffmpeg-android 目录:
Ubuntu 14.04下编译FFmpeg及在Android Studio中调用_第3张图片
其中include目录存放的是头文件:
Ubuntu 14.04下编译FFmpeg及在Android Studio中调用_第4张图片
lib目录以cpu架构类型存放库文件:
Ubuntu 14.04下编译FFmpeg及在Android Studio中调用_第5张图片

移植到Android工程

  1. 新建一个HelloFFmpeg工程,注意勾选“Include C++ support”。将编译生成的include和lib目录拷贝到app目录的libs目录下:
    Ubuntu 14.04下编译FFmpeg及在Android Studio中调用_第6张图片
    其中arm64-v8a目录下保留以下文件:
    Ubuntu 14.04下编译FFmpeg及在Android Studio中调用_第7张图片
  2. 修改build.grald:
    在defaultconfig中添加:
    Ubuntu 14.04下编译FFmpeg及在Android Studio中调用_第8张图片
  3. native-lib.cpp:
#include 

#include 
#include 


extern "C" {
//编码
#include "libavcodec/avcodec.h"
//封装格式处理
#include "libavformat/avformat.h"
//像素处理
#include "libswscale/swscale.h"
}


#define FFLOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"ffmpeg",FORMAT,##__VA_ARGS__);
#define FFLOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"ffmpeg",FORMAT,##__VA_ARGS__);

extern "C"
JNIEXPORT jstring

JNICALL
Java_com_cdrc_helloffmpeg_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {

        #if defined(__arm__)
            #if defined(__ARM_ARCH_7A__)
                #if defined(__ARM_NEON__)
                  #define ABI "armeabi-v7a/NEON"
                #else
                  #define ABI "armeabi-v7a"
                #endif
             #else
               #define ABI "armeabi"
             #endif
        #elif defined(__i386__)
            #define ABI "x86"
        #elif defined(__mips__)
            #define ABI "mips"
        #elif defined(__aarch64__)
            #define ABI "aarch64"
        #else
            #define ABI "unknown"
        #endif

        return env->NewStringUTF("Hello from JNI !  Compiled with ABI " ABI ".");
}

extern "C"
JNIEXPORT jstring

JNICALL
Java_com_cdrc_helloffmpeg_MainActivity_InitFFMPEG(
        JNIEnv *env,
        jobject /* this */) {
        av_register_all();

    std::string hello = "av_register_all OK";
    return env->NewStringUTF(hello.c_str());

}
  1. 在src/main/cpp 目录新建simplest_ffmpeg_helloworld.c:
//
// Created by CDRC on 2018/8/21.
//

#include 


#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"

//Log
#ifdef ANDROID
#include 
#include 
#define LOGE(format, ...)  __android_log_print(ANDROID_LOG_ERROR, "(>_<)", format, ##__VA_ARGS__)
#else
#define LOGE(format, ...)  printf("(>_<) " format "\n", ##__VA_ARGS__)
#endif

//FIX
struct URLProtocol;
/**
 * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.urlprotocolinfo()
 * Protocol Support Information
 */
JNIEXPORT jstring Java_com_cdrc_helloffmpeg_MainActivity_urlprotocolinfo(JNIEnv *env, jobject obj){

    char info[40000]={0};
    av_register_all();

    struct URLProtocol *pup = NULL;
    //Input
    struct URLProtocol **p_temp = &pup;
    avio_enum_protocols((void **)p_temp, 0);
    while ((*p_temp) != NULL){
        sprintf(info, "%s[In ][%10s]\n", info, avio_enum_protocols((void **)p_temp, 0));
    }
    pup = NULL;
    //Output
    avio_enum_protocols((void **)p_temp, 1);
    while ((*p_temp) != NULL){
        sprintf(info, "%s[Out][%10s]\n", info, avio_enum_protocols((void **)p_temp, 1));
    }

    //LOGE("%s", info);
    return (*env)->NewStringUTF(env, info);
}

/**
 * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.avformatinfo()
 * AVFormat Support Information
 */
JNIEXPORT jstring Java_com_cdrc_helloffmpeg_MainActivity_avformatinfo(JNIEnv *env, jobject obj){

    char info[40000] = { 0 };

    av_register_all();

    AVInputFormat *if_temp = av_iformat_next(NULL);
    AVOutputFormat *of_temp = av_oformat_next(NULL);
    //Input
    while(if_temp!=NULL){
        sprintf(info, "%s[In ][%10s]\n", info, if_temp->name);
        if_temp=if_temp->next;
    }
    //Output
    while (of_temp != NULL){
        sprintf(info, "%s[Out][%10s]\n", info, of_temp->name);
        of_temp = of_temp->next;
    }
    //LOGE("%s", info);
    return (*env)->NewStringUTF(env, info);
}
  1. MainActivity.java:
package com.cdrc.helloffmpeg;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
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("avcodec-57");
        System.loadLibrary("avformat-57");
        System.loadLibrary("avdevice-57");
        System.loadLibrary("swresample-2");
        System.loadLibrary("swscale-4");
        System.loadLibrary("postproc-54");
        System.loadLibrary("avfilter-6");
        System.loadLibrary("native-lib");
    }

    TextView tv;

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

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

    public void InitFFmpeg(View v){
        tv.setText(InitFFMPEG());
    }

    public void protocol(View v){
        tv.setText(urlprotocolinfo());
    }

    public void format(View v){
        tv.setText(avformatinfo());
    }

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

stringFromJNI和InitFFMPEG是在native-lib.cpp中实现,urlprotocolinfo和avformatinfo是在simplest_ffmpeg_helloworld.c中实现。
6. 修改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)

# 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
             src/main/cpp/simplest_ffmpeg_helloworld.c
             )

# 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.


set(distribution_DIR ../../../../libs)
add_library( avcodec-57
             SHARED
             IMPORTED)
set_target_properties( avcodec-57
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/arm64-v8a/libavcodec-57.so)

add_library( avdevice-57
            SHARED
            IMPORTED)
set_target_properties( avdevice-57
                      PROPERTIES IMPORTED_LOCATION
                      ${distribution_DIR}/arm64-v8a/libavdevice-57.so)

add_library( avfilter-6
             SHARED
             IMPORTED)
set_target_properties( avfilter-6
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/arm64-v8a/libavfilter-6.so)

add_library( avformat-57
             SHARED
             IMPORTED)
set_target_properties( avformat-57
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/arm64-v8a/libavformat-57.so)

add_library( avutil-55
             SHARED
             IMPORTED)
set_target_properties( avutil-55
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/arm64-v8a/libavutil-55.so)

add_library( postproc-54
             SHARED
             IMPORTED)
set_target_properties( postproc-54
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/arm64-v8a/libpostproc-54.so)

add_library( swresample-2
             SHARED
             IMPORTED)
set_target_properties( swresample-2
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/arm64-v8a/libswresample-2.so)

add_library( swscale-4
             SHARED
             IMPORTED)
set_target_properties( swscale-4
                       PROPERTIES IMPORTED_LOCATION
                       ${distribution_DIR}/arm64-v8a/libswscale-4.so)
include_directories(libs/include)

target_link_libraries( # Specifies the target library.
                       native-lib
                       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.
                       ${log-lib} )

演示结果:
Ubuntu 14.04下编译FFmpeg及在Android Studio中调用_第9张图片

你可能感兴趣的:(FFmpeg)