Ubuntu系统下交叉编译Android的X264库

发布于:2025-09-01 ⋅ 阅读:(20) ⋅ 点赞:(0)

        本次交叉编译是在Ubuntu20.4上进行的。

一、下载X264源码:

        1、进入官网:Git - VideoLAN Wiki

        

        在命令行中git clone下载源码:

    git clone https://code.videolan.org/videolan/x264.git

        

        2、进入x264源码目录:

        3、查看x264目录下的configure:

        打开configure后查看帮助文档,在这会有编译相关的设置参数的解析:

        下面是configure参数设置的内容:

Usage: ./configure [options]

Help:
  -h, --help               print this message

Standard options:
  --prefix=PREFIX          install architecture-independent files in PREFIX
                           [/usr/local]
  --exec-prefix=EPREFIX    install architecture-dependent files in EPREFIX
                           [PREFIX]
  --bindir=DIR             install binaries in DIR [EPREFIX/bin]
  --libdir=DIR             install libs in DIR [EPREFIX/lib]
  --includedir=DIR         install includes in DIR [PREFIX/include]
  --extra-asflags=EASFLAGS add EASFLAGS to ASFLAGS
  --extra-cflags=ECFLAGS   add ECFLAGS to CFLAGS
  --extra-ldflags=ELDFLAGS add ELDFLAGS to LDFLAGS
  --extra-rcflags=ERCFLAGS add ERCFLAGS to RCFLAGS

Configuration options:
  --disable-cli            disable cli
  --system-libx264         use system libx264 instead of internal
  --enable-shared          build shared library
  --enable-static          build static library
  --disable-bashcompletion disable installation of bash-completion script
  --enable-bashcompletion  force installation of bash-completion script
  --bashcompletionsdir=DIR install bash-completion script in DIR [auto]
  --disable-opencl         disable OpenCL features
  --disable-gpl            disable GPL-only features
  --disable-thread         disable multithreaded encoding
  --disable-win32thread    disable win32threads (windows only)
  --disable-interlaced     disable interlaced encoding support
  --bit-depth=BIT_DEPTH    set output bit depth (8, 10, all) [all]
  --chroma-format=FORMAT   output chroma format (400, 420, 422, 444, all) [all]

Advanced options:
  --disable-asm            disable platform-specific assembly optimizations
  --enable-lto             enable link-time optimization
  --enable-debug           add -g
  --enable-gprof           add -pg
  --enable-strip           add -s
  --enable-pic             build position-independent code

Cross-compilation:
  --host=HOST              build programs to run on HOST
  --cross-prefix=PREFIX    use PREFIX for compilation tools
  --sysroot=SYSROOT        root of cross-build tree

External library support:
  --disable-avs            disable avisynth support
  --disable-swscale        disable swscale support
  --disable-lavf           disable libavformat support
  --disable-ffms           disable ffmpegsource support
  --disable-gpac           disable gpac support
  --disable-lsmash         disable lsmash support

二、下载及安装NDK:

        在NDK 下载 NDK 下载  |  Android NDK  |  Android Developers下载Liunx平台的NDK。

         本次交叉编译下载的是android-ndk-r27c-linux.zip版本的。

        下载解压后安装,具体的过程在前面的文章已经做介绍,需要的可详细查看文章:

                Ubuntu系统下交叉编译Android的X265库

               

三、创建脚本x264_build.sh进行编译:

        1、创建x264_build.sh并编辑:

    sudo gedit x264_build.sh

        

        2、在x264_build.sh加入如下代码:

#!/bin/bash
# 用于android平台NDK为android-ndk-r27c交叉编译X264的脚本
 
# NDK所在目录
export NDK=/home/wyy/Android/android-ndk-r27c 
export TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
export SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot

export ANDROID_API=24
export AR=$TOOLCHAIN/bin/llvm-ar

export LD=$TOOLCHAIN/bin/ld
export RANLIB=$TOOLCHAIN/bin/llvm-ranlib
export STRIP=$TOOLCHAIN/bin/llvm-strip
export NM=$TOOLCHAIN/bin/llvm-nm
#export STRINGS=$TOOLCHAIN/llvm-strings


build_armv7(){
  echo "-------- > Start install build_armv7"
  export CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$ANDROID_API-clang
  export CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$ANDROID_API-clang++
  
    #编译选项
  EXTRA_CFLAGS="-march=armv7-a -D__ANDROID__"
  
  
  PREFIX=`pwd`/android/arm-v7a
  
  # 配置和编译
  ./configure \
  --host=armv7a-linux-androideabi \
  --sysroot=${SYSROOT} \
  --prefix=${PREFIX} \
  --enable-shared \
  --enable-static \
  --disable-asm \
  --chroma-format=all \
  --enable-pic \
  --enable-strip \
  --disable-cli \
  --disable-win32thread \
  --disable-avs \
  --disable-swscale \
  --disable-lavf \
  --disable-ffms \
  --disable-gpac \
  --disable-lsmash \
  --extra-cflags="-Os -fpic ${EXTRA_CFLAGS}" \
  --extra-ldflags="" \

  make clean
  make -j4
  make install

}

build_armv8(){

  echo "-------- > Start install build_armv8"
  
  export CC=$TOOLCHAIN/bin/aarch64-linux-android$ANDROID_API-clang
  export CXX=$TOOLCHAIN/bin/aarch64-linux-android$ANDROID_API-clang++
  
  #编译选项
EXTRA_CFLAGS="-march=armv8-a -D__ANDROID__"
  
  PREFIX=`pwd`/android/arm64-v8a
  # 配置和编译
  ./configure \
  --host=aarch64-linux-android \
  --sysroot=${SYSROOT} \
  --prefix=${PREFIX} \
  --enable-shared \
  --enable-static \
  --disable-asm \
  --chroma-format=all \
  --enable-pic \
  --enable-strip \
  --disable-cli \
  --disable-win32thread \
  --disable-avs \
  --disable-swscale \
  --disable-lavf \
  --disable-ffms \
  --disable-gpac \
  --disable-lsmash \
  --extra-cflags="-Os -fpic ${EXTRA_CFLAGS}" \
  --extra-ldflags="" \

  make clean
  make -j4
  make install

}


build_x86(){

  echo "-------- > Start install build_x86"
  
  export CC=$TOOLCHAIN/bin/i686-linux-android$ANDROID_API-clang
  export CXX=$TOOLCHAIN/bin/i686-linux-android$ANDROID_API-clang++
  
  #编译选项
  EXTRA_CFLAGS="-D__ANDROID__"
  
  PREFIX=`pwd`/android/x86
  # 配置和编译
  ./configure \
  --host=i686-linux-android \
  --sysroot=${SYSROOT} \
  --prefix=${PREFIX} \
  --enable-shared \
  --enable-static \
  --chroma-format=all \
  --disable-asm \
  --enable-pic \
  --enable-strip \
  --disable-cli \
  --disable-win32thread \
  --disable-avs \
  --disable-swscale \
  --disable-lavf \
  --disable-ffms \
  --disable-gpac \
  --disable-lsmash \
  --extra-cflags="-Os -fpic ${EXTRA_CFLAGS}" \
  --extra-ldflags="" \

  make clean
  make -j4
  make install

}


build_x86_64(){

  echo "-------- > Start install build_x86_64"
  export CC=$TOOLCHAIN/bin/x86_64-linux-android$ANDROID_API-clang
  export CXX=$TOOLCHAIN/bin/x86_64-linux-android$ANDROID_API-clang++
  
  #编译选项
  EXTRA_CFLAGS="-D__ANDROID__"
  
  PREFIX=`pwd`/android/x86_64
  # 配置和编译
  ./configure \
  --host=x86_64-linux-android \
  --sysroot=${SYSROOT} \
  --prefix=${PREFIX} \
  --enable-shared \
  --enable-static \
  --disable-asm \
  --chroma-format=all \
  --enable-pic \
  --enable-strip \
  --disable-cli \
  --disable-win32thread \
  --disable-avs \
  --disable-swscale \
  --disable-lavf \
  --disable-ffms \
  --disable-gpac \
  --disable-lsmash \
  --extra-cflags="-Os -fpic ${EXTRA_CFLAGS}" \
  --extra-ldflags="" \

  make clean
  make -j4
  make install

}

build_armv7
build_armv8
build_x86
build_x86_64

        3、在X264的源码目录下,开启x264_build.sh的编译:

    sudo sh x264_build.sh 

四、NDK交叉编译X264的结果:

        交叉编译的资源地址在:https://download.csdn.net/download/wangyongyao1989/91731308

        1、在输入“PREFIX=`pwd`/android/”的目录下生成如下的结果:

        2、在每个CPU架构的文件夹下,又有“include”和“lib”:

        3、“include”文件夹下存放了两个头文件:

        4、“lib”文件夹下存放了编译出来的lib库:

        5、“pkgconfig”

        pkg-config 文件是一个元数据文件,它的核心用途是告诉编译器和链接器如何正确地编译和链接依赖于 x264库的程序。它解决了在编译软件时指定头文件路径和库文件路径的麻烦。

        pkgconfig工具自动完成的工作:

  • 查找文件:它会根据系统环境,自动找到名为x264.pc的文件。

  • 解析信息:从x264.pc文件中提取出必要的编译和链接信息。

  • 输出参数

               --cflags 选项会输出类似于 -I/usr/local/include 这样的参数。

               --libs 选项会输出类似于 -L/usr/local/lib -lx264 这样的参数。

        6、坑:

        虽然编译出了上述的编译结果,但后面把libx264加入ffmpeg交叉编译时。会有一个巨坑,就是libx264动态库的链接是链接到“libx264.so.165”而不是链接到“libx264.so”。

在android的动态库的链接时会报错“dlopen failed: library "libx264.so.165" not found”

        替换后在重新编译,完成的结果就不包含“libx264.so.165”。

五、在编译FFmpeg过程中加入第三方X264库编译:

        此次编译是基于文章:Android Liunx ffmpeg交叉编译_交叉编译ffmpeg-CSDN博客 的android_build1.sh基础上进行改造编译的,感兴趣的可以翻回去回顾一下具体内容。

        1、准备好FFmpeg的源码:

        在文章Android Liunx ffmpeg交叉编译_交叉编译ffmpeg-CSDN博客中已经详细的描述了,FFmpeg的下载及配置的全部过程。

        2、准备编译好的x264库:

        前面内容也介绍了,x264下载编译的过程。需要的是NDK交叉编译X264库的结果。

        3、编写编译脚本:

        启动x264编码器的设置:

        在FFmpeg的configure设置打开“--enable-libx264”及“--enable-encoder=libx264”启动x264编码器的设置。


  #libx264
  CONFIGURATION="$CONFIGURATION --enable-libx264"
  CONFIGURATION="$CONFIGURATION --enable-encoder=libx264"

        指定X264的pkgconfig的路径:

        这里指定路径时,根据不同平台设置对应的路径,如是arm64-v8a则是例如下:

    export PKG_CONFIG_PATH=/home/wyy/x264/x264/android/arm64-v8a/lib/pkgconfig

        同时也要在configure的设置“--pkg-config”设定库的类型,如下:

    ./configure --pkg-config="pkg-config --static"

        指定X264的include和lib路径:

        找到include和lib路径,用于第三方库的设定,如:     

    #编译x264 arm64-v8a结果的目录地址
    X264_INCLUDE="/home/wyy/x264/x264/android/arm64-v8a/include"
    X264_LIB="/home/wyy/x264/x264/android/arm64-v8a/lib"

    EXTRA_CFLAGS="$EXTRA_CFLAGS -I$X264_INCLUDE"
    EXTRA_LDFLAGS="$EXTRA_LDFLAGS -L$X264_LIB"


    ./configure --extra-cflags="$EXTRA_CFLAGS" --extra-ldflags="$EXTRA_LDFLAGS" 

        android_x264_build.sh编译脚本:

        下面是脚本的全部代码:

        

#!/bin/bash
# 用于编译android平台的脚本

# NDK所在目录
NDK_PATH=/home/wyy/Android/android-ndk-r27c
# 主机平台
HOST_PLATFORM=linux-x86_64
# minSdkVersion
API=24  # 提高到 API 24 以避免 fseeko64/ftello64 问题

TOOLCHAINS="$NDK_PATH/toolchains/llvm/prebuilt/$HOST_PLATFORM"
SYSROOT="$NDK_PATH/toolchains/llvm/prebuilt/$HOST_PLATFORM/sysroot"
# 生成 -fpic 与位置无关的代码
CFLAG="-D__ANDROID_API__=$API -Os -fPIC -DANDROID -D_FILE_OFFSET_BITS=64"
LDFLAG="-lc -lm -ldl -llog "

# 输出目录
PREFIX=$(pwd)/android
# 日志输出目录
CONFIG_LOG_PATH=${PREFIX}/log
# 公共配置
COMMON_OPTIONS=
# 交叉配置
CONFIGURATION=

# 创建必要的目录
mkdir -p ${PREFIX} ${CONFIG_LOG_PATH}

build() {
  APP_ABI=$1
  echo "======== > Start build $APP_ABI"
  
  # 重置变量
  EXTRA_CFLAGS="$CFLAG"
  EXTRA_LDFLAGS="$LDFLAG"
  EXTRA_OPTIONS=""
  PKG_CONFIG_AVAILABLE=false
  
  case ${APP_ABI} in
  armeabi-v7a)
    ARCH="arm"
    CPU="armv7-a"
    MARCH="armv7-a"
    TARGET=armv7a-linux-androideabi
    CC="$TOOLCHAINS/bin/$TARGET$API-clang"
    CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
    LD="$TOOLCHAINS/bin/$TARGET$API-clang"
    # 交叉编译工具前缀
    CROSS_PREFIX="$TOOLCHAINS/bin/arm-linux-androideabi-"
    
    # 设置pkg-config路径
    export PKG_CONFIG_PATH="/home/wyy/x264/x264/android/arm-v7a/lib/pkgconfig"
    echo "-------- > PKG_CONFIG_PATH=$PKG_CONFIG_PATH"
    
    # 指定X264的库
    X264_INCLUDE="/home/wyy/x264/x264/android/arm-v7a/include"
    X264_LIB="/home/wyy/x264/x264/android/arm-v7a/lib"
    echo "-------- > X264_INCLUDE=$X264_INCLUDE"
    echo "-------- > X264_LIB=$X264_LIB"
    
    # 检查x264库是否存在
    if [ ! -f "$X264_LIB/libx264.so" ]; then
        echo "错误: 找不到x264共享库文件在 $X264_LIB"
        echo "请先编译x264库或检查路径是否正确"
        exit 1
    fi
    
    # 检查x264.pc文件是否存在
    local x264_pc_file="$X264_LIB/pkgconfig/x264.pc"
    if [ -f "$x264_pc_file" ]; then
        echo "-------- > x264.pc found"
        # 尝试使用pkg-config获取正确的编译标志
        if pkg-config --exists x264; then
            X264_CFLAGS=$(pkg-config --cflags x264)
            X264_LIBS=$(pkg-config --libs x264)
            echo "-------- > pkg-config found x264 successfully"
            echo "-------- > x264 CFLAGS: $X264_CFLAGS"
            echo "-------- > x264 LIBS: $X264_LIBS"
            EXTRA_CFLAGS="$EXTRA_CFLAGS $X264_CFLAGS"
            EXTRA_LDFLAGS="$EXTRA_LDFLAGS $X264_LIBS"
            PKG_CONFIG_AVAILABLE=true
        else
            echo "-------- > WARNING: pkg-config cannot find x264, using manual flags"
            EXTRA_CFLAGS="$EXTRA_CFLAGS -I$X264_INCLUDE"
            EXTRA_LDFLAGS="$EXTRA_LDFLAGS -L$X264_LIB -lx264"
        fi
    else
        echo "-------- > WARNING: x264.pc not found, using manual flags"
        EXTRA_CFLAGS="$EXTRA_CFLAGS -I$X264_INCLUDE"
        EXTRA_LDFLAGS="$EXTRA_LDFLAGS -L$X264_LIB -lx264"
    fi
    
    # 添加架构特定标志
    EXTRA_CFLAGS="$EXTRA_CFLAGS -mfloat-abi=softfp -mfpu=neon -marm -march=$MARCH"
    EXTRA_OPTIONS="--enable-neon --cpu=$CPU"
    ;;
    
  arm64-v8a)
    ARCH="aarch64"
    CPU="armv8-a"
    TARGET=aarch64-linux-android
    CC="$TOOLCHAINS/bin/$TARGET$API-clang"
    CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
    LD="$TOOLCHAINS/bin/$TARGET$API-clang"
    CROSS_PREFIX="$TOOLCHAINS/bin/aarch64-linux-android-"
    
    # 设置pkg-config路径
    export PKG_CONFIG_PATH="/home/wyy/x264/x264/android/arm64-v8a/lib/pkgconfig"
    
    # 指定X264的库
    X264_INCLUDE="/home/wyy/x264/x264/android/arm64-v8a/include"
    X264_LIB="/home/wyy/x264/x264/android/arm64-v8a/lib"
    
    # 检查x264库是否存在
    if [ ! -f "$X264_LIB/libx264.so" ]; then
        echo "错误: 找不到x264共享库文件在 $X264_LIB"
        exit 1
    fi
    
    # 手动添加x264标志
    EXTRA_CFLAGS="$EXTRA_CFLAGS -I$X264_INCLUDE"
    EXTRA_LDFLAGS="$EXTRA_LDFLAGS -L$X264_LIB -lx264"
    ;;
    
  x86)
    ARCH="x86"
    CPU="i686"
    TARGET=i686-linux-android
    CC="$TOOLCHAINS/bin/$TARGET$API-clang"
    CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
    LD="$TOOLCHAINS/bin/$TARGET$API-clang"
    CROSS_PREFIX="$TOOLCHAINS/bin/i686-linux-android-"
    
    # 禁用不支持的选项
    EXTRA_OPTIONS="--disable-neon --disable-asm"
    ;;
    
  x86_64)
    ARCH="x86_64"
    CPU="x86_64"
    TARGET=x86_64-linux-android
    CC="$TOOLCHAINS/bin/$TARGET$API-clang"
    CXX="$TOOLCHAINS/bin/$TARGET$API-clang++"
    LD="$TOOLCHAINS/bin/$TARGET$API-clang"
    CROSS_PREFIX="$TOOLCHAINS/bin/x86_64-linux-android-"
    
    # 禁用不支持的选项
    EXTRA_OPTIONS="--disable-neon"
    ;;
    
  esac

  echo "-------- > Start clean workspace"
  make clean > /dev/null 2>&1

  echo "-------- > Start build configuration"
  CONFIGURATION="$COMMON_OPTIONS"
  CONFIGURATION="$CONFIGURATION --logfile=$CONFIG_LOG_PATH/config_$APP_ABI.log"
  CONFIGURATION="$CONFIGURATION --prefix=$PREFIX"
  CONFIGURATION="$CONFIGURATION --libdir=$PREFIX/libs/$APP_ABI"
  CONFIGURATION="$CONFIGURATION --incdir=$PREFIX/includes/$APP_ABI"
  CONFIGURATION="$CONFIGURATION --pkgconfigdir=$PREFIX/pkgconfig/$APP_ABI"
  CONFIGURATION="$CONFIGURATION --cross-prefix=$CROSS_PREFIX"
  CONFIGURATION="$CONFIGURATION --arch=$ARCH"
  CONFIGURATION="$CONFIGURATION --sysroot=$SYSROOT"
  CONFIGURATION="$CONFIGURATION --cc=$CC"
  CONFIGURATION="$CONFIGURATION --cxx=$CXX"
  CONFIGURATION="$CONFIGURATION --ld=$LD"
  #libx264
  CONFIGURATION="$CONFIGURATION --enable-libx264"
  CONFIGURATION="$CONFIGURATION --enable-encoder=libx264"
  # nm 和 strip
  CONFIGURATION="$CONFIGURATION --nm=$TOOLCHAINS/bin/llvm-nm"
  CONFIGURATION="$CONFIGURATION --strip=$TOOLCHAINS/bin/llvm-strip"
  CONFIGURATION="$CONFIGURATION $EXTRA_OPTIONS"
  

  
  echo "-------- > Start config makefile with $CONFIGURATION"
  echo "-------- > Extra CFLAGS: $EXTRA_CFLAGS"
  echo "-------- > Extra LDFLAGS: $EXTRA_LDFLAGS"
  
  ./configure ${CONFIGURATION} \
    --extra-cflags="$EXTRA_CFLAGS" \
    --extra-ldflags="$EXTRA_LDFLAGS" \
    --pkg-config=$(which pkg-config) 2>&1 | tee $CONFIG_LOG_PATH/configure_$APP_ABI.log
    
  echo "-------- > Start make $APP_ABI with -j4"
  make -j4
 
  echo "-------- > Start install $APP_ABI"
  make install
  
  echo "++++++++ > make and install $APP_ABI complete."
  return 0
}

build_all() {
  #配置开源协议声明
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-gpl"
  #目标android平台
  COMMON_OPTIONS="$COMMON_OPTIONS --target-os=android"
  #取消默认的静态库
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-static"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-shared"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-protocols"
  #开启交叉编译
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-cross-compile"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-optimizations"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-debug"
  #尽可能小
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-small"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-doc"
  #不要命令(执行文件)
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-programs"  # do not build command line programs
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-ffmpeg"    # disable ffmpeg build
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-ffplay"    # disable ffplay build
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-ffprobe"   # disable ffprobe build
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-symver"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-network"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-x86asm"
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-vulkan"
  
  # 有条件地禁用asm,某些架构可能不支持
  COMMON_OPTIONS="$COMMON_OPTIONS --disable-asm"
  
  #启用
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-pthreads"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-mediacodec"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-jni"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-zlib"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-pic"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-muxer=flv"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=h264"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=mpeg4"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=mjpeg"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=png"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=vorbis"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=opus"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-decoder=flac"
  
  #启用编码器
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-encoder=h264"
  
  #libx264
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-libx264"
  COMMON_OPTIONS="$COMMON_OPTIONS --enable-encoder=libx264"
  
  echo "COMMON_OPTIONS=$COMMON_OPTIONS"
  echo "PREFIX=$PREFIX"
  echo "CONFIG_LOG_PATH=$CONFIG_LOG_PATH"

  # 构建所有支持的架构
  build "armeabi-v7a" || echo "Failed to build armeabi-v7a"
  build "arm64-v8a" || echo "Failed to build arm64-v8a"
  # build "x86" || echo "Failed to build x86"
  # build "x86_64" || echo "Failed to build x86_64"
}

echo "-------- Start --------"
build_all
echo "-------- End --------"

             

   4、编译结果及验证:     

       编译后的结果放在:https://download.csdn.net/download/wangyongyao1989/91765163资源中。

        在本人github:GitHub - wangyongyao1989/FFmpegPractices at dev8项目中的

        练习十四:使用libx264编码器“对视频流重新编码(使用libx264的编码器)”得到验证。

        对应的代码如下:

 //使用libx264的编码器
 AVCodec *video_codec = (AVCodec *) avcodec_find_encoder_by_name("libx264");

完成的c++代码如下:

//
// Created by wangyao on 2025/8/17.
//

#include "includes/RecodecVideo.h"

RecodecVideo::RecodecVideo() {

}

RecodecVideo::~RecodecVideo() {
    if (in_fmt_ctx) {
        in_fmt_ctx = nullptr;
    }
    if (video_encode_ctx) {
        video_encode_ctx = nullptr;
    }
    video_index = -1;
    audio_index = -1;
    if (src_video) {
        src_video = nullptr;
    }
    if (src_audio) {
        src_audio = nullptr;
    }
    if (dest_video) {
        dest_video = nullptr;
    }
    if (out_fmt_ctx) {
        out_fmt_ctx = nullptr;
    }
    if (video_encode_ctx) {
        video_encode_ctx = nullptr;
    }
}

string RecodecVideo::recodecVideo(const char *srcPath, const char *destPath) {
    if (open_input_file(srcPath) < 0) { // 打开输入文件
        return recodecInfo;
    }
    if (open_output_file(srcPath) < 0) { // 打开输出文件
        return recodecInfo;
    }
    int ret = -1;
    AVPacket *packet = av_packet_alloc(); // 分配一个数据包
    AVFrame *frame = av_frame_alloc(); // 分配一个数据帧
    while (av_read_frame(in_fmt_ctx, packet) >= 0) { // 轮询数据包
        if (packet->stream_index == video_index) { // 视频包需要重新编码
            packet->stream_index = 0;
            recode_video(packet, frame); // 对视频帧重新编码
        } else { // 音频包暂不重新编码,直接写入目标文件
            packet->stream_index = 1;
            ret = av_write_frame(out_fmt_ctx, packet); // 往文件写入一个数据包
            if (ret < 0) {
                LOGE("write frame occur error %d.\n", ret);
                recodecInfo = recodecInfo + "\n write frame occur error:" + to_string(ret);
                break;
            }
        }
        av_packet_unref(packet); // 清除数据包
    }
    packet->data = nullptr; // 传入一个空包,冲走解码缓存
    packet->size = 0;
    recode_video(packet, frame); // 对视频帧重新编码
    output_video(nullptr); // 传入一个空帧,冲走编码缓存
    av_write_trailer(out_fmt_ctx); // 写文件尾
    LOGI("Success recode file.\n");
    recodecInfo = recodecInfo + "\n Success recode file!!!!!!";

    av_frame_free(&frame); // 释放数据帧资源
    av_packet_free(&packet); // 释放数据包资源
    avio_close(out_fmt_ctx->pb); // 关闭输出流
    avcodec_close(video_decode_ctx); // 关闭视频解码器的实例
    avcodec_free_context(&video_decode_ctx); // 释放视频解码器的实例
    avcodec_close(video_encode_ctx); // 关闭视频编码器的实例
    avcodec_free_context(&video_encode_ctx); // 释放视频编码器的实例
    avformat_free_context(out_fmt_ctx); // 释放封装器的实例
    avformat_close_input(&in_fmt_ctx); // 关闭音视频文件
    return recodecInfo;
}

// 打开输入文件
int RecodecVideo::open_input_file(const char *src_name) {
    // 打开音视频文件
    int ret = avformat_open_input(&in_fmt_ctx, src_name, nullptr, nullptr);
    if (ret < 0) {
        LOGE("Can't open file %s.\n", src_name);
        recodecInfo = recodecInfo + "\n Can't open file :" + string(src_name);
        return -1;
    }
    LOGI("Success open input_file %s.\n", src_name);
    recodecInfo = recodecInfo + "\n Success open input_file:" + string(src_name);
    // 查找音视频文件中的流信息
    ret = avformat_find_stream_info(in_fmt_ctx, nullptr);
    if (ret < 0) {
        LOGE("Can't find stream information.\n");
        recodecInfo = recodecInfo + "\n Can't find stream information. ";
        return -1;
    }
    // 找到视频流的索引
    video_index = av_find_best_stream(in_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
    if (video_index >= 0) {
        src_video = in_fmt_ctx->streams[video_index];
        enum AVCodecID video_codec_id = src_video->codecpar->codec_id;
        // 查找视频解码器
        AVCodec *video_codec = (AVCodec *) avcodec_find_decoder(video_codec_id);
        if (!video_codec) {
            LOGE("video_codec not found\n");
            recodecInfo = recodecInfo + "\n video_codec not found. ";
            return -1;
        }
        video_decode_ctx = avcodec_alloc_context3(video_codec); // 分配解码器的实例
        if (!video_decode_ctx) {
            LOGE("video_decode_ctx is nullptr\n");
            recodecInfo = recodecInfo + "\n video_decode_ctx is nullptr ";
            return -1;
        }
        // 把视频流中的编解码参数复制给解码器的实例
        avcodec_parameters_to_context(video_decode_ctx, src_video->codecpar);
        ret = avcodec_open2(video_decode_ctx, video_codec, nullptr); // 打开解码器的实例
        if (ret < 0) {
            LOGE("Can't open video_decode_ctx.\n");
            recodecInfo = recodecInfo + "\n Can't open video_decode_ctx";
            return -1;
        }
    } else {
        LOGE("Can't find video stream.\n");
        recodecInfo = recodecInfo + "\n Can't find video stream.";
        return -1;
    }
    // 找到音频流的索引
    audio_index = av_find_best_stream(in_fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
    if (audio_index >= 0) {
        src_audio = in_fmt_ctx->streams[audio_index];
    }
    return 0;
}

// 给视频帧编码,并写入压缩后的视频包
int RecodecVideo::output_video(AVFrame *frame) {
    // 把原始的数据帧发给编码器实例
    int ret = avcodec_send_frame(video_encode_ctx, frame);
    if (ret < 0) {
        LOGE("send frame occur error %d.\n", ret);
        recodecInfo = recodecInfo + "\n send frame occur error" + to_string(ret);
        return ret;
    }
    while (1) {
        AVPacket *packet = av_packet_alloc(); // 分配一个数据包
        // 从编码器实例获取压缩后的数据包
        ret = avcodec_receive_packet(video_encode_ctx, packet);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            return (ret == AVERROR(EAGAIN)) ? 0 : 1;
        } else if (ret < 0) {
            LOGE("encode frame occur error %d.\n", ret);
            recodecInfo = recodecInfo + "\n encode frame occur error:" + to_string(ret);
            break;
        }
        // 把数据包的时间戳从一个时间基转换为另一个时间基
        av_packet_rescale_ts(packet, src_video->time_base, dest_video->time_base);
//        LOGI( "pts=%ld, dts=%ld.\n", packet->pts, packet->dts);
        packet->stream_index = 0;
        ret = av_write_frame(out_fmt_ctx, packet); // 往文件写入一个数据包
        if (ret < 0) {
            LOGE("write frame occur error %d.\n", ret);
            recodecInfo = recodecInfo + "\n write frame occur error:" + to_string(ret);
            break;
        }
        av_packet_unref(packet); // 清除数据包
    }
    return ret;
}

// 对视频帧重新编码
int RecodecVideo::recode_video(AVPacket *packet, AVFrame *frame) {
    // 把未解压的数据包发给解码器实例
    int ret = avcodec_send_packet(video_decode_ctx, packet);
    if (ret < 0) {
        LOGE("send packet occur error %d.\n", ret);
        recodecInfo = recodecInfo + "\n send packet occur error:" + to_string(ret);
        return ret;
    }
    while (1) {
        // 从解码器实例获取还原后的数据帧
        ret = avcodec_receive_frame(video_decode_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            return (ret == AVERROR(EAGAIN)) ? 0 : 1;
        } else if (ret < 0) {
            LOGE("decode frame occur error %d.\n", ret);
            recodecInfo = recodecInfo + "\n decode frame occur error :" + to_string(ret);
            break;
        }
        if (frame->pts == AV_NOPTS_VALUE) { // 对H.264裸流做特殊处理
            double interval = 1.0 / av_q2d(src_video->r_frame_rate);
            frame->pts = count * interval / av_q2d(src_video->time_base);
            count++;
        }
        output_video(frame); // 给视频帧编码,并写入压缩后的视频包
    }
    return ret;
}

int RecodecVideo::open_output_file(const char *dest_name) {
    // 分配音视频文件的封装实例
    int ret = avformat_alloc_output_context2(&out_fmt_ctx, nullptr, nullptr, dest_name);
    if (ret < 0) {
        LOGE("Can't alloc output_file %s.\n", dest_name);
        recodecInfo = recodecInfo + "\n Can't alloc output_file :" + string(dest_name);
        return -1;
    }
    // 打开输出流
    ret = avio_open(&out_fmt_ctx->pb, dest_name, AVIO_FLAG_READ_WRITE);
    if (ret < 0) {
        LOGE("Can't open output_file %s.\n", dest_name);
        recodecInfo = recodecInfo + "\n Can't open output_file:" + string(dest_name);
        return -1;
    }
    LOGI("Success open output_file %s.\n", dest_name);
    recodecInfo = recodecInfo + "\n Success open output_file :" + string(dest_name);
    if (video_index >= 0) { // 创建编码器实例和新的视频流
        enum AVCodecID video_codec_id = src_video->codecpar->codec_id;
        // 查找视频编码器
//        AVCodec *video_codec = (AVCodec *) avcodec_find_encoder(video_codec_id);
        //使用libx264的编码器
        AVCodec *video_codec = (AVCodec *) avcodec_find_encoder_by_name("libx264");
        if (!video_codec) {
            LOGE("video_codec not found\n");
            recodecInfo = recodecInfo + "\n video_codec not found .";
            return -1;
        }
        video_encode_ctx = avcodec_alloc_context3(video_codec); // 分配编码器的实例
        if (!video_encode_ctx) {
            LOGE("video_encode_ctx is null\n");
            recodecInfo = recodecInfo + "\n video_encode_ctx is null";
            return -1;
        }
        // 把源视频流中的编解码参数复制给编码器的实例
        avcodec_parameters_to_context(video_encode_ctx, src_video->codecpar);
        // 注意:帧率和时间基要单独赋值,因为avcodec_parameters_to_context没复制这两个参数
        video_encode_ctx->framerate = src_video->r_frame_rate;
        // framerate.num值过大,会导致视频头一秒变灰色
        if (video_encode_ctx->framerate.num > 60) {
            video_encode_ctx->framerate = (AVRational) {25, 1}; // 帧率
        }
        video_encode_ctx->time_base = src_video->time_base;
        video_encode_ctx->gop_size = 12; // 关键帧的间隔距离
        //video_encode_ctx->max_b_frames = 0; // 0表示不要B帧
        // AV_CODEC_FLAG_GLOBAL_HEADER标志允许操作系统显示该视频的缩略图
        if (out_fmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
            video_encode_ctx->flags = AV_CODEC_FLAG_GLOBAL_HEADER;
        }
        ret = avcodec_open2(video_encode_ctx, video_codec, nullptr); // 打开编码器的实例
        if (ret < 0) {
            LOGE("Can't open video_encode_ctx.\n");
            av_strerror(ret, errbuf, sizeof(errbuf)); // 将错误码转换为字符串
            LOGE("avcodec_open2失败:%s\n", errbuf);
            recodecInfo = recodecInfo + "\n avcodec_open2失败:" + string(errbuf);
            return -1;
        }
        dest_video = avformat_new_stream(out_fmt_ctx, nullptr); // 创建数据流
        // 把编码器实例的参数复制给目标视频流
        avcodec_parameters_from_context(dest_video->codecpar, video_encode_ctx);
        // 如果后面有对视频帧转换时间基,这里就无需复制时间基
        //dest_video->time_base = src_video->time_base;
        dest_video->codecpar->codec_tag = 0;
    }
    if (audio_index >= 0) { // 源文件有音频流,就给目标文件创建音频流
        AVStream *dest_audio = avformat_new_stream(out_fmt_ctx, nullptr); // 创建数据流
        // 把源文件的音频参数原样复制过来
        avcodec_parameters_copy(dest_audio->codecpar, src_audio->codecpar);
        dest_audio->codecpar->codec_tag = 0;
    }
    ret = avformat_write_header(out_fmt_ctx, nullptr); // 写文件头
    if (ret < 0) {
        LOGE("write file_header occur error %d.\n", ret);
        recodecInfo = recodecInfo + "\n write file_header occur error :" + to_string(ret);
        av_strerror(ret, errbuf, sizeof(errbuf)); // 将错误码转换为字符串
        recodecInfo = recodecInfo + "\n avformat_write_header 失败:" + string(errbuf);
        return -1;
    }
    LOGI("Success write file_header.\n");
    recodecInfo = recodecInfo + "\n Success write file_header.";
    return 0;
}


       


网站公告

今日签到

点亮在社区的每一天
去签到