天天看點

FFmpeg 開發(01):FFmpeg 編譯和內建為什麼要學 FFmpeg 開發FFmpeg 編譯FFmpeg 內建參考

作者:位元組流動

來源:

https://blog.csdn.net/Kennethdroid/article/details/106956601

為什麼要學 FFmpeg 開發

FFmpeg 是一款知名的開源音視訊處理軟體,它提供了豐富而友好的接口支援開發者進行二次開發。

FFmpeg 讀作 “ef ef em peg” ,其中的 “FF” 指的是 “Fast Forward”,“mpeg” 則是 “Moving Picture Experts Group” (動态圖像專家組)。

FFmpeg 項目功能複雜而龐大,基本上支援所有常見的音視訊處理操作,如封裝格式轉換、音視訊轉碼、音視訊播放和剪輯、視訊添加水印濾鏡等。

盡管 FFmpeg 功能強大,但是由于其采用的是帶有傳染性的 LGPL/GPL 開源協定,是以一些大廠基本上都是自己獨立開發類似的音視訊處理庫,甚至在接口群組織子產品上模仿 FFmpeg 。

是以,學習 FFmpeg 不僅能夠幫助你掌握音視訊開發的相關知識脈絡,還能讓你快速适應不同的音視訊處理架構。

FFmpeg 編譯

FFmpeg 有六個常用的功能子產品:

  • libavformat:多媒體檔案或協定的封裝和解封裝庫,如 Mp4、Flv 等檔案封裝格式,RTMP、RTSP 等網絡協定封裝格式;
  • libavcodec:音視訊編解碼庫;
  • libavfilter:音視訊、字幕濾鏡庫;
  • libswscale:圖像格式轉換庫;
  • libswresample:音頻重采樣庫;
  • libavutil:工具庫;

本文主要是幫助初學者快速上手 FFmpeg 的編譯和內建,對 FFmpeg 項目的編譯配置細節就不過多闡述,這不是本篇内容所能容納。

這裡主要選擇編譯 ffmpeg v4.2.2 版本,因為這個版本網上的解決方案比較多,而且大部分可行。

編譯環境:

  • CentOS Linux release 7.6.1810 (Core)
  • android-ndk-r20b-linux-x86_64
  • ffmpeg-4.2.2

編譯前準備:

//1. 下載下傳 ffmpeg-4.2.2
wget https://ffmpeg.org/releases/ffmpeg-4.2.2.tar.bz2
//2. 解壓 FFmpeg 
tar -jxvf ffmpeg-4.2.2.tar.bz2
//3. 配置項目
./configure --disable-x86asm      
FFmpeg 開發(01):FFmpeg 編譯和內建為什麼要學 FFmpeg 開發FFmpeg 編譯FFmpeg 內建參考

在 FFmpeg 4.2.2 解壓目錄下建立編譯腳本

build_android_arm64-v8a_clang.sh

#!/bin/bash
export NDK=/root/workspace/android-ndk-r20b #這裡配置先你的 NDK 路徑
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
function build_android
{
./configure \
--prefix=$PREFIX \
--enable-neon  \
--enable-hwaccels  \
--enable-gpl   \
--disable-postproc \
--disable-debug \
--enable-small \
--enable-jni \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
--enable-static \
--enable-shared \
--disable-doc \
--enable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-avdevice \
--disable-doc \
--disable-symver \
--cross-prefix=$CROSS_PREFIX \
--target-os=android \
--arch=$ARCH \
--cpu=$CPU \
--cc=$CC \
--cxx=$CXX \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS"
make clean
make -j16
make install
echo "============================ build android arm64-v8a success =========================="
}
#arm64-v8a
ARCH=arm64
CPU=armv8-a
API=21
CC=$TOOLCHAIN/bin/aarch64-linux-android$API-clang
CXX=$TOOLCHAIN/bin/aarch64-linux-android$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-march=$CPU"
build_android      

編譯 FFmpeg Android 平台的 64 位動态庫和靜态庫:

# 修改 build_android_arm64-v8a_clang.sh 可執行權限
chmod +x build_android_arm64-v8a_clang.sh
# 運作編譯腳本
./build_android_arm64-v8a_clang.sh      
FFmpeg 開發(01):FFmpeg 編譯和內建為什麼要學 FFmpeg 開發FFmpeg 編譯FFmpeg 內建參考

編譯成功後會在 android 目錄下生成對應六個子產品的靜态庫和動态庫。

另外,若要編譯成 32 位的庫,則需修改對應的編譯腳本:

#armv7-a
ARCH=arm
CPU=armv7-a
API=21
CC=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang
CXX=$TOOLCHAIN/bin/armv7a-linux-androideabi$API-clang++
SYSROOT=$NDK/toolchains/llvm/prebuilt/linux-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
PREFIX=$(pwd)/android/$CPU
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "      

FFmpeg 內建

基于上節編譯好的 FFmpeg 靜态庫,我們在 Android Studio 上進行簡單的內建測試。

FFmpeg 開發(01):FFmpeg 編譯和內建為什麼要學 FFmpeg 開發FFmpeg 編譯FFmpeg 內建參考

我們可以按照上圖所示,将 FFmpeg 各個子產品的靜态庫和頭檔案放置到指定目錄下,實作一個擷取各個子產品版本資訊的 jni 。

#include <cstdio>
#include <cstring>
#include "util/LogUtil.h"
#include "jni.h"
//由于 FFmpeg 庫是 C 語言實作的,告訴編譯器按照 C 的規則進行編譯
extern "C" {
#include <libavcodec/version.h>
#include <libavcodec/avcodec.h>
#include <libavformat/version.h>
#include <libavutil/version.h>
#include <libavfilter/version.h>
#include <libswresample/version.h>
#include <libswscale/version.h>
};
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_byteflow_learnffmpeg_media_FFMediaPlayer
 * Method:    native_GetFFmpegVersion
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_byteflow_learnffmpeg_media_FFMediaPlayer_native_1GetFFmpegVersion
        (JNIEnv *env, jclass cls)
{
    char strBuffer[1024 * 4] = {0};
    strcat(strBuffer, "libavcodec : ");
    strcat(strBuffer, AV_STRINGIFY(LIBAVCODEC_VERSION));
    strcat(strBuffer, "\nlibavformat : ");
    strcat(strBuffer, AV_STRINGIFY(LIBAVFORMAT_VERSION));
    strcat(strBuffer, "\nlibavutil : ");
    strcat(strBuffer, AV_STRINGIFY(LIBAVUTIL_VERSION));
    strcat(strBuffer, "\nlibavfilter : ");
    strcat(strBuffer, AV_STRINGIFY(LIBAVFILTER_VERSION));
    strcat(strBuffer, "\nlibswresample : ");
    strcat(strBuffer, AV_STRINGIFY(LIBSWRESAMPLE_VERSION));
    strcat(strBuffer, "\nlibswscale : ");
    strcat(strBuffer, AV_STRINGIFY(LIBSWSCALE_VERSION));
    strcat(strBuffer, "\navcodec_configure : \n");
    strcat(strBuffer, avcodec_configuration());
    strcat(strBuffer, "\navcodec_license : ");
    strcat(strBuffer, avcodec_license());
    LOGCATE("GetFFmpegVersion\n%s", strBuffer);
    return env->NewStringUTF(strBuffer);
}
#ifdef __cplusplus
}
#endif      

Java 層的調用邏輯:

package com.byteflow.learnffmpeg.media;
public class FFMediaPlayer {
    static {
        System.loadLibrary("learn-ffmpeg");
    }
    public static String GetFFmpegVersion() {
        return native_GetFFmpegVersion();
    }
    private static native String native_GetFFmpegVersion();
}
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((TextView)findViewById(R.id.text_view)).setText(FFMediaPlayer.GetFFmpegVersion());
    }
}      

CMakeLists.txt 建構腳本:

# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")
set(jnilibs ${CMAKE_SOURCE_DIR}/../jniLibs)
set(libname learn-ffmpeg)
include_directories(
        include
        ${CMAKE_SOURCE_DIR}/util
)
link_directories(
        ${jnilibs}/${ANDROID_ABI})
file(GLOB src-files
        ${CMAKE_SOURCE_DIR}/*.cpp)
add_library( # Sets the name of the library.
             ${libname}
             # Sets the library as a shared library.
             SHARED
             # Provides a relative path to your source file(s).
             ${src-files}
        )
set(third-party-libs
        avformat
        avcodec
        avfilter
        swresample
        swscale
        avutil
        )
set(native-libs
        android
        EGL
        GLESv3
        OpenSLES
        log
        m
        z
        )
target_link_libraries( # Specifies the target library.
                       ${libname}
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib}
                       ${third-party-libs}
                       ${native-libs}
                       )      

編譯完成後,運作 App 擷取 FFmpeg 各個子產品版本和編譯配置資訊。

FFmpeg 開發(01):FFmpeg 編譯和內建為什麼要學 FFmpeg 開發FFmpeg 編譯FFmpeg 內建參考

參考

https://blog.csdn.net/leixiaohua1020 https://juejin.im/post/5e1eace16fb9a02fec66474e
「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。
FFmpeg 開發(01):FFmpeg 編譯和內建為什麼要學 FFmpeg 開發FFmpeg 編譯FFmpeg 內建參考