macOS上编译android的ffmpeg及ffmpeg.c

 1 前言

    前段时间介绍过使用xcode和qt creator编译调试ffmepg.c,运行平台是在macOS上,本文拟介绍下android平台如何用NDK编译链编译ffmepg库并使用。

macOS上使用qt creator编译调试ffmpeg.c

macOS上将ffmpeg.c编译成Framework

    大体思路:

  • 其一,分别介绍使用GCC和CLang编译器来编译ffmpeg库的方法;
  • 其二,介绍如何将ffmpeg的多个.a库打包成1个so库之法;
  • 其三,使用android studio新建一个native c++ Library工程,并将ffmepg库引入到工程使用;

2 下载FFmpeg源码

    首先从git仓库将ffmpeg代码下载到本地:

git clone https://github.com/FFmpeg/FFmpeg.git && git checkout release/6.1

3 编译FFmpeg

3.1 GCC编译

    编译环境:

  • ffmpeg release/6.1分支
  • android ndk 17.2.4988734版本,可借助android studio工具下载;

    通过给编译脚本传参(aarch64/x86_64)支持arm64和x86_64架构:  

#! /usr/bin/env bashset -eARG_COUNT=$#
#支持架构aarch64 x86_64
ARCH_NAME=$1
if [[ ${ARG_COUNT} -lt 1 ]]||[[ ${ARCH_NAME} != "aarch64" && ${ARCH_NAME} != "x86_64" ]]; thenecho "Usage:./build_ffmpeg_for_android.sh aarch64/x86_64"exit -1
fiNDK_ROOT=/Users/mingo/Library/Android/sdk/ndk/17.2.4988734
SYSROOT=${NDK_ROOT}/sysroot
echo "ARCH_NAME=${ARCH_NAME}"
TOOLCHAIN_ARCH=${ARCH_NAME}-linux-android
FLATFORM=${NDK_ROOT}/platforms/android-28/arch-arm64
SYSROOT_INCLUDE_PATH="-I${SYSROOT}/usr/include/${ARCH_NAME}-linux-android -isysroot ${SYSROOT}"
COMPILER_PREFIX=${ARCH_NAME}-linux-android
CURRENT_DIR=`pwd`
BUILD_OUTPUT_DIR="${CURRENT_DIR}/android/arm64"
if [ ${ARCH_NAME} == "x86_64" ]; thenTOOLCHAIN_ARCH=${ARCH_NAME}FLATFORM=${NDK_ROOT}/platforms/android-28/arch-x86_64BUILD_OUTPUT_DIR="${CURRENT_DIR}/android/x86_64"
fi
if [ ! -d ${BUILD_OUTPUT_DIR} ]; thenmkdir -p ${BUILD_OUTPUT_DIR}
fi
echo "TOOLCHAIN_RCH=${TOOLCHAIN_ARCH}"
PREBUILT=${NDK_ROOT}/toolchains/${TOOLCHAIN_ARCH}-4.9/prebuilt/darwin-x86_64/binFF_CFLAGS="-O3 -Wall -pipe -std=c11 -ffast-math -fstrict-aliasing -Werror=strict-aliasing -Wno-psabi -Wa,--noexecstack -DANDROID -DDEBUG"
FF_CXXFLAGS="-D__thumb__ -fexceptions -frtti"function build_for() {echo "start to configure ffmpeg"./configure --prefix=${BUILD_OUTPUT_DIR} \--sysroot=${FLATFORM} \--cross-prefix=${PREBUILT}/${COMPILER_PREFIX}- \--cc=${PREBUILT}/${COMPILER_PREFIX}-gcc \--enable-cross-compile --arch=${ARCH_NAME} --target-os=android \--enable-shared \--enable-pic \--disable-symver \--disable-asm \--enable-inline-asm \--disable-optimizations \--enable-debug \--disable-small \--disable-ffmpeg \--disable-ffprobe \--disable-ffplay \--disable-doc \--extra-cflags="${SYSROOT_INCLUDE_PATH} ${FFLAGS}" \--extra-cxxflags="${FF_CXXFLAGS}" \--extra-ldflags="-L${FLATFORM}/usr/lib"make cleanmake -j9make install
}build_forif [ $? -eq 0 ]; thenecho "configure ffmpeg succ"
elseecho "configure ffmpeg fail"
fi

     起初所用NDK版本是16.1.4479499版本,遇到编译问题:

    通过将NDK版本升级到17.2.4988734解决:

     NDK的android api选择android-28:

xxxxx@localhost:~/Library/Android/sdk/ndk/17.2.4988734/platforms$tree -L 1
.
├── NOTICE
├── android-14
├── android-15
├── android-16
├── android-17
├── android-18
├── android-19
├── android-21
├── android-22
├── android-23
├── android-24
├── android-26
├── android-27
├── android-28
└── repo.prop14 directories, 2 files

    将shell脚本改成如下即可: 

NDK_ROOT=/Users/xxx/Library/Android/sdk/ndk/17.2.4988734FLATFORM=${NDK_ROOT}/platforms/android-28/arch-arm64--extra-ldflags="-L${FLATFORM}/usr/lib"

     然后执行编译安装:

sh build_for_android.sh aarch64(或x86_64)
  • arm64平台编译后输出目录在ffmpeg的根目录下android/arm64目录;
  • x86_64平台编译后输出目录在ffmpeg的根目录下的android/x86_64目录; 

    执行脚本命令之后,可编译成功: 

3.2 使用ffmpeg库

    CMakeLists脚本如下:

# 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.22.1)# Declares and names the project.project("ndkffmpeg")# 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.ndkffmpeg# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).native-lib.cpp)# 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(FFMPEG_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs)
set(FFMPEG_INCLUDE_DIR ${FFMPEG_ROOT_DIR}/include)
set(FFMPEG_LIB_DIR ${FFMPEG_ROOT_DIR}/${ANDROID_ABI})#导入ffmpeg相关依赖库
list(APPEND DEPENDCY_LIB_LIST avutil avformat avcodec avfilter avdevice swscale swresample)
list(APPEND DEPENDCY_LIB_LIST ffmpeg)
foreach(libname IN LISTS DEPENDCY_LIB_LIST)add_library(${libname} SHARED IMPORTED)set_target_properties( ${libname} PROPERTIES IMPORTED_LOCATION  ${FFMPEG_LIB_DIR}/lib${libname}.so)
endforeach()include_directories(${FFMPEG_INCLUDE_DIR} ${FFMPEG_ROOT_DIR}/../)
target_link_directories(ndkffmpeg PRIVATE ${FFMPEG_LIB_DIR})# 设置库文件的输出路径
set_target_properties(ndkffmpeg PROPERTIESLIBRARY_OUTPUT_DIRECTORY ${FFMPEG_LIB_DIR}ARCHIVE_OUTPUT_DIRECTORY ${FFMPEG_LIB_DIR})
target_link_libraries( # Specifies the target library.ndkffmpegavutilavformatavcodecavfilteravdeviceswresampleswscale# Links the target library to the log library# included in the NDK.${log-lib})

    在编写完成CMakeLists脚本后,并且在工程目录下引入so和include文件:

    在native-lib.cpp里写一个简单的程序看看效果: 

#include <jni.h>
#include <string>extern "C" {
#include "libavformat/avformat.h"
}static int decode_interrupt_cb(void *ctx) {return 0;
}static void test_ffmpeg_func() {AVFormatContext* ifmt = NULL;const char* filename = "rtmp://10.0.2.2/live/8";AVDictionary *d = NULL;//av_dict_set(&d, "timeout", NULL, 0);//av_dict_set(&d, "fflags", "nobuffer", 0);int ret = avformat_open_input(&ifmt, filename, NULL, &d);ret = avformat_find_stream_info(ifmt, NULL);AVPacket pkt;av_init_packet(&pkt);while (1) {int ret = av_read_frame(ifmt, &pkt);if (ret < 0) {av_log(NULL, AV_LOG_INFO, "error\n");}if (pkt.stream_index == AVMEDIA_TYPE_VIDEO && pkt.flags & AV_PKT_FLAG_KEY) {//av_log(NULL, AV_LOG_INFO, "keyframe\n");}}
}extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ndkffmpeg_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {av_log_set_level(AV_LOG_DEBUG);test_ffmpeg_func();std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}

    调试运行效果; 

    可以看到,app已经成功将ffmpeg各库加载起来,并可使用了。 

 3.3 Clang编译

    以上是GCC编译器编译ffmpeg,此处再介绍使用clang编译器来编译ffmpeg,编译环境:

  • ffmpeg release/6.1版本;
  • android ndk 21.3.6528147版本;

    支持arm64和x86_64架构: 

#! /usr/bin/env bashset -eARG_COUNT=$#
#支持架构aarch64 x86_64
ARCH_NAME=$1
if [[ ${ARG_COUNT} -lt 1 ]]||[[ ${ARCH_NAME} != "aarch64" && ${ARCH_NAME} != "x86_64" ]]; thenecho "Usage:./build_ffmpeg_for_android.sh aarch64/x86_64"exit -1
fiNDK_ROOT=/Users/mingo/Library/Android/sdk/ndk/21.3.6528147
ANDROID_VER=28
FLATFORM=${NDK_ROOT}/platforms/android-${ANDROID_VER}/arch-arm64
ENABLE_OPT="--enable-asm"
CURRENT_DIR=`pwd`
OUTPUT_DIR="${CURRENT_DIR}/android_clang/arm64"
if [ ${ARCH_NAME} == "x86_64" ]; thenFLATFORM=${NDK_ROOT}/platforms/android-${ANDROID_VER}/arch-x86_64ENABLE_OPT="--disable-asm"OUTPUT_DIR="${CURRENT_DIR}/android_clang/x86_64"
fi
if [ ! -d ${OUTPUT_DIR} ]; thenmkdir -p ${OUTPUT_DIR}
fi
PREBUILT=${NDK_ROOT}/toolchains/llvm/prebuilt/darwin-x86_64/bin
COMPILER_PREFIX=${ARCH_NAME}-linux-android
SYSROOT=${PREBUILT}/../sysrootFF_CFLAGS="-O3 -Wall -pipe -std=c11 -ffast-math -fstrict-aliasing -Werror=strict-aliasing -Wno-psabi -Wa,--noexecstack -DANDROID -DDEBUG"
FF_CXXFLAGS="-D__thumb__ -fexceptions -frtti"function build_for() {echo "start to configure ffmpeg"./configure --prefix=${OUTPUT_DIR} \--sysroot=${SYSROOT} \--cross-prefix=${PREBUILT}/${COMPILER_PREFIX}- \--cc=${PREBUILT}/${COMPILER_PREFIX}${ANDROID_VER}-clang \--cxx=${PREBUILT}/${COMPILER_PREFIX}${ANDROID_VER}-clang++ \--enable-cross-compile --arch=${ARCH_NAME} --target-os=android \--enable-shared \--enable-pic \--disable-symver \${ENABLE_OPT} \--enable-inline-asm \--disable-optimizations \--enable-debug \--disable-small \--disable-ffmpeg \--disable-ffprobe \--disable-ffplay \--disable-doc \--extra-cflags="${FFLAGS}"\--extra-cxxflags="${FF_CXXFLAGS}" \--extra-ldflags="-L${FLATFORM}/usr/lib"make cleanmake -j9make install
}build_forif [ $? -eq 0 ]; thenecho "configure ffmpeg succ"
elseecho "configure ffmpeg fail"
fi

    然后,通过在命令行执行如下命令编译,编译arm64架构的: 

sh build_ffmpeg_for_android_with_clang.sh aarch64

    编译x86_64架构的: 

sh build_ffmpeg_for_android_with_clang.sh x86_64

     编译的输出目录在ffmpeg的目录下:

  • arm64输出在ffmpeg根目录下android_clang/arm64目录;
  • x86_64输出在ffmpeg的根目录下的android_clang/x86_64下;

    提示:clang编译仅做介绍,后续ffmpeg.c的编译仍将使用NDK 17.2.4988734版本和GCC编译器。

3.4 多个.a库打包成1个so

  • 将libavutil libavformat libavcodec libavfilter libavdevice libswsample libswscale几个.a库打包成一个so库;
  • *.a  =>  libffmpeg.so

    主要思路:

  • configure的时候配置只编译生成ffmpeg的static库,而放弃编译shared库;
  • 用交叉编译链中的链接器将ffmpeg的相关.a库链接成1个so库;

    贴出编译&打包的shell脚本:

#! /usr/bin/env bashset -eARG_COUNT=$#
#支持架构aarch64 x86_64
ARCH_NAME=$1
if [[ ${ARG_COUNT} -lt 1 ]]||[[ ${ARCH_NAME} != "aarch64" && ${ARCH_NAME} != "x86_64" ]]; thenecho "Usage:./build_ffmpeg_for_android.sh aarch64/x86_64"exit -1
fiNDK_ROOT=/Users/mingo/Library/Android/sdk/ndk/17.2.4988734
SYSROOT=${NDK_ROOT}/sysroot
echo "ARCH_NAME=${ARCH_NAME}"
TOOLCHAIN_ARCH=${ARCH_NAME}-linux-android
FLATFORM=${NDK_ROOT}/platforms/android-28/arch-arm64
echo "FLATFORM=${FLATFORM}"
SYSROOT_INCLUDE_PATH="-I${SYSROOT}/usr/include/${ARCH_NAME}-linux-android -isysroot ${SYSROOT}"
COMPILER_PREFIX=${ARCH_NAME}-linux-android
CURRENT_DIR=`pwd`
BUILD_OUTPUT_DIR="${CURRENT_DIR}/android/arm64"
if [ ${ARCH_NAME} == "x86_64" ]; thenTOOLCHAIN_ARCH=${ARCH_NAME}FLATFORM=${NDK_ROOT}/platforms/android-28/arch-x86_64BUILD_OUTPUT_DIR="${CURRENT_DIR}/android/x86_64"
fi
if [ ! -d ${BUILD_OUTPUT_DIR} ]; thenmkdir -p ${BUILD_OUTPUT_DIR}
fi
echo "TOOLCHAIN_RCH=${TOOLCHAIN_ARCH}"
PREBUILT=${NDK_ROOT}/toolchains/${TOOLCHAIN_ARCH}-4.9/prebuilt/darwin-x86_64/binFF_CFLAGS="-O3 -Wall -pipe -fpic -std=c11 -ffast-math -fstrict-aliasing -Werror=strict-aliasing -Wno-psabi -Wa,--noexecstack -DANDROID -DDEBUG"
FF_CXXFLAGS="-D__thumb__ -fexceptions -frtti"
FF_LDFLAGS="-lc -ldl -lm -lz -llog -lgcc"function build_for() {echo "start to configure ffmpeg"./configure --prefix=${BUILD_OUTPUT_DIR} \--sysroot=${FLATFORM} \--cross-prefix=${PREBUILT}/${COMPILER_PREFIX}- \--cc=${PREBUILT}/${COMPILER_PREFIX}-gcc \--enable-cross-compile --arch=${ARCH_NAME} --target-os=android \--disable-shared \--enable-static \--disable-optimizations \--enable-debug \--disable-small \--disable-ffmpeg \--disable-ffprobe \--disable-ffplay \--disable-doc \--extra-cflags="${SYSROOT_INCLUDE_PATH} ${FFLAGS}" \--extra-cxxflags="${FF_CXXFLAGS}" \--extra-ldflags="${FF_LDFLAGS} -L${FLATFORM}/usr/lib"make cleanmake -j9make install
}build_forif [ $? -eq 0 ]; thenecho "configure ffmpeg succ"
elseecho "configure ffmpeg fail"
fiGCC_PREFIX=${PREBUILT}/../lib/gcc/${COMPILER_PREFIX}/4.9.x
RPATH=${FLATFORM}/usr/lib
if [ ${ARCH_NAME} == "x86_64" ]; thenRPATH=${FLATFORM}/usr/lib64
fi
echo "RPATH=${RPATH}"
package_ffmpeg_libs() {${PREBUILT}/${COMPILER_PREFIX}-ld -L${BUILD_OUTPUT_DIR}/lib -L${GCC_PREFIX} -L${RPATH} \-rpath-link=${RPATH} -L${RPATH} -soname libffmpeg.so \-shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o ${BUILD_OUTPUT_DIR}/lib/libffmpeg.so \-lavformat -lavcodec -lavfilter -lavdevice -lswresample -lswscale -lavutil -lgcc \-lcamera2ndk -lmediandk -lnativewindow \-lc -ldl -lm -lz -llog \--dynamic-linker=/system/bin/linker# 设置动态链接器,不同平台的不同,android 使用的是/system/bin/linker
}package_ffmpeg_libsif [ $? -eq 0 ]; thenecho "package ffmpeg succ"
elseecho "package ffmpeg fail"
fi

    遇到1个问题:

/Users/xxx/Applications/workspace/FFmpeg/android/arm64/lib/libswscale.a(half2float.o): In function `ff_init_half2float_tables':
/Users/xxx/Applications/workspace/FFmpeg/./libavutil/half2float.c:40: multiple definition of `ff_init_half2float_tables'
/Users/xxx/Applications/workspace/FFmpeg/android/arm64/lib/libavcodec.a(half2float.o):/Users/mingo/Applications/workspace/FFmpeg/./libavutil/half2float.c:40: first defined here

    问题原因:

  • 上述问题的原因是libavcodec.a库和libswscale.a库均打包了half2float.o文件;
  • 在将上述库最终链接打包成同一个so的时候就会出现上述重复定义问题;

    解决办法:

  • 打开libswscale/Makefile文件,将half2float.o文件去掉,libswscale不打包该文件,只让libavcodec打包该文件; 

    打包后的libffmpeg.so输出在以下目录,成功将ffmpeg相关.a库打包成libffmpeg.so库: 

mingo@localhost:~/Applications/workspace/FFmpeg/android$tree -L 3
.
└── arm64├── include│   ├── libavcodec│   ├── libavdevice│   ├── libavfilter│   ├── libavformat│   ├── libavutil│   ├── libswresample│   └── libswscale├── lib│   ├── libavcodec.a│   ├── libavdevice.a│   ├── libavfilter.a│   ├── libavformat.a│   ├── libavutil.a│   ├── libffmpeg.so│   ├── libswresample.a│   ├── libswscale.a│   └── pkgconfig└── share└── ffmpeg14 directories, 8 files

 4 使用libffmpeg.so

    使用android studio新建一个native c++ Library工程:

  • 在main目录下新建jniLibs目录,将libffmpeg.so库放到arm64-v8a子目录下;
  • ffmpeg相关头文件放到jniLibs目录下的include子目录下;

    在app的build.gradle文件下增加abiFilters

android {namespace 'com.example.ndkffmpeg'compileSdk 33defaultConfig {applicationId "com.example.ndkffmpeg"minSdk 24targetSdk 33versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"ndk {// 其他x86_64按此添加,逗号分割abiFilters 'arm64-v8a'}externalNativeBuild {cmake {cppFlags '-std=c++11'}}}
}

     贴出所写CMakeLists脚本:

# 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.22.1)# Declares and names the project.project("ndkffmpeg")# 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.ndkffmpeg# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).native-lib.cpp)# 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(FFMPEG_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs)
set(FFMPEG_INCLUDE_DIR ${FFMPEG_ROOT_DIR}/include)
set(FFMPEG_LIB_DIR ${FFMPEG_ROOT_DIR}/${ANDROID_ABI})include_directories(${FFMPEG_INCLUDE_DIR} ${FFMPEG_ROOT_DIR}/../)
target_link_directories(ndkffmpeg PRIVATE ${FFMPEG_LIB_DIR})# 设置库文件的输出路径
set_target_properties(ndkffmpeg PROPERTIESLIBRARY_OUTPUT_DIRECTORY ${FFMPEG_LIB_DIR}ARCHIVE_OUTPUT_DIRECTORY ${FFMPEG_LIB_DIR})
target_link_libraries( # Specifies the target library.ndkffmpegffmpeg# Links the target library to the log library# included in the NDK.${log-lib})

    在AndroidManifest.xml文件中请求网络访问权限: 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.READ_PHONE_STATE" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /><applicationandroid:allowBackup="true"android:dataExtractionRules="@xml/data_extraction_rules"android:fullBackupContent="@xml/backup_rules"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.MyApplication"tools:targetApi="31"><activityandroid:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

    native-lib.cpp代码贴出:

#include <jni.h>
#include <string>extern "C" {
#include "libavformat/avformat.h"
}static int decode_interrupt_cb(void *ctx) {return 0;
}static void test_ffmpeg_func() {AVFormatContext* ifmt = NULL;const char* filename = "rtmp://10.0.2.2/live/8";AVDictionary *d = NULL;//av_dict_set(&d, "timeout", NULL, 0);//av_dict_set(&d, "fflags", "nobuffer", 0);int ret = avformat_open_input(&ifmt, filename, NULL, &d);ret = avformat_find_stream_info(ifmt, NULL);AVPacket pkt;av_init_packet(&pkt);while (1) {int ret = av_read_frame(ifmt, &pkt);if (ret < 0) {av_log(NULL, AV_LOG_INFO, "error\n");}if (pkt.stream_index == AVMEDIA_TYPE_VIDEO && pkt.flags & AV_PKT_FLAG_KEY) {//av_log(NULL, AV_LOG_INFO, "keyframe\n");}}
}extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ndkffmpeg_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {av_log_set_level(AV_LOG_DEBUG);test_ffmpeg_func();std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}

     输入正确的filename地址即调试运行:

5 编译ffmpeg.c为so

    编译环境如下:

  • ffmpeg release/6.1
  • NDK 17.2.4988734;

    首先进入到ffmpeg源码目录下的fftools子目录,即为编译ffmpeg命令行程序的工作目录:

xxx@localhost:~/Applications/workspace/FFmpeg/fftools$tree -L 1
.
├── Makefile
├── build_ffmpeg_for_android.sh
├── cmdutils.c
├── cmdutils.h
├── ffmpeg.c
├── ffmpeg.h
├── ffmpeg_dec.c
├── ffmpeg_demux.c
├── ffmpeg_enc.c
├── ffmpeg_filter.c
├── ffmpeg_hw.c
├── ffmpeg_mux.c
├── ffmpeg_mux.h
├── ffmpeg_mux_init.c
├── ffmpeg_opt.c
├── ffplay.c
├── ffprobe.c
├── fftools.manifest
├── fftoolsres.rc
├── fopen_utf8.h
├── objpool.c
├── objpool.h
├── opt_common.c
├── opt_common.h
├── sync_queue.c
├── sync_queue.h
├── thread_queue.c
└── thread_queue.h1 directory, 28 files

5.1 改写ffmpeg接口名

    将ffmpeg命令行程序的main方法名改写为ffmpeg名。头文件和.c文件都需要修改:

int ffmpeg(int argc, char* argv[])

 5.1 工作目录

    在此列出主要工作目录和fftools目录结构及输出目录接口,其他目录结构忽略掉:

├── CONTRIBUTING.md
├── COPYING.GPLv2
├── COPYING.GPLv3
├── COPYING.LGPLv2.1
├── COPYING.LGPLv3
├── CREDITS
├── Changelog
├── INSTALL.md
├── LICENSE.md
├── MAINTAINERS
├── Makefile
├── README.md
├── RELEASE
├── RELEASE_NOTES
├── android_arm64_output
│   ├── include
│   ├── lib
│   └── share
├── android_x86_64_output
│   ├── include
│   ├── lib
│   └── share
├── build_ffmpeg_for_android.sh
├── build_ffmpeg_for_android_with_clang.sh
├── config.h
├── config_components.h
├── configure
├── fftools
│   ├── Makefile
│   ├── build_ffmpeg_for_android.sh
│   ├── cmdutils.c
│   ├── cmdutils.h
│   ├── ffmpeg.c
│   ├── ffmpeg.h
│   ├── ffmpeg_dec.c
│   ├── ffmpeg_demux.c
│   ├── ffmpeg_enc.c
│   ├── ffmpeg_filter.c
│   ├── ffmpeg_hw.c
│   ├── ffmpeg_mux.c
│   ├── ffmpeg_mux.h
│   ├── ffmpeg_mux_init.c
│   ├── ffmpeg_opt.c
│   ├── ffplay.c
│   ├── ffprobe.c
│   ├── fftools.manifest
│   ├── fftoolsres.rc
│   ├── fopen_utf8.h
│   ├── objpool.c
│   ├── objpool.h
│   ├── opt_common.c
│   ├── opt_common.h
│   ├── sync_queue.c
│   ├── sync_queue.h
│   ├── thread_queue.c
│   └── thread_queue.h

5.2 GCC编译

    编写shell脚本,放在fftools目录下,使用GCC编译ffmepg命令行程序为so库,脚本如下,支持arm64和x86_64架构:

#! /usr/bin/env bashset -eARG_COUNT=$#
#支持架构aarch64 x86_64
ARCH_NAME=$1
if [[ ${ARG_COUNT} -lt 1 ]]||[[ ${ARCH_NAME} != "aarch64" && ${ARCH_NAME} != "x86_64" ]]; thenecho "Usage:./build_ffmpeg_for_android.sh aarch64/x86_64"exit -1
fi
echo "ARCH_NAME=${ARCH_NAME}"NDK_ROOT=/Users/mingo/Library/Android/sdk/ndk/17.2.4988734
SYSROOT=${NDK_ROOT}/sysroot
TOOLCHAIN_ARCH=${ARCH_NAME}-linux-android
FLATFORM=${NDK_ROOT}/platforms/android-28/arch-arm64
SYSROOT_INCLUDE_PATH="${SYSROOT}/usr/include/${ARCH_NAME}-linux-android -isysroot ${SYSROOT}"
COMPILER_PREFIX=${ARCH_NAME}-linux-android
CURRENT_DIR=`pwd`
BUILD_OUTPUT_DIR="${CURRENT_DIR}/../android/arm64"
if [ ${ARCH_NAME} == "x86_64" ]; thenTOOLCHAIN_ARCH=${ARCH_NAME}FLATFORM=${NDK_ROOT}/platforms/android-28/arch-x86_64BUILD_OUTPUT_DIR="${CURRENT_DIR}/../android/x86_64"
fi
echo "BUILD_OUTPUT_DIR=${BUILD_OUTPUT_DIR}"
if [ ! -d ${BUILD_OUTPUT_DIR} ]; thenmkdir -p ${BUILD_OUTPUT_DIR}
fi
echo "TOOLCHAIN_RCH=${TOOLCHAIN_ARCH}"
PREBUILT=${NDK_ROOT}/toolchains/${TOOLCHAIN_ARCH}-4.9/prebuilt/darwin-x86_64/binFF_CFLAGS="-O3 -Wall -fpic -pipe -std=c11 -ffast-math -fstrict-aliasing -Werror=strict-aliasing -Wno-psabi -Wa,--noexecstack -Wpointer-sign -Wparentheses -DANDROID -DDEBUG"
FF_CXXFLAGS="-D__thumb__ -fexceptions -frtti"CC=${PREBUILT}/${COMPILER_PREFIX}-gcc
FFMPEG_ROOT_DIR=${BUILD_OUTPUT_DIR}
FFMPEG_INCLUDE_DIR=${FFMPEG_ROOT_DIR}/include
FFMPEG_LIB_DIR=${FFMPEG_ROOT_DIR}/lib
CONFIG_H_DIR=${CURRENT_DIR}/../
FFMPEG_LIBS="-lavutil -lavformat -lavcodec -lavfilter -lavdevice -lswresample -lswscale"
ANDROID_MEDIA_LIBS="-lcamera2ndk -lmediandk"function build_for() {echo "compile ffmpeg..."FFMPEG_SRC="cmdutils.c ffmpeg_dec.c ffmpeg_demux.c ffmpeg_enc.c ffmpeg_filter.c \ffmpeg_hw.c ffmpeg_mux_init.c ffmpeg_mux.c ffmpeg_opt.c ffmpeg.c \objpool.c opt_common.c \sync_queue.c thread_queue.c"${CC} --sysroot=${FLATFORM} ${FF_CFLAGS} -shared ${FFMPEG_SRC} -o ${FFMPEG_LIB_DIR}/libffmpegc.so \-I${FFMPEG_INCLUDE_DIR} -I${CONFIG_H_DIR} -I${SYSROOT_INCLUDE_PATH} \-L${FFMPEG_LIB_DIR} -L${PLATFORM}/usr/lib \${FFMPEG_LIBS} ${ANDROID_MEDIA_LIBS}
}build_forif [ $? -eq 0 ]; thenecho "compile ffmpegc succ"
elseecho "compile ffmpegc fail"
fi

    按照以上脚本编译,可顺利完成arm64和x86_64的编译工作。最后的输出目录在:

  • arm64和_x8664平台输出均与对应平台ffmpeg库路径一致; 

    提示:此处编译ffmpeg.c为so的时候,需要静态链接ffmpeg的各.a库。 

    然后,使用工具可以看到libffmpegc.so相关so的依赖库信息:

Dynamic section at offset 0x1c62808 contains 28 entries:Tag        Type                         Name/Value0x0000000000000001 (NEEDED)             Shared library: [libcamera2ndk.so]0x0000000000000001 (NEEDED)             Shared library: [libmediandk.so]0x0000000000000001 (NEEDED)             Shared library: [libc.so]0x0000000000000001 (NEEDED)             Shared library: [libdl.so]0x0000000000000001 (NEEDED)             Shared library: [libm.so]0x0000000000000001 (NEEDED)             Shared library: [libz.so]0x0000000000000001 (NEEDED)             Shared library: [liblog.so]0x000000000000001a (FINI_ARRAY)         0x1b703780x000000000000001c (FINI_ARRAYSZ)       8 (bytes)0x0000000000000004 (HASH)               0x1c80x0000000000000005 (STRTAB)             0x323100x0000000000000006 (SYMTAB)             0xab300x000000000000000a (STRSZ)              137735 (bytes)0x000000000000000b (SYMENT)             24 (bytes)0x0000000000000003 (PLTGOT)             0x1c72a080x0000000000000002 (PLTRELSZ)           73680 (bytes)0x0000000000000014 (PLTREL)             RELA0x0000000000000017 (JMPREL)             0x163c500x0000000000000007 (RELA)               0x572100x0000000000000008 (RELASZ)             1100352 (bytes)0x0000000000000009 (RELAENT)            24 (bytes)0x0000000000000018 (BIND_NOW)0x000000006ffffffb (FLAGS_1)            Flags: NOW0x000000006ffffffe (VERNEED)            0x571c00x000000006fffffff (VERNEEDNUM)         20x000000006ffffff0 (VERSYM)             0x53d180x000000006ffffff9 (RELACOUNT)          403180x0000000000000000 (NULL)               0x0

    所用工具及其执行命令如下:

~/Library/Android/sdk/ndk/21.3.6528147/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin/aarch64-linux-android-readelf libffmpegc.so -d

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/336741.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android Context 详解

一、什么是Context&#xff1f; Context是一个抽象基类。在翻译为上下文&#xff0c;是提供一些程序的运行环境基础信息。 Context下有两个子类&#xff0c;ContextWrapper是上下文功能的封装类&#xff08;起到方法传递的作用&#xff0c;主要实现还是ContextImpl&#xff0…

万字长文详解QUIC协议,为什么有了TCP我们还需要QUIC?

本文目录 1.前言2. HTTP缺点缺点一&#xff1a;建立连接的握手延迟大缺点二&#xff1a;多路复用的队首阻塞缺点三&#xff1a;TCP协议的更新滞后 3.TCP缺点3.QUIC优点一&#xff1a;避免队首阻塞的多路复用优点二&#xff1a;支持连接迁移优点三&#xff1a;可插拔的拥塞控制优…

【OceanBase诊断调优】—— obdiag 工具助力OceanBase数据库诊断调优(DBA 从入门到实践第八期)

1. 前言 昨天给大家分享了【DBA从入门到实践】第八期&#xff1a;OceanBase数据库诊断调优、认证体系和用户实践 中obdiag的部分&#xff0c;今天将其中的内容以博客的形式给大家展开一下&#xff0c;方便大家阅读。 2. 正文 在介绍敏捷诊断工具之前&#xff0c;先说说OceanBa…

VMware虚拟机安装Ubuntu-Server版教程(超详细)

目录 1. 下载2. 安装 VMware3. 安装 Ubuntu3.1 新建虚拟机3.2 安装操作系统 4. SSH方式连接操作系统4.1 好用的SSH工具下载&#xff1a;4.2 测试SSH连接 5. 开启root用户登录5.1 设置root用户密码5.2 传统方式切换root用户5.3 直接用root用户登录5.4 SSH启用root用户登录 6. 安…

FANUC机器人保养服务包,高效又可靠!

发那科机器人作为工业生产中的重要设备&#xff0c;其保养工作至关重要。定期FANUC机械手保养不仅可以延长机器人的使用寿命&#xff0c;还能提高生产效率和质量。 法那科机器人保养步骤&#xff1a; 基本的法兰克机器人保养是维护机器人的第一步&#xff0c;正确的保养步骤还…

Rainbond 携手 TOPIAM 打造企业级云原生身份管控新体验

TOPIAM 企业数字身份管控平台&#xff0c; 是一个开源的IDaas/IAM平台、用于管理账号、权限、身份认证、应用访问&#xff0c;帮助整合部署在本地或云端的内部办公系统、业务系统及三方 SaaS 系统的所有身份&#xff0c;实现一个账号打通所有应用的服务。 传统企业 IT 采用烟囱…

Redis用GEO实现附近的人功能

文章目录 ☃️概述☃️命令演示☃️API将数据库表中的数据导入到redis中去☃️实现附近功能 ☃️概述 GEO就是Geolocation的简写形式&#xff0c;代表地理坐标。Redis在3.2版本中加入了对GEO的支持&#xff0c;允许存储地理坐标信息&#xff0c;帮助我们根据经纬度来检索数据。…

木馒头头戴式蓝牙耳机

这里写目录标题 木馒头二代头戴式蓝牙耳机清除连接记忆 木馒头二代头戴式蓝牙耳机清除连接记忆 在配对模式下&#xff0c;同时按住播放和暂停按钮4秒&#xff0c;LED闪烁紫色3次&#xff0c;即为清除成功。

HTML动态响应2-Servlet+Ajax实现HTTP前后台交互方式

作者:私语茶馆 前言 其他涉及到的参考章节: HTML动态响应1—Ajax动态处理服务端响应-CSDN博客 Web应用JSON解析—FastJson1.2.83/Tomcat/IDEA解析案例-CSDN博客 HTML拆分与共享方式——多HTML组合技术-CSDN博客 1.场景: WEb项目经常需要前后端交互数据,并动态修改HTML页…

OSError: [Errno 117] Structure needs cleaning

一 问题描述 OSError: [Errno 117] Structure needs cleaning: /tmp/pymp-wafeatri 我重新使用SSH登录也会提示这个类似问题 二 解决方法 2.1 尝试删除报错的文件 &#xff08;想直接看最终解决方法的可忽略此处&#xff09; sudo rm -rf /tmp/pymp-wafeatri 此种方法只能保证…

【linux-imx6ull-设备树点灯】

目录 1. 设备树简介1.1 编译-引用1.2 设备树文件结构1.3 设备树节点介绍1.3.1 特殊节点chosen 1.4 节点内容追加 2. 设备树常用OF操作函数2.1 节点寻找类2.2 属性提取类2.3 其它常用类 4. 设备树下LED实验4.1 实验简介4.2 添加LED设备节点4.3 获取设备节点并提取属性4.3.1 获取…

内网渗透-隧道搭建ssp隧道代理工具

内网渗透-隧道搭建&ssp隧道代理工具 目录 内网渗透-隧道搭建&ssp隧道代理工具spp隧道代理工具spp工作原理图cs上线主机spp代理通信服务端配置客户端配置CS配置设置CS生成木马的监听器配置CS监听上线的监听器生成木马 spp隧道搭建服务端配置客户端配置CS配置 内网穿透&a…

【机器学习300问】100、怎么理解卷积神经网络CNN中的池化操作?

一、什么是池化&#xff1f; 卷积神经网络&#xff08;CNN&#xff09;中的池化&#xff08;Pooling&#xff09;操作是一种下采样技术&#xff0c;其目的是减少数据的空间维度&#xff08;宽度和高度&#xff09;&#xff0c;同时保持最重要的特征并降低计算复杂度。池化操作不…

【吊打面试官系列】Java高并发篇 - 什么是乐观锁和悲观锁?

大家好&#xff0c;我是锋哥。今天分享关于 【什么是乐观锁和悲观锁?】面试题&#xff0c;希望对大家有帮助&#xff1b; 什么是乐观锁和悲观锁? 1、乐观锁&#xff1a; 就像它的名字一样&#xff0c;对于并发间操作产生的线程安全问题持乐观状态&#xff0c; 乐观锁认为竞争…

手拉手springboot整合kafka发送消息

环境介绍技术栈springbootmybatis-plusmysqlrocketmq软件版本mysql8IDEAIntelliJ IDEA 2022.2.1JDK17Spring Boot3.1.7kafka2.13-3.7.0 创建topic时&#xff0c;若不指定topic的分区(Partition主题分区数)数量使&#xff0c;则默认为1个分区(partition) springboot加入依赖kafk…

【深度学习基础】使用Pytorch搭建DNN深度神经网络与手写数字识别

目录 写在开头 一、DNN的搭建 问题描述与数据集 神经网络搭建 模型训练 模型评估 模型复用 二、手写数字识别 任务描述 数据集 神经网络搭建 模型训练 模型评估 写在最后 写在开头 本文将介绍如何使用PyTorch框架搭建深度神经网络模型。实现模型的搭建、模…

Ps系统教程03

选区工具的组合使用 先用魔棒将大致区域点击圈主 会发现一些零散的小区域 使用套索工具进行区域的加减&#xff08;按住shift/alt键进行相关区域加减&#xff09; 可以放大查看 基本处理完细节之后 如果把不用的填充背景直接按delete删除&#xff0c;那么原版图案就会…

【贪心算法题目练习】

1. 分发饼干 这道题目和我们之前讲到的田忌赛马的问题很相似&#xff0c;只不过这这里不需要劣等马去抵消掉优等马&#xff0c;直接上贪心策略&#xff1a; 先将两个数组排序。针对胃口较小的孩子&#xff0c;从小到大挑选饼干: i. 如果当前饼干能满足&#xff0c;直接喂(最小…

大语言模型实战——最小化模型评测

1. 引言 现在国内外的主流模型&#xff0c;在新模型发布时都会给出很多评测数据&#xff0c;用以说明当前模型在不同数据集上的测评表现&#xff08;如下面llama3发布的评测数据&#xff09;。 这些评测数据是如何给出来的呢&#xff1f;这篇文章会用一个最小化的流程来还原下…

【限免】短时傅里叶变换时频分析【附MATLAB代码】

来源&#xff1a;微信公众号&#xff1a;EW Frontier 简介 一种能够同时对信号时域和频域分析的方法——短时傅里叶变换&#xff08;STFT&#xff09;&#xff0c;可以在时频二维角度准确地描述信号 的时间、频域的局部特性&#xff0c;与其他算法不同&#xff0c;通过该算法可…