FFmpeg添加MediaCodec硬編碼

音視頻文章匯總

接到需求响迂,做一個(gè)iOS和Android兩端的編碼測試工具,可選編碼器,分辨率,幀率寇损,碼率控制ABR或CBR,GOP進(jìn)行轉(zhuǎn)碼,查看軟編碼libx264和硬編碼MediaCodec的編碼效率和畫質(zhì)以及查看是否少幀裳食,具體如下:

20220501163338.jpeg

1.gif

Android效果圖


圖片.png

iOS效果圖


圖片.png

可以用ffmpeg自帶的ffmpeg.c中的main函數(shù)來執(zhí)行上面的所選參數(shù)矛市,iOS端,ffmpeg是支持VideoToolBox硬編碼h264和h265,直接傳入所選參數(shù)即可執(zhí)行诲祸,問題是Android端ffmpeg并不支持MediaCodec硬編碼

1.Android端浊吏,通過查看ffmpeg官網(wǎng)發(fā)現(xiàn),ffmpeg只支持mediacodec硬解碼救氯,并不支持mediacodec硬編碼找田,但目前Android手機(jī)是支持硬編碼的,必須自己修改ffmpeg源碼將MediaCodec硬編碼添加到ffmpeg源碼中着憨,如何給ffmpeg添加codec呢墩衙?

圖片.png

查看官網(wǎng),大致分為五步

A.查看libavcodec/avcodec.h中AVCodec結(jié)構(gòu)體,知道我們新加的MediaCodec編碼器有哪些屬性,name,type,id,pix_fmts等

圖片.png

B.編寫自己的編碼器MediaCodec,通過宏定義享扔,取名h264_hlmediacodec,hevc_hlmediacodec分別代表h264和h265的編碼器名稱底桂,根據(jù)此name可以找到編碼器進(jìn)行編碼

// receive_packet modify to encode2
#define DECLARE_HLMEDIACODEC_ENC(short_name, full_name, codec_id, codec_type)                           \
    DECLARE_HLMEDIACODEC_VCLASS(short_name)                                                             \
    AVCodec ff_##short_name##_hlmediacodec_encoder = {                                                  \
        .name = #short_name "_hlmediacodec",                                                            \
        .long_name = full_name " (Ffmpeg MediaCodec NDK)",                                              \
        .type = codec_type,                                                                             \
        .id = codec_id,                                                                                 \
        .priv_class = &ff_##short_name##_hlmediacodec_enc_class,                                        \
        .priv_data_size = sizeof(HLMediaCodecEncContext),                                               \
        .init = hlmediacodec_encode_init,                                                               \
        .encode2 = hlmediacodec_encode_receive_packet,                                                  \
        .close = hlmediacodec_encode_close,                                                             \
        .capabilities = AV_CODEC_CAP_DELAY,                                                             \
        .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP,                      \
        .pix_fmts = (const enum AVPixelFormat[]){AV_PIX_FMT_NV12, AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE}, \
    };
#ifdef CONFIG_H264_HLMEDIACODEC_ENCODER
DECLARE_HLMEDIACODEC_ENC(h264, "H.264", AV_CODEC_ID_H264, AVMEDIA_TYPE_VIDEO)
#endif
#ifdef CONFIG_HEVC_HLMEDIACODEC_ENCODER
DECLARE_HLMEDIACODEC_ENC(hevc, "H.265", AV_CODEC_ID_HEVC, AVMEDIA_TYPE_VIDEO)
#endif

C.libavcodec/avcodec.h中要有自己的編碼器的id,上面?zhèn)魅氲腁V_CODEC_ID_H264惧眠,AV_CODEC_ID_HEVC在avcodec.h中本來就有

圖片.png

圖片.png

D.libavcodec/allcodecs.c中導(dǎo)出新添加的編碼器ff_h264_hlmediacodec_encoder,ff_hevc_hlmediacodec_encoder,這樣獲取所有的編碼器能輸出ff_h264_hlmediacodec_encoder和ff_hevc_hlmediacodec_encoder

圖片.png

E.libavcodec/Makefile中添加新加的文件,編譯到ffmpeg庫中,編譯的時(shí)候才會將這些新增的文件添加到ffmpeg庫中

OBJS-$(CONFIG_HLMEDIACODEC)            += hlmediacodec.o hlmediacodec_codec.o
OBJS-$(CONFIG_AAC_HLMEDIACODEC_DECODER) += hlmediacodec_dec.o
OBJS-$(CONFIG_MP3_HLMEDIACODEC_DECODER) += hlmediacodec_dec.o
OBJS-$(CONFIG_H264_HLMEDIACODEC_DECODER) += hlmediacodec_dec.o
OBJS-$(CONFIG_H264_HLMEDIACODEC_ENCODER) += hlmediacodec_enc.o
OBJS-$(CONFIG_HEVC_HLMEDIACODEC_DECODER) += hlmediacodec_dec.o
OBJS-$(CONFIG_HEVC_HLMEDIACODEC_ENCODER) += hlmediacodec_enc.o
OBJS-$(CONFIG_MPEG4_HLMEDIACODEC_DECODER) += hlmediacodec_dec.o
OBJS-$(CONFIG_VP8_HLMEDIACODEC_DECODER) += hlmediacodec_dec.o
OBJS-$(CONFIG_VP9_HLMEDIACODEC_DECODER) += hlmediacodec_dec.o
SKIPHEADERS-$(CONFIG_HLMEDIACODEC)     += hlmediacodec.h hlmediacodec_codec.h

2.編譯的時(shí)候可以直接執(zhí)行原始腳本編譯嘛籽懦?答案是不是能的,需要修改腳本氛魁,我們需要在configure中打開硬件加速和新增的MediaCodec編碼器,并且在鏈接外部庫中新增鏈接libmediandk.so暮顺,如果不添加,則會編譯報(bào)錯秀存,找不到MediaCodec的庫捶码,

--enable-mediacodec
--enable-hlmediacodec
--enable-hwaccels
--enable-decoder=h264_mediacodec
--enable-encoder=h264_mediacodec
--enable-decoder=hevc_mediacodec
--enable-decoder=mpeg4_mediacodec
--enable-encoder=mpeg4_mediacodec
--enable-hwaccel=h264_mediacodec
--enable-encoder=h264_hlmediacodec
最后鏈接ndk中的libmediandk.so庫文件,通過指定libmediandk.so庫路徑或链,這一步的實(shí)質(zhì)是就是編譯的時(shí)候再M(fèi)ac環(huán)境下模擬出Android MediaCodec的硬編碼環(huán)境#libmediandk.so路徑
MEDIA_NDK_LIB=$TOOLCHAIN/sysroot/usr/lib/aarch64-linux-android/21
Android的模擬環(huán)境都在ndk路徑下android-ndk-r20b惫恼,armv7和arm64分別對應(yīng)不同的路徑,這個(gè)涉及到Android腳本編譯澳盐,后面再寫祈纯,只有真正編譯過一次才知道其對應(yīng)關(guān)系

圖片.png

#!/bin/bash

echo ">>>>>>>>> 編譯ffmpeg <<<<<<<<"

#NDK路徑.
export NDK=/Users/cloud/Library/android-ndk-r20b
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64

#如果只需要單獨(dú)的ffmpeg,不需要依賴x264叼耙,去掉$ADD_H264_FEATURE這句就可以了腕窥;
#如果你需要的是動態(tài)庫,--enable-static 改為 --disable-static筛婉,--disable-shared 改為 --enable-shared

function build_android
{

echo "開始編譯 $CPU"

./configure \
--prefix=$PREFIX \
--enable-neon  \
--enable-mediacodec \
--enable-hlmediacodec \
--enable-hwaccels  \
--enable-decoder=h264_mediacodec \
--enable-encoder=h264_mediacodec \
--enable-decoder=hevc_mediacodec \
--enable-decoder=mpeg4_mediacodec \
--enable-encoder=mpeg4_mediacodec \
--enable-hwaccel=h264_mediacodec \
--enable-encoder=h264_hlmediacodec \
--enable-gpl   \
--enable-postproc \
--enable-avresample \
--enable-avdevice \
--enable-pic \
--disable-shared \
--enable-debug \
--disable-yasm \
--enable-zlib \
--disable-bzlib \
--disable-iconv \
--disable-optimizations \
--disable-stripping \
--enable-small \
--enable-jni \
--enable-static \
--disable-doc \
--enable-ffmpeg \
--enable-ffplay \
--enable-ffprobe \
--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" \
$ADD_H264_FEATURE \
$ADD_FDK_AAC_FEATURE \
$ADD_MEDIA_NDK_SO


make clean
make -j8
make install

echo "編譯完成 $CPU"

}

#x264庫所在的位置簇爆,ffmpeg 需要鏈接 x264
X264_LIB_DIR=/Users/cloud/Documents/iOS/ego/FFmpeg/Android_sh/x264-snapshot-20191217-2245-stable/android/arm64-v8a;
FDK_AAC_LIB_DIR=/Users/cloud/Documents/iOS/ego/FFmpeg/Android_sh/fdk-aac-2.0.2/android/armv8-a;

#x264的頭文件地址
X264_INC="$X264_LIB_DIR/include"
FDK_AAC_INC="$FDK_AAC_LIB_DIR/include"

#x264的靜態(tài)庫地址
X264_LIB="$X264_LIB_DIR/lib"
FDK_AAC_LIB="$FDK_AAC_LIB_DIR/lib"

#libmediandk.so路徑
MEDIA_NDK_LIB=$TOOLCHAIN/sysroot/usr/lib/aarch64-linux-android/21

ADD_H264_FEATURE="--enable-gpl \
    --enable-libx264 \
    --enable-encoder=libx264 \
    --extra-cflags=-I$X264_INC $OPTIMIZE_CFLAGS \
    --extra-ldflags=-L$X264_LIB $ADDI_LDFLAGS "
    
ADD_FDK_AAC_FEATURE="--enable-libfdk-aac \
    --enable-nonfree \
    --extra-cflags=-I$FDK_AAC_INC $OPTIMIZE_CFLAGS \
    --extra-ldflags=-L$FDK_AAC_LIB $ADDI_LDFLAGS "
    
ADD_MEDIA_NDK_SO="--extra-ldflags=-L$MEDIA_NDK_LIB \
--extra-libs=-lmediandk "

#ADD_H264_FDK_AAC_FEATURE="--enable-encoder=aac \
#    --enable-decoder=aac \
#    --enable-gpl \
#    --enable-encoder=libx264 \
#    --enable-libx264 \
#    --enable-libfdk-aac \
#    --enable-encoder=libfdk-aac \
#    --enable-nonfree \
#    --extra-cflags=-I$X264_INC -I$FDK_AAC_INC \
#    --extra-ldflags=-lm -L$X264_LIB -L$FDK_AAC_LIB $ADDI_LDFLAGS "
#armv8-a
ARCH=aarch64
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/darwin-x86_64/sysroot
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
PREFIX=$(pwd)/android/$CPU
#OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "
build_android

執(zhí)行腳本命令同時(shí)輸出log文件方便排錯sh build_arm64.sh > /Users/cloud/Desktop/0.log,編譯成功生成.a靜態(tài)庫,我這兒是將armv7和arm64分開執(zhí)行的入蛆,也分開合并成.so文件

圖片.png

執(zhí)行合并.so的腳本union_ffmpeg_so_armv8.sh响蓉,將libx264,fdk-aac和ffmpeg中的.a合并為libffmpeg.so文件

echo "開始編譯ffmpeg so"

#NDK路徑.
export NDK=/Users/cloud/Library/android-ndk-r20b

PLATFORM=$NDK/platforms/android-21/arch-arm64
TOOLCHAIN=$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64
TOOL=$NDK/toolchains/llvm/prebuilt/darwin-x86_64

PREFIX=$(pwd)

#如果不需要依賴x264,去掉/usr/x264/x264-master/android/armeabi-v7a/lib/libx264.a \就可以了

$TOOLCHAIN/bin/aarch64-linux-android-ld \
-rpath-link=$PLATFORM/usr/lib \
-L$PLATFORM/usr/lib \
-L$PREFIX/lib \
-soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o \
$PREFIX/libffmpeg.so \
    libavcodec.a \
    libavfilter.a \
    libswresample.a \
    libavformat.a \
    libavutil.a \
    libpostproc.a \
    libswscale.a \
    libavresample.a \
    libavdevice.a \
    /Users/cloud/Documents/iOS/ego/FFmpeg/Android_sh/x264-snapshot-20191217-2245-stable/android/arm64-v8a/lib/libx264.a \
    /Users/cloud/Documents/iOS/ego/FFmpeg/Android_sh/fdk-aac-2.0.2/android/armv8-a/lib/libfdk-aac.a \
    -lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker \
    $TOOLCHAIN/lib/gcc/aarch64-linux-android/4.9.x/libgcc.a \
    $TOOL/sysroot/usr/lib/aarch64-linux-android/21/libmediandk.so \

echo "完成編譯ffmpeg so"

圖片.png

同理再生成armv7架構(gòu)的so安寺,拖入到Andriod工程中厕妖,就可以執(zhí)行通過ffmpeg執(zhí)行Android硬編碼了,比如具體命令
ffmpeg -i MyHeart.mp4 -c:a aac -c:v h264_hlmediacodec output.mp4
ffmpeg -i MyHeart.mp4 -c:a aac -c:v hevc_hlmediacodec output.mp4
ffmpeg -i MyHeart.mp4 -c:a aac -c:v libx264 output.mp4
當(dāng)然后面可以添加更改分辨率,幀率挑庶,碼率言秸,gop,ABR和CBR的參數(shù)配置,不同的參數(shù)輸出的結(jié)果不一致
圖片.png

圖片.png

先將我編好的工程傳到github上面迎捺,Demo地址
iOS的則簡單些举畸,直接打開開關(guān)編譯ffmpeg即可,進(jìn)行腳本編譯,生成.a靜態(tài)庫,我編譯的腳本是聯(lián)合了libx264和fdk-aac
--enable-videotoolbox --enable-encoder=h264_videotoolbox --enable-encoder=hevc_videotoolbox
編譯腳本如下

#!/bin/sh

# directories
FF_VERSION="4.2.2"
#FF_VERSION="snapshot-git"
if [[ $FFMPEG_VERSION != "" ]]; then
  FF_VERSION=$FFMPEG_VERSION
fi
SOURCE="ffmpeg-$FF_VERSION"
FAT="FFmpeg-iOS"

SCRATCH="scratch"
# must be an absolute path
THIN=`pwd`/"thin"

# absolute path to x264 library
X264=`pwd`/X264/x264-iOS

#FDK_AAC=`pwd`/../fdk-aac-build-script-for-iOS/fdk-aac-ios
FDK_AAC=`pwd`/FDK-AAC/fdk-aac-ios

CONFIGURE_FLAGS="--enable-cross-compile --enable-debug --disable-programs --disable-optimizations --disable-stripping \
                 --disable-doc --enable-pic --disable-asm --disable-yasm --enable-avresample \
                 --enable-videotoolbox --enable-encoder=h264_videotoolbox \
                 --enable-nonfree"

if [ "$X264" ]
then
    CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-gpl --enable-libx264"
fi

if [ "$FDK_AAC" ]
then
    CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-libfdk-aac --enable-nonfree"
fi

# avresample
#CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-avresample"

ARCHS="arm64 armv7"

COMPILE="y"
LIPO="y"

DEPLOYMENT_TARGET="8.0"

if [ "$*" ]
then
    if [ "$*" = "lipo" ]
    then
        # skip compile
        COMPILE=
    else
        ARCHS="$*"
        if [ $# -eq 1 ]
        then
            # skip lipo
            LIPO=
        fi
    fi
fi

if [ "$COMPILE" ]
then
    if [ ! `which yasm` ]
    then
        echo 'Yasm not found'
        if [ ! `which brew` ]
        then
            echo 'Homebrew not found. Trying to install...'
                        ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" \
                || exit 1
        fi
        echo 'Trying to install Yasm...'
        brew install yasm || exit 1
    fi
    if [ ! `which gas-preprocessor.pl` ]
    then
        echo 'gas-preprocessor.pl not found. Trying to install...'
        (curl -L https://github.com/libav/gas-preprocessor/raw/master/gas-preprocessor.pl \
            -o /usr/local/bin/gas-preprocessor.pl \
            && chmod +x /usr/local/bin/gas-preprocessor.pl) \
            || exit 1
    fi

    if [ ! -r $SOURCE ]
    then
        echo 'FFmpeg source not found. Trying to download...'
        curl http://www.ffmpeg.org/releases/$SOURCE.tar.bz2 | tar xj \
            || exit 1
    fi

    CWD=`pwd`
    for ARCH in $ARCHS
    do
        echo "building $ARCH..."
        mkdir -p "$SCRATCH/$ARCH"
        cd "$SCRATCH/$ARCH"

        CFLAGS="-arch $ARCH"
        if [ "$ARCH" = "i386" -o "$ARCH" = "x86_64" ]
        then
            PLATFORM="iPhoneSimulator"
            CFLAGS="$CFLAGS -mios-simulator-version-min=$DEPLOYMENT_TARGET"
        else
            PLATFORM="iPhoneOS"
            CFLAGS="$CFLAGS -mios-version-min=$DEPLOYMENT_TARGET -fembed-bitcode"
            if [ "$ARCH" = "arm64" ]
            then
                EXPORT="GASPP_FIX_XCODE5=1"
            fi
        fi

        XCRUN_SDK=`echo $PLATFORM | tr '[:upper:]' '[:lower:]'`
        CC="xcrun -sdk $XCRUN_SDK clang"

        # force "configure" to use "gas-preprocessor.pl" (FFmpeg 3.3)
        if [ "$ARCH" = "arm64" ]
        then
            AS="gas-preprocessor.pl -arch aarch64 -- $CC"
        else
            AS="gas-preprocessor.pl -- $CC"
        fi

        CXXFLAGS="$CFLAGS"
        LDFLAGS="$CFLAGS"
        if [ "$X264" ]
        then
            CFLAGS="$CFLAGS -I$X264/include"
            LDFLAGS="$LDFLAGS -L$X264/lib"
        fi
        if [ "$FDK_AAC" ]
        then
            CFLAGS="$CFLAGS -I$FDK_AAC/include"
            LDFLAGS="$LDFLAGS -L$FDK_AAC/lib"
        fi

        TMPDIR=${TMPDIR/%\/} $CWD/$SOURCE/configure \
            --target-os=darwin \
            --arch=$ARCH \
            --cc="$CC" \
            --as="$AS" \
            $CONFIGURE_FLAGS \
            --extra-cflags="$CFLAGS" \
            --extra-ldflags="$LDFLAGS" \
            --prefix="$THIN/$ARCH" \
        || exit 1

        make -j3 install $EXPORT || exit 1
        cd $CWD
    done
fi

if [ "$LIPO" ]
then
    echo "building fat binaries..."
    mkdir -p $FAT/lib
    set - $ARCHS
    CWD=`pwd`
    cd $THIN/$1/lib
    for LIB in *.a
    do
        cd $CWD
        echo lipo -create `find $THIN -name $LIB` -output $FAT/lib/$LIB 1>&2
        lipo -create `find $THIN -name $LIB` -output $FAT/lib/$LIB || exit 1
    done

    cd $CWD
    cp -rf $THIN/$1/include $FAT
fi

echo Done

iOS的Demo地址

3.我們?nèi)绾沃牢覀冃绿砑拥木幋a器h264_hlmediacodec,hevc_hlmediacodec是否在ffmpeg中生效了呢抄沮?我們通過jni調(diào)用打印所有的編碼器,看是否有Android mediacodec硬編碼器,控制臺會將所有的編碼器打印出來岖瑰,存在新增的編碼器h264_hlmediacodec,hevc_hlmediacodec叛买。

JNIEXPORT jstring JNICALL
Java_com_fish_ffmpegtranscoding_MainActivity_ffmpegInfo(JNIEnv *env, jobject  /* this */) {
    av_log_set_callback(log_callback_test2);
    char info[40000] = {0};
    AVCodec *c_temp = av_codec_next(NULL);
    while (c_temp != NULL) {
        if (c_temp->decode != NULL) {
            sprintf(info, "%sdecode:", info);
        } else {
            sprintf(info, "%sencode:", info);
        }
        switch (c_temp->type) {
            case AVMEDIA_TYPE_VIDEO:
                sprintf(info, "%s(video):", info);
                break;
            case AVMEDIA_TYPE_AUDIO:
                sprintf(info, "%s(audio):", info);
                break;
            default:
                sprintf(info, "%s(other):", info);
                break;
        }
        if (strcmp(c_temp->name,"h264_hlmediacodec") == 0){
            sprintf(info, "%s[%s]\n", info, c_temp->name);
        }
        sprintf(info, "%s[%s]\n", info, c_temp->name);
        c_temp = c_temp->next;
    }
//    AVCodec *codec =avcodec_find_encoder_by_name("h264_hlmediacodec") ;
    return env->NewStringUTF(info);
    }
圖片.png

4.新加的Android編碼器是如何在ffmpeg.c中生效的呢?我們執(zhí)行ffmpeg -i in.mp4 -c:a aac -c:v h264_hlmediacodec -y output.mp4,ffmpeg是如何發(fā)現(xiàn)h264_hlmediacodec編碼器的蹋订,先去查看iOS的硬編碼VideoToolBox是如何工作的率挣,三個(gè)函數(shù)init,encode2,close

1.編碼器初始化init函數(shù),為編碼器硬編碼做準(zhǔn)備


圖片.png
static av_cold int vtenc_init(AVCodecContext *avctx)
{
    VTEncContext    *vtctx = avctx->priv_data;
    CFBooleanRef    has_b_frames_cfbool;
    int             status;

    pthread_once(&once_ctrl, loadVTEncSymbols);

    pthread_mutex_init(&vtctx->lock, NULL);
    pthread_cond_init(&vtctx->cv_sample_sent, NULL);

    vtctx->session = NULL;
    status = vtenc_configure_encoder(avctx);
    if (status) return status;

    status = VTSessionCopyProperty(vtctx->session,
                                   kVTCompressionPropertyKey_AllowFrameReordering,
                                   kCFAllocatorDefault,
                                   &has_b_frames_cfbool);

    if (!status && has_b_frames_cfbool) {
        //Some devices don't output B-frames for main profile, even if requested.
        vtctx->has_b_frames = CFBooleanGetValue(has_b_frames_cfbool);
        CFRelease(has_b_frames_cfbool);
    }
    avctx->has_b_frames = vtctx->has_b_frames;
    return 0;
}

2.encode2函數(shù),有4個(gè)參數(shù),AVCodecContext *avctx表示當(dāng)前編碼器上下文,AVPacket *pkt表示一幀純YUV數(shù)據(jù)編碼后用pkt來接受H264文件,int *got_packet表示編碼成功后將got_packet置為1露戒,返回給發(fā)送方椒功,發(fā)送下一幀YUV數(shù)據(jù),若編碼失敗got_packet置為0智什,返回給發(fā)送方动漾,編碼失敗停止發(fā)送
通過閱讀源碼查看這部分邏輯,同理,新增的videotoolboxenc.c也是一樣的邏輯荠锭,傳入純YUV數(shù)據(jù)后旱眯,編碼成功得到AVPacket

static av_cold int vtenc_frame(
    AVCodecContext *avctx,
    AVPacket       *pkt,
    const AVFrame  *frame,
    int            *got_packet)
圖片.png

編碼成功后的AVPacket是如何回傳到ffmpeg.c中的do_video_out方法的呢?繼續(xù)往下看


圖片.png

如下圖,傳入的frame和pkt的引用計(jì)數(shù)內(nèi)部不用去管,外部ffmpeg自行去釋放证九,pkt為棧變量键思,函數(shù)結(jié)束就釋放了,frame則每有新一幀的時(shí)候去覆蓋掉前一幀甫贯,前一幀的引用計(jì)數(shù)減一被釋放


圖片.png

3.hlmediacodec_encode_close,編碼完成看蚜,已經(jīng)重新將編碼后的數(shù)據(jù)寫入容器叫搁,釋放編碼器上下文,內(nèi)部的frame若有數(shù)據(jù),則清空

static av_cold int hlmediacodec_encode_close(AVCodecContext *avctx)
{
    hi_logi(avctx, "hlmediacodec_encode_close %s %d", __FUNCTION__, __LINE__);

    HLMediaCodecEncContext *ctx = avctx->priv_data;
    ctx->stats.uint_stamp = av_gettime_relative();

    hlmediacodec_show_stats(avctx, ctx->stats);

    if (ctx->mediacodec)
    {
        AMediaCodec_stop(ctx->mediacodec);
        AMediaCodec_delete(ctx->mediacodec);
        ctx->mediacodec = NULL;
    }

    if (ctx->mediaformat)
    {
        AMediaFormat_delete(ctx->mediaformat);
        ctx->mediaformat = NULL;
    }

    if (ctx->frame)
    {
        av_frame_free(&ctx->frame);
        ctx->frame = NULL;
    }
    return 0;
}

若要添加編碼參數(shù),則在options中添加渴逻,不如碼率模式CQ,VBR,CBR

static const AVOption ff_hlmediacodec_enc_options[] = {
    {"rc-mode", "The bitrate mode to use", OFFSET(rc_mode), AV_OPT_TYPE_INT, {.i64 = HLMEDIACODEC_BITRATE_MODE_VBR}, HLMEDIACODEC_BITRATE_MODE_CQ, HLMEDIACODEC_BITRATE_MODE_CBR, VE, "rc_mode"},
    {"cq", "Constant quality", 0, AV_OPT_TYPE_CONST, {.i64 = HLMEDIACODEC_BITRATE_MODE_CQ}, INT_MIN, INT_MAX, VE, "rc_mode"},
    {"vbr", "Variable bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = HLMEDIACODEC_BITRATE_MODE_VBR}, INT_MIN, INT_MAX, VE, "rc_mode"},
    {"cbr", "Constant bitrate", 0, AV_OPT_TYPE_CONST, {.i64 = HLMEDIACODEC_BITRATE_MODE_CBR}, INT_MIN, INT_MAX, VE, "rc_mode"},
    {"in_timeout", "in buff timeout", OFFSET(in_timeout), AV_OPT_TYPE_INT, {.i64 = HLMEDIACODEC_IN_SET_TIMEOUT_USEC}, HLMEDIACODEC_MIN_TIMEOUT_USEC, HLMEDIACODEC_MAX_TIMEOUT_USEC, VE},
    {"ou_timeout", "ou buff timeout", OFFSET(ou_timeout), AV_OPT_TYPE_INT, {.i64 = HLMEDIACODEC_OU_SET_TIMEOUT_USEC}, HLMEDIACODEC_MIN_TIMEOUT_USEC, HLMEDIACODEC_MAX_TIMEOUT_USEC, VE},
    {"eof_timeout", "eof buff timeout", OFFSET(eof_timeout), AV_OPT_TYPE_INT, {.i64 = HLMEDIACODEC_EOF_SET_TIMEOUT_USEC}, HLMEDIACODEC_MIN_TIMEOUT_USEC, HLMEDIACODEC_MAX_TIMEOUT_USEC, VE},
    {"in_timeout_times", "in buff timeout times", OFFSET(in_timeout_times), AV_OPT_TYPE_INT, {.i64 = HLMEDIACODEC_IN_SET_TIMEOUT_TIMES}, HLMEDIACODEC_MIN_TIMEOUT_TIMES, HLMEDIACODEC_MAX_TIMEOUT_TIMES, VE},
    {"ou_timeout_times", "ou buff timeout times", OFFSET(ou_timeout_times), AV_OPT_TYPE_INT, {.i64 = HLMEDIACODEC_ENC_OU_SET_TIMEOUT_TIMES}, HLMEDIACODEC_MIN_TIMEOUT_TIMES, HLMEDIACODEC_MAX_TIMEOUT_TIMES, VE},
    {NULL},
};

修改后的添加了MediaCodec硬編碼后的ffmpeg_4.2.2版本源碼下載地址
分別執(zhí)行里面的build_arm64.sh腳本在android/armv8-a目錄下生成arm64架構(gòu)的.a靜態(tài)庫,通過執(zhí)行合并腳本union_ffmpeg_so_armv8.sh,得到.so,這個(gè).so文件就是最終我們可以用來在Android工程中去跑的ffmpeg命令行比如ffmpeg -i MyHeartWillGoOn.mp4 -c:a aac -c:v h264_hlmediacodec output.mp4,最后得到的mp4文件為使用mediacodec硬編碼后的output.mp4文件

圖片.png

echo "開始編譯ffmpeg so"

#NDK路徑.
export NDK=/Users/cloud/Library/android-ndk-r20b

PLATFORM=$NDK/platforms/android-21/arch-arm64
TOOLCHAIN=$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64
TOOL=$NDK/toolchains/llvm/prebuilt/darwin-x86_64

PREFIX=$(pwd)

#如果不需要依賴x264疾党,去掉/usr/x264/x264-master/android/armeabi-v7a/lib/libx264.a \就可以了

$TOOLCHAIN/bin/aarch64-linux-android-ld \
-rpath-link=$PLATFORM/usr/lib \
-L$PLATFORM/usr/lib \
-L$PREFIX/lib \
-soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o \
$PREFIX/libffmpeg.so \
    libavcodec.a \
    libavfilter.a \
    libswresample.a \
    libavformat.a \
    libavutil.a \
    libpostproc.a \
    libswscale.a \
    libavresample.a \
    libavdevice.a \
    /Users/cloud/Documents/iOS/ego/FFmpeg/Android_sh/x264-snapshot-20191217-2245-stable/android/arm64-v8a/lib/libx264.a \
    /Users/cloud/Documents/iOS/ego/FFmpeg/Android_sh/fdk-aac-2.0.2/android/armv8-a/lib/libfdk-aac.a \
    -lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker \
    $TOOLCHAIN/lib/gcc/aarch64-linux-android/4.9.x/libgcc.a \
    $TOOL/sysroot/usr/lib/aarch64-linux-android/21/libmediandk.so \

echo "完成編譯ffmpeg so"

同理執(zhí)行build_armv7.sh腳本后會生成armv7架構(gòu)的.a靜態(tài)庫,執(zhí)行union_ffmpeg_so_armv7.sh合并生成.so,兩種架構(gòu)對應(yīng)的ndk環(huán)境不一致


圖片.png
echo "開始編譯ffmpeg so"

#NDK路徑.
export NDK=/Users/cloud/Library/android-ndk-r20b

PLATFORM=$NDK/platforms/android-21/arch-arm
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
TOOL=$NDK/toolchains/llvm/prebuilt/darwin-x86_64

PREFIX=$(pwd)

#如果不需要依賴x264惨奕,去掉/usr/x264/x264-master/android/armeabi-v7a/lib/libx264.a \就可以了

$TOOLCHAIN/bin/arm-linux-androideabi-ld \
-rpath-link=$PLATFORM/usr/lib \
-L$PLATFORM/usr/lib \
-L$PREFIX/lib \
-soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o \
$PREFIX/libffmpeg.so \
    libavcodec.a \
    libavfilter.a \
    libswresample.a \
    libavformat.a \
    libavutil.a \
    libpostproc.a \
    libswscale.a \
    libavresample.a \
    libavdevice.a \
    /Users/cloud/Documents/iOS/ego/FFmpeg/Android_sh/x264-snapshot-20191217-2245-stable/android/armeabi-v7a/lib/libx264.a \
    /Users/cloud/Documents/iOS/ego/FFmpeg/Android_sh/fdk-aac-2.0.2/android/armv7-a/lib/libfdk-aac.a \
    -lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker \
    $TOOLCHAIN/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a \
    $TOOL/sysroot/usr/lib/arm-linux-androideabi/21/libmediandk.so \

echo "完成編譯ffmpeg so"
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末雪位,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子梨撞,更是在濱河造成了極大的恐慌雹洗,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卧波,死亡現(xiàn)場離奇詭異时肿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)港粱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門螃成,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人查坪,你說我怎么就攤上這事寸宏。” “怎么了偿曙?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵氮凝,是天一觀的道長。 經(jīng)常有香客問我遥昧,道長覆醇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任炭臭,我火速辦了婚禮永脓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鞋仍。我一直安慰自己常摧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布威创。 她就那樣靜靜地躺著落午,像睡著了一般。 火紅的嫁衣襯著肌膚如雪肚豺。 梳的紋絲不亂的頭發(fā)上溃斋,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機(jī)與錄音吸申,去河邊找鬼梗劫。 笑死享甸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的梳侨。 我是一名探鬼主播蛉威,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼走哺!你這毒婦竟也來了蚯嫌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤丙躏,失蹤者是張志新(化名)和其女友劉穎择示,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體彼哼,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡对妄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了敢朱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片剪菱。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖拴签,靈堂內(nèi)的尸體忽然破棺而出孝常,到底是詐尸還是另有隱情,我是刑警寧澤蚓哩,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布构灸,位于F島的核電站,受9級特大地震影響岸梨,放射性物質(zhì)發(fā)生泄漏喜颁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一曹阔、第九天 我趴在偏房一處隱蔽的房頂上張望半开。 院中可真熱鬧,春花似錦赃份、人聲如沸寂拆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纠永。三九已至,卻和暖如春谒拴,著一層夾襖步出監(jiān)牢的瞬間尝江,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工英上, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留茂装,地道東北人怠蹂。 一個(gè)月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像少态,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子易遣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容