macOS上编译android的ffmpeg及ffmpeg.c

news2024/11/15 18:36:33

 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 bash

set -e

ARG_COUNT=$#
#支持架构aarch64 x86_64
ARCH_NAME=$1
if [[ ${ARG_COUNT} -lt 1 ]]||[[ ${ARCH_NAME} != "aarch64" && ${ARCH_NAME} != "x86_64" ]]; then
	echo "Usage:./build_ffmpeg_for_android.sh aarch64/x86_64"
	exit -1
fi

NDK_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" ]; then
	TOOLCHAIN_ARCH=${ARCH_NAME}
	FLATFORM=${NDK_ROOT}/platforms/android-28/arch-x86_64
	BUILD_OUTPUT_DIR="${CURRENT_DIR}/android/x86_64"
fi
if [ ! -d ${BUILD_OUTPUT_DIR} ]; then
	mkdir -p ${BUILD_OUTPUT_DIR}
fi
echo "TOOLCHAIN_RCH=${TOOLCHAIN_ARCH}"
PREBUILT=${NDK_ROOT}/toolchains/${TOOLCHAIN_ARCH}-4.9/prebuilt/darwin-x86_64/bin

FF_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 clean
	make -j9
	make install
}

build_for

if [ $? -eq 0 ]; then
	echo "configure ffmpeg succ"
else
	echo "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.prop

14 directories, 2 files

    将shell脚本改成如下即可: 

NDK_ROOT=/Users/xxx/Library/Android/sdk/ndk/17.2.4988734

FLATFORM=${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 PROPERTIES
        LIBRARY_OUTPUT_DIRECTORY ${FFMPEG_LIB_DIR}
        ARCHIVE_OUTPUT_DIRECTORY ${FFMPEG_LIB_DIR}
        )
target_link_libraries( # Specifies the target library.
        ndkffmpeg
        avutil
        avformat
        avcodec
        avfilter
        avdevice
        swresample
        swscale
        # 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 = "";
    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 bash

set -e

ARG_COUNT=$#
#支持架构aarch64 x86_64
ARCH_NAME=$1
if [[ ${ARG_COUNT} -lt 1 ]]||[[ ${ARCH_NAME} != "aarch64" && ${ARCH_NAME} != "x86_64" ]]; then
	echo "Usage:./build_ffmpeg_for_android.sh aarch64/x86_64"
	exit -1
fi

NDK_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" ]; then
	FLATFORM=${NDK_ROOT}/platforms/android-${ANDROID_VER}/arch-x86_64
	ENABLE_OPT="--disable-asm"
	OUTPUT_DIR="${CURRENT_DIR}/android_clang/x86_64"
fi
if [ ! -d ${OUTPUT_DIR} ]; then
	mkdir -p ${OUTPUT_DIR}
fi
PREBUILT=${NDK_ROOT}/toolchains/llvm/prebuilt/darwin-x86_64/bin
COMPILER_PREFIX=${ARCH_NAME}-linux-android
SYSROOT=${PREBUILT}/../sysroot

FF_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 clean
	make -j9
	make install
}

build_for

if [ $? -eq 0 ]; then
	echo "configure ffmpeg succ"
else
	echo "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 bash

set -e

ARG_COUNT=$#
#支持架构aarch64 x86_64
ARCH_NAME=$1
if [[ ${ARG_COUNT} -lt 1 ]]||[[ ${ARCH_NAME} != "aarch64" && ${ARCH_NAME} != "x86_64" ]]; then
	echo "Usage:./build_ffmpeg_for_android.sh aarch64/x86_64"
	exit -1
fi

NDK_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" ]; then
	TOOLCHAIN_ARCH=${ARCH_NAME}
	FLATFORM=${NDK_ROOT}/platforms/android-28/arch-x86_64
	BUILD_OUTPUT_DIR="${CURRENT_DIR}/android/x86_64"
fi
if [ ! -d ${BUILD_OUTPUT_DIR} ]; then
	mkdir -p ${BUILD_OUTPUT_DIR}
fi
echo "TOOLCHAIN_RCH=${TOOLCHAIN_ARCH}"
PREBUILT=${NDK_ROOT}/toolchains/${TOOLCHAIN_ARCH}-4.9/prebuilt/darwin-x86_64/bin

FF_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 clean
	make -j9
	make install
}

build_for

if [ $? -eq 0 ]; then
	echo "configure ffmpeg succ"
else
	echo "configure ffmpeg fail"
fi

GCC_PREFIX=${PREBUILT}/../lib/gcc/${COMPILER_PREFIX}/4.9.x
RPATH=${FLATFORM}/usr/lib
if [ ${ARCH_NAME} == "x86_64" ]; then
	RPATH=${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_libs

if [ $? -eq 0 ]; then
	echo "package ffmpeg succ"
else
	echo "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
        └── ffmpeg

14 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 33

    defaultConfig {
        applicationId "com.example.ndkffmpeg"
        minSdk 24
        targetSdk 33
        versionCode 1
        versionName "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 PROPERTIES
        LIBRARY_OUTPUT_DIRECTORY ${FFMPEG_LIB_DIR}
        ARCHIVE_OUTPUT_DIRECTORY ${FFMPEG_LIB_DIR}
        )
target_link_libraries( # Specifies the target library.
        ndkffmpeg
        ffmpeg
        # 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" />

    <application
        android: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">
        <activity
            android: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 = "";
    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.h

1 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 bash

set -e

ARG_COUNT=$#
#支持架构aarch64 x86_64
ARCH_NAME=$1
if [[ ${ARG_COUNT} -lt 1 ]]||[[ ${ARCH_NAME} != "aarch64" && ${ARCH_NAME} != "x86_64" ]]; then
	echo "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" ]; then
	TOOLCHAIN_ARCH=${ARCH_NAME}
	FLATFORM=${NDK_ROOT}/platforms/android-28/arch-x86_64
	BUILD_OUTPUT_DIR="${CURRENT_DIR}/../android/x86_64"
fi
echo "BUILD_OUTPUT_DIR=${BUILD_OUTPUT_DIR}"
if [ ! -d ${BUILD_OUTPUT_DIR} ]; then
	mkdir -p ${BUILD_OUTPUT_DIR}
fi
echo "TOOLCHAIN_RCH=${TOOLCHAIN_ARCH}"
PREBUILT=${NDK_ROOT}/toolchains/${TOOLCHAIN_ARCH}-4.9/prebuilt/darwin-x86_64/bin

FF_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_for

if [ $? -eq 0 ]; then
	echo "compile ffmpegc succ"
else
	echo "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/Value
 0x0000000000000001 (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)         0x1b70378
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x0000000000000004 (HASH)               0x1c8
 0x0000000000000005 (STRTAB)             0x32310
 0x0000000000000006 (SYMTAB)             0xab30
 0x000000000000000a (STRSZ)              137735 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x1c72a08
 0x0000000000000002 (PLTRELSZ)           73680 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0x163c50
 0x0000000000000007 (RELA)               0x57210
 0x0000000000000008 (RELASZ)             1100352 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x0000000000000018 (BIND_NOW)
 0x000000006ffffffb (FLAGS_1)            Flags: NOW
 0x000000006ffffffe (VERNEED)            0x571c0
 0x000000006fffffff (VERNEEDNUM)         2
 0x000000006ffffff0 (VERSYM)             0x53d18
 0x000000006ffffff9 (RELACOUNT)          40318
 0x0000000000000000 (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.coloradmin.cn/o/1702283.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构】链式二叉树(超详细)

文章目录 前言二叉树的链式结构二叉树的遍历方式二叉树的深度优先遍历前序遍历(先根遍历)中序遍历(中根遍历)后序遍历(后根遍历) 二叉树的广度优先遍历层序遍历 二叉树链式结构接口实现二叉树结点个数二叉树叶子结点个数二叉树的深度&#xff08;高度&#xff09;二叉树第k层结…

R18 NTN中的RACH-less HO

在看R18 38.300时,发现NTN场景 增加了如下黄色字体的内容,R18 NTN支持了RACH-less HO,索性就简单看了看。 NTN RACH less HO相关的描述主要在38.331,38.213和38.321中。38.300中的描述显示:网络侧会通过RRCReconfiguration消息将RACH-less HO相关的配置下发给UE, 其中会包…

Linux入侵应急响应与排查

入侵者在入侵成功后&#xff0c;往往会留下后门以便再次访问被入侵的系统&#xff0c; 而创建系统账号是一种比较常见的后门方式。 查询特权用户特权用户(uid 为0) awk -F: $30{print $1} /etc/passwd 查找远程可以登录的账户 awk /\$1|\$5|\$6/{print $1} /etc/shadow $1&a…

仿真APP在微波加热仿真分析中的应用

一、背景介绍 微波炉是一种常用的食物加热工具&#xff0c;主要是由腔室、磁控管、波导管三个部分组成。在工作过程中&#xff0c;磁控管产生波长约为12.2cm的微波&#xff08;对应频率2.45GHz&#xff09;&#xff0c;通过波导管注入腔室内&#xff0c;在腔室内产生振荡的磁场…

echarts取消纵坐标,自定义提示内容,完整 echarts 布局代码

效果图 实现代码 开启点击柱子时的提示内容 //完整写法请看下面tooltip: {trigger: axis,axisPointer: {type: shadow}},自定义提示内容 //完整写法请看下面formatter: function (param) {// param是悬浮窗所在的数据&#xff08;x、y轴数据&#xff09;let relVal "&…

【LeeCode算法】第67题:二进制求和

目录 一、题目描述 二、初次解答 三、官方解法 四、总结 一、题目描述 二、初次解答 1. 思路&#xff1a;将a和b两个字符串转换成十进制&#xff0c;然后将相加的结果转换回文本的二进制。 2. 代码&#xff1a; char* addBinary(char* a, char* b) {int a_len strlen(a);i…

P3-P6容器之分类与各种测试

序列容器 关联式容器 这里讲到的哈希碰撞&#xff0c;是两个元素的存放位置冲突&#xff0c;用链表进行处理&#xff1b;同时hashtable的链表不能太长 以下测试程序之辅助函数 一、序列容器 array vector vector容量是两倍增长&#xff0c; 当添加元素之后&#xff0c;容…

什么样的跨网文件交换系统适合车企行业?

对于汽车行业而言&#xff0c;随着市场的全球化&#xff0c;产品和数据的跨网流通已成为常态。在这个过程中&#xff0c;跨网文件交换系统成为了连接全球供应链、加速产品开发周期、确保数据安全的不可或缺的桥梁。但什么样的系统才能真正满足车企的严苛要求呢&#xff1f;让我…

阿里通义千问大模型AI接入火车头自动生成内容插件

插件特点&#xff1a; 可以根据采集的关键词&#xff0c;自动生成文章可自定义提示词 也可以分析标题重写一个标题2个提问标签 如有需要可自由增加对话标签自己可以设置TXT关键词导入&#xff0c;自动采集生成 安装说明&#xff1a; 1.需要python环境 &#xff0c;具体可以…

【CSDN独家公开】Python解析.SchDoc格式文件转换为json文件

前情提要 因工作需求&#xff0c;需要解析.SchDoc格式文件&#xff0c;提取文本和位置关系&#xff0c;通常方式是转换为图片或PDF&#xff0c;再进行OCR&#xff0c;但是这样识别精度太低了 Github找了好些项目&#xff0c;都不支持 PyAltium不支持 https://github.com/plu…

docker安装应用

软件安装步骤可以在docker的官网查找&#xff1a; https://hub.docker.com/1.安装tomcat 这里演示用的最新版&#xff0c;版本是10.0.14 1.拉取镜像 docker pull tomcat 直接下载最新版 [rootlocalhost ~]# docker pull tomcat Using default tag: latest latest: Pulling f…

Discuz!X3.4论坛网站公安备案号怎样放到网站底部?

Discuz&#xff01;网站的工信部备案号都知道在后台——全局——站点信息——网站备案信息代码填写&#xff0c;那公安备案号要添加在哪里呢&#xff1f;并没有看到公安备案号填写栏&#xff0c;今天驰网飞飞和你分享 1&#xff09;工信部备案号和公安备案号统一填写到网站备案…

8. C++通过epoll+fork的方式实现高性能网络服务器

epollfork 实现高性能网络服务器 一般在服务器上&#xff0c;CPU是多核的&#xff0c;上述epoll实现方式只使用了其中的一个核&#xff0c;造成了资源的大量浪费。因此我们可以将epoll和fork结合来实现更高性能的网络服务器。 创建子进程函数–fork( ) 要了解线程我们先来了解…

2024新数据库入门教程

1.官网下载MySQL 下载Mysql链接: 点击下载mysql 下载完成后解压到某一个文件夹&#xff08;记住这个路径&#xff0c;一会要用到&#xff09; 2.配置初始化文件my.ini 在根目录下创建一个txt文件&#xff0c;名字叫my&#xff0c;文件后缀为ini 以下代码除安装目录和数…

C字符串和内存函数介绍(一)——长度不固定的字符串函数

前面我们一起学习了strlen&#xff0c;strcpy&#xff0c;strcmp&#xff0c;strcat的使用以及它们的模拟实现&#xff0c;它们的特点是你传参的时候&#xff0c;传过去的是数组首元素的地址&#xff0c;然后无论是计算长度&#xff0c;实现拷贝&#xff0c;相互比较还是进行追…

GIS 交通线网可视化:优化城市交通管理与规划

图扑 GIS 交通线网可视化可帮助城市规划和交通管理部门做出更精准的决策&#xff0c;提升出行效率和城市整体交通秩序。

从零入门激光SLAM(二十一)——FAST-LIO2论文解析

FAST-LIO2: Fast Direct LiDAR-Inertial Odometry 论文地址&#xff1a;https://ieeexplore.ieee.org/stamp/stamp.jsp?tp&arnumber9697912 代码&#xff1a;https://github.com/hku-mars/FAST_LIO 一、文章概述 1.问题导向 基于视觉传感器的高分辨率和高精度的实时密…

金锋关晓柔短视频:成都鼎茂宏升文化传媒公司

金锋关晓柔短视频&#xff1a;情感与创意的交织 在短视频的浪潮中&#xff0c;无数创作者凭借独特的视角和创意脱颖而出。其中&#xff0c;金锋和关晓柔共同打造的短视频系列以其深厚的情感内涵和精湛的创意表达&#xff0c;成都鼎茂宏升文化传媒公司吸引了大量观众的关注&…

zabbix自定义监控项

文章目录 1、配置conf文件(zabbix_agent2)linuxwindows 2、配置监控项3、配置触发器4、查看监控数据 示例自定义程序 hash_tool&#xff1a;输出指定目录的哈希值 调用指令&#xff1a; hash_tool --path [指定目录] 1、配置conf文件(zabbix_agent2) linux vim /etc/zabbix/z…

液氢产业化进程提速 液氢装备检测市场需求空间广阔

液氢产业化进程提速 液氢装备检测市场需求空间广阔 液氢装备检测试验项目涉及到火烧试验、置换试验、振动试验、燃烧实验、高压氢循环试验、预冷试验、液氢阀门检测试验等。检测试验是推动氢能技术自主化、高质量发展的重要步骤&#xff0c;近年来&#xff0c;随着液氢应用场景…