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.pswp.cn/bicheng/20681.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/20681.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/20681.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

信息學奧賽初賽天天練-18-挑戰程序閱讀-最長公共子序列、字符串與數組越界的巧妙應用

PDF文檔公眾號回復關鍵字:20240601 1 2023 CSP-J 閱讀程序2 閱讀程序&#xff08;程序輸入不超過數組成字符串定義的范圍&#xff1a;判斷題正確填√&#xff0c;錯誤填&#xff1b;除特殊說明外&#xff0c;判斷題1.5分&#xff0c;選擇題3分&#xff0c;共計40分&#xff…

從創意到成功:創業全過程詳解

目錄 創業目標市場的選擇和分析用戶畫像的描繪軟件產品的核心功能和價值主張競爭對手分析及自身競爭優勢目標用戶的具體需求調研初步的產品設計思路或框架技術棧的選擇基于哪些考量如何規劃產品的迭代路線圖預計的商業模式 1. 創業目標市場的選擇和分析 市場選擇的重要性 創…

YOLOv10漲點改進:IoU優化 | Powerful-IoU更好、更快的收斂IoU,效果秒殺CIoU、GIoU等 | 2024年最新IoU

??????本文獨家改進:Powerful-IoU更好、更快的收斂IoU,是一種結合了目標尺寸自適應懲罰因子和基于錨框質量的梯度調節函數的損失函數 ??????MS COCO和PASCAL VOC數據集實現漲點 《YOLOv10魔術師專欄》將從以下各個方向進行創新: 【原創自研模塊】【多組合點優…

spark SQL優化器catalyst學習

一、Catalyst 概述 Catalyst 是 Spark SQL 的優化器&#xff0c;它負責將 SQL 查詢轉換為物理執行計劃。Catalyst 優化器的目標是生成高效的執行計劃&#xff0c;以最小化查詢的執行時間。它使用了多種優化技術&#xff0c;包括基于規則的優化、基于代價的優化和動態規劃等。我…

Dijkstra求最短路篇二(全網最詳細講解兩種方法,適合小白)(python,其他語言也適用)

前言&#xff1a; Dijkstra算法博客講解分為兩篇講解&#xff0c;這兩篇博客對所有有難點的問題都會講解&#xff0c;小白也能很好理解。看完這兩篇博客后保證收獲滿滿。 第一篇博客講解樸素Dijkstra算法Dijkstra求最短路篇一(全網最詳細講解兩種方法&#xff0c;適合小白)(p…

openstack 中如何檢查VLAN 配置: 確保正確配置了兩個 VLAN,并且兩個 VLAN 之間進行了正確的路由。

在 OpenStack 中檢查 VLAN 配置并確保兩個 VLAN 之間進行了正確的路由&#xff0c;可以按照以下步驟進行操作&#xff1a; 查看網絡配置&#xff1a; 登錄到 OpenStack 控制節點上的命令行界面。使用 neutron net-list 命令查看當前存在的網絡列表。找到與你關注的 VLAN 相關的…

計網ppt標黃知識點整理第(2)章節——謝希仁版本、期末復習自用

物理層考慮的是怎樣才能在連接各種計算機的傳輸媒體上傳輸數據比特流&#xff0c;而不是指具體的傳輸媒體。4 個特性&#xff1a; 機械特性&#xff1a;指明接口所用接線器的形狀和尺寸、引線數目和排列、固定和鎖定裝置等。 電氣特性&#xff1a;指明在接口電纜的各條線上出現…

如何在 JS 中快速讀取文件

本文翻譯自 How to read files quickly in JavaScript&#xff0c;作者&#xff1a;Daniel Lemire&#xff0c; 略有刪改。 假設你需要在服務器上使用JavaScript讀取多個文件。在像Node.js這樣的運行時環境中&#xff0c;JavaScript有多種讀取文件的方式。哪一種是最好的呢&…

Linux軟件安裝包rpm與tgz格式的區別

rpm與tgz的區別 1、Linux軟件包的內容分類2、Linux軟件包的格式分類 1、Linux軟件包的內容分類 Linux應用程序的軟件包按內容類別可分為兩類&#xff1a; 可執行文件&#xff08;編譯后的二進制軟件包&#xff09; 解包后可以直接運行&#xff0c;看不到源代碼。例如&#xff0…

基于Springboot駕校預約平臺小程序的設計與實現(源碼+數據庫+文檔)

一.項目介紹 系統角色&#xff1a;管理員、教練、學員 小程序(僅限于學員注冊、登錄)&#xff1a; 查看管理員發布的公告信息 查看管理員發布的駕校信息 查看所有教練信息、預約(需教練審核)、評論、收藏喜歡的教練 查看管理員發布的考試信息、預約考試(需管理…

代碼隨想錄算法訓練營Day8|541. 反轉字符串II、替換數字、151.翻轉字符串里的單詞、卡碼網:55.右旋轉字符串

541. 反轉字符串II 1.這道題剛開始把題意理解錯了&#xff0c;以為對于任意長度的字符串都只反轉[0,k-1]以及[2k,3k-1]區間的值。 2.但實際上是要把一個字符串分成若干長度為2k的小區間&#xff0c;反轉前[0,k-1]的字符串&#xff0c;[k,2k-1]保持不變; 3.如果有一個區間字符串…

2024年東北師范CCPC

文章目錄 A.Paper WateringB.nIM gAMEE.Checksum A.Paper Watering 思路&#xff1a;題目說有平方和開方兩種操作&#xff0c;如果這個數是平方數&#xff0c;那么它開方之后就只能開方&#xff0c;如果平方的話就重復了&#xff0c;反之就有開方和平方兩種操作。 代碼如下 //…

為了方便看公眾號文章,我搭建了個博客,在線看公眾號所有歷史文章,想看哪天的文章一秒就能找到

公眾號沒有個網頁版的文章列表&#xff0c;只能在電腦和手機客戶端看&#xff0c;想看之前的歷史文章只能一直往下拉&#xff0c;想找某篇文章非常費勁。 為了方便看公眾號文章&#xff0c;我搭建了個博客&#xff0c;博客地址https://sushengbuhuo.github.io/blog &#xf…

通過 SFP 接口實現千兆光纖以太網通信1

基于米聯客ARTIX-7 系列開發板及其開發手冊。 總體實現框圖如下&#xff1a; SFP 接口 SFP 信號定義如下圖所示。 Tri Mode Ethernet MAC 設置 由于使用千兆通訊&#xff0c;因此將速率設為 1Gbps。如下圖所示。 首先&#xff0c;由于該 IP 需要與 IP 核 1G/2.5G Ethernet …

基于IoTDB 平臺的學習和研究

Apache IoTDB&#xff08;物聯網數據庫&#xff09;是一個針對物聯網領域的高性能原生數據庫&#xff0c;適用于數據管理和分析&#xff0c;并可在邊緣計算和云端部署。由于它輕量級的架構、高性能和豐富的功能集&#xff0c;以及與Apache Hadoop、Spark和Flink的深度集成&…

【面試】生成class文件的編譯器有哪些?

目錄 1. 說明2. javac3. IDE(集成開發環境)中的編譯器3.1 Eclipse編譯器3.2 IntelliJ IDEA編譯器 1. 說明 1.javac和IDE中的編譯器是最常用的和主要的。2.這些編譯器都能夠將Java源代碼編譯為可在JVM上執行的字節碼文件&#xff0c;是實現Java跨平臺特性的關鍵。3.選擇編譯器時…

數據管理知識體系必知的14張語境關系圖

近期對數據管理知識體系中的語境關系圖進行了整體學習梳理,總共有14張圖,具體如下,供大家參考。應該說語境關系圖和環境因素六邊形圖是各有側重、互為補充關系。語境關系圖是環境因素六邊形圖的細化,描述了每個知識領域中的細節,相當于數據管理的微觀視角, 包括與人員、 …

kali中切換python版本

kali中切換python版本 在日常使用的過程中&#xff0c;可以通過一些工具來做打靶環境&#xff0c;或者工具的啟動&#xff0c;都和python關聯&#xff0c;而有時存在工具安裝&#xff0c;或者運行的時候出現報錯&#xff0c;這時候極大可能是因為我們本地的kali中python的版本不…

Android Studio | 小白如何運行別人的安卓項目

目錄 Step1&#xff1a;正確地打開項目 Step2&#xff1a;AS 同步時報錯 Step3&#xff1a;同步完成后啟動 Step4&#xff1a;啟動成功 說明&#xff1a;本文簡稱 Android Studio 為 AS Step1&#xff1a;正確地打開項目 重點&#xff1a;確認好項目的根目錄是哪個目錄&am…

進程與線程(三)

進程與線程&#xff08;三&#xff09; 進程間通信傳統間的進程間通信機制無名管道無名管道的特征無名管道的創建父子進程通信測試管道的大小管道讀寫易出現的問題 有名管道創建有名管道有名管道的寫端代碼有名管道的讀端代碼 信號信號的特征產生信號硬件來源軟件來源發送信號的…