Android音视频-FFmpeg编译与测试调用

FFmpeg的名声很大,是很多应用都使用的一个开源库。我们可以下载编译好的FFmpeg的so文件来使用,但是我们还是从0开始学会自己编译FFmpeg来提供给Android使用

编译FFmpeg

编译FFmpeg大体的步骤就是到官网下载FFmpeg源码,然后编译为so库

编译环境

  • MacOS High Sierra 10.13.3
  • NDK 15(AndroidStudio里面更新即可)
  • FFmpeg 3.3.6下载

开始编译

修改configure

修改ffmpeg-3.3.6/configure文件如下几行配置,因为编译出来的动态库文件名的版本号在.so之后(例如“libavcodec.so.5.100.1”),而android平台不能识别这样文件名,所以需要修改这种文件名

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)'

编写编译脚步

编译脚步如下:

#!/bin/bash
# NDK的路径,根据自己的安装位置进行设置
#NDK=/Users/yehu/Library/Android/sdk/ndk-bundle
NDK=/Users/yehu/Downloads/android-ndk-r15c
SYSROOT=$NDK/platforms/android-21/arch-arm
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
#这句很重要,不然会报错 unable to create temporary file in ...
export TMPDIR="/Users/yehu/Study/Android/MultiMedia/FFmpeg/ffmpeg-3.3.6/ffmpegtemp"
function build_one
{
bash ./configure \
    --prefix=$PREFIX \
    --enable-shared \
    --enable-avdevice \
    --enable-gpl \
    --enable-nonfree \
    --enable-postproc \
    --disable-static \
    --disable-doc \
    --disable-ffmpeg \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-ffserver \
    --disable-doc \
    --disable-symver \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --target-os=linux \
    --arch=arm \
    --enable-cross-compile \
    --sysroot=$SYSROOT \
    --extra-cflags="-Os -fpic $ADDI_CFLAGS" \
    --extra-ldflags="$ADDI_LDFLAGS" \
    $ADDITIONAL_CONFIGURE_FLAG
make clean
make
make install
}
CPU=arm
PREFIX=$(pwd)/android/$CPU 
ADDI_CFLAGS="-marm"
build_one

编译脚本

  • 设置脚步权限:chimed +x build_android.sh
  • 执行脚步:./build_android.sh

如果成功就可以在ffmepg-3.3.6/android下面看到编译好的so文件了

我刚开始编译的时候使用的ndk是最新的16,FFmpeg是最新的3.4.2,但是不是出现Ndk的问题里面的文件头部缺失就是出现FFmpeg的问题,里面的c代码编译出问题,完全整不明白,于是把NDK和FFmpeg都降低了一个版本来编译,然后就编译通过了。

  • 首先一定要把上面的编译文件配置正确了,主要是找的到配置路径下面的文件
  • 对于编译错误的追踪,出现头文件缺失的时候就到NDK下面去找,是不是真的缺失
  • 对于FFmpeg的代码编译出现问题就找到相应的代码看一看

最后追踪到自己编译的问题所在个个解决即可
当然我实在是拿最新的编译不出来就整低一个版本的了。

上述编译过程参考链接查看

测试FFmpeg so库信息

我们这里仿照之前的Ndk的文章以两种方式ndk-build和Cmake来集成使用so库。同时把之前学习的NDK的配置复习一下。

ndk-build集成FFmpeg so库

由于第一次使用最新的ndk和FFmpeg我在Mac平台下编译FFmepg失败了,所以我下面的ndk-build方式引入so用的都是来自雷神的博客里面的。

引入编译好的so库进入工程

so库下载地址查看

倒入so文件

引入到我的项目工程结构如下图:


Android音视频-FFmpeg编译与测试调用_第1张图片

我直接把jni目录copy过来了,下面做一些小的修改:

建立自己的native-lib.c文件用来调用Ffmepg的so代码
修改Android.mk文件替换引入我自己的native-lib.c文件

编译引入FFmepg和自己的so库文件

在jni目录执行ndk-build命令:


Android音视频-FFmpeg编译与测试调用_第2张图片

这个ndk-build命令是我之前的文章关于ndk的使用配置的 查看
成功编译完成,我们可以看到在libs下面会得到so:

Android音视频-FFmpeg编译与测试调用_第3张图片

配置gradle可以使用编译好的so

在gradle文件里面进行简单的配置,把jniLibs指向libs:

android {
    defaultConfig {
        //...
        sourceSets.main.jniLibs.srcDirs = ['src/main/libs','libs']
        sourceSets.main.jni.srcDirs = []
    }
//...
}

Java层调用native-lib实现方法

现在就只要倒入我们的native-lib库,然后调用它实现的方法就好了:

public class BasicInfoActivity extends AppCompatActivity {
    private static final String TAG = "BasicInfoActivity";
    private TextView mContentTV;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_basic_info);
        mContentTV = findViewById(R.id.tv_content);
    }

    public void onClickProtocol(View view) {
        mContentTV.setText(urlprotocolinfo());
    }

    public void onClickFormat(View view) {
        mContentTV.setText(avformatinfo());
    }

    public void onClickCodec(View view) {
        mContentTV.setText(avcodecinfo());
    }

    public void onClickFilter(View view) {
        mContentTV.setText(avfilterinfo());
    }

    public void onClickConfigure(View view) {
        mContentTV.setText(configurationinfo());
    }

    public native String urlprotocolinfo();

    public native String avformatinfo();

    public native String avcodecinfo();

    public native String avfilterinfo();

    public native String configurationinfo();

    static {
//        System.loadLibrary("avutil-54");
//        System.loadLibrary("swresample-1");
//        System.loadLibrary("avcodec-56");
//        System.loadLibrary("avformat-56");
//        System.loadLibrary("swscale-3");
//        System.loadLibrary("postproc-53");
//        System.loadLibrary("avfilter-5");
//        System.loadLibrary("avdevice-56");
//        System.loadLibrary("sffhelloworld");
        System.loadLibrary("native-lib");
    }
}

运行程序可以看到FFmepg的基本的信息啦。
参考代码地址:查看

Cmake引入FFmpeg so库

这里我们编译成功的FFmpeg的版本是3.3.6

引入编译好的so库和头文件进入工程

  • 引入so文件


    Android音视频-FFmpeg编译与测试调用_第4张图片

  • 引入头文件


    Android音视频-FFmpeg编译与测试调用_第5张图片

配置CmakeLists.txt

cmake_minimum_required(VERSION 3.4.1)

set(jnilibs "${CMAKE_SOURCE_DIR}/src/main/jniLibs")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${jnilibs}/${ANDROID_ABI})


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.c)
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 )



#第三方so存放的根目录
SET(
    third_path
    ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}
    )
#引入源文件.h目录
include_directories( ${CMAKE_SOURCE_DIR}/src/main/cpp/include)


#设置libavcodec.so
add_library(libavcodec-57
              SHARED
              IMPORTED
)

set_target_properties(libavcodec-57
                      PROPERTIES IMPORTED_LOCATION
                      ${third_path}/libavcodec-57.so
)
#设置libavdevice.so
add_library(libavdevice-57
              SHARED
              IMPORTED
)

set_target_properties(libavdevice-57
                      PROPERTIES IMPORTED_LOCATION
                      ${third_path}/libavdevice-57.so
)

#设置libavfilter.so
 add_library(libavfilter-6
               SHARED
               IMPORTED
 )

 set_target_properties(libavfilter-6
                       PROPERTIES IMPORTED_LOCATION
                       ${third_path}/libavfilter-6.so
 )

#设置libavformat.so
add_library(libavformat-57
              SHARED
              IMPORTED
)

set_target_properties(libavformat-57
                      PROPERTIES IMPORTED_LOCATION
                      ${third_path}/libavformat-57.so
)

#设置libavutil.so
add_library(libavutil-55
              SHARED
              IMPORTED
)

set_target_properties(libavutil-55
                      PROPERTIES IMPORTED_LOCATION
                      ${third_path}/libavutil-55.so
)

#设置libpostproc.so
add_library(libpostproc-54
              SHARED
              IMPORTED
)

set_target_properties(libpostproc-54
                      PROPERTIES IMPORTED_LOCATION
                      ${third_path}/libpostproc-54.so
)

#设置libswresample.so
 add_library(libswresample-2
               SHARED
               IMPORTED
 )

 set_target_properties(libswresample-2
                       PROPERTIES IMPORTED_LOCATION
                       ${third_path}/libswresample-2.so
 )

 #设置libswscale.so
 add_library(libswscale-4
               SHARED
               IMPORTED
 )

 set_target_properties(libswscale-4
                       PROPERTIES IMPORTED_LOCATION
                       ${third_path}/libswscale-4.so
 )

target_link_libraries(
                       native-lib
                       libavcodec-57
                       libavdevice-57
                       libavfilter-6
                       libavformat-57
                       libavutil-55
                       libpostproc-54
                       libswresample-2
                       libswscale-4
                       ${log-lib} )

在C层调用和在Java层调用

在C层和Java层的调用和上面一样,我们只是换了一下底层的库和引入的方式而已。
要注意的是我在项目最后运行的时候遇到一个这样的错误:




Google找到解决方案:

android {
    //...
    packagingOptions {
        pickFirst 'lib/armeabi-v7a/libnative-lib.so'
    }
}

配置这个信息即可:
本示例Module地址:查看

参考链接:
雷神blog
Mac平台编译FFmpeg参考

你可能感兴趣的:(Android,多媒体)