在Android上使用FFmpeg壓縮視頻

前幾天項(xiàng)目需要壓縮視頻,Github上找了許多庫扇苞,要么就是太大欺殿,要么就是質(zhì)量不高寄纵,其實(shí)我只需要壓縮視頻,最好的方案還是定制編譯一個(gè) FFmpegAndroid 用脖苏。

本項(xiàng)目使用 FFmpeglibx264(一個(gè)第三方的視頻編碼器) 來編譯出可以在 Android 上使用的動(dòng)態(tài)庫

一程拭、下載源碼

創(chuàng)建一個(gè)叫 FFmpegAndroid 的目錄,下載 libx264源碼ffmpeg源碼棍潘,然后在 FFmpegAndroid 文件夾下建立一個(gè) bulid 文件夾恃鞋,用于存放編譯腳本和輸出

--- FFmpegAndroid
 |-- ffmpeg
 |-- x264
 |-- build

二、編譯 FFmpeg

編譯 x264 編碼器

先在 build 文件夾下建立 setting.sh, 用于申明一些公用的環(huán)境變量亦歉,比如 $NDK恤浪、$CPU...

setting.sh

# ndk 環(huán)境
NDK=$HOME/Library/Android/sdk/ndk-bundle
SYSROOT=$NDK/platforms/android-14/arch-arm/
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
# cpu 架構(gòu)平臺(tái),若要編譯 x86 則指定 x86
CPU=armv7-a

然后建立 libx264 的編譯腳本 build_x264.sh肴楷,libx264 是一個(gè)開源的H.264編碼器水由,據(jù)說是最好的視頻有損編碼器。ffmpeg 默認(rèn)不自帶赛蔫,但是支持 x264 作為第三方編碼器編譯砂客。

build_x264.sh

./config 內(nèi)的# 注釋必須在運(yùn)行的時(shí)候去掉

#!/bin/bash

# 引入需要的環(huán)境變量
. setting.sh

# 輸出下看看對(duì)不對(duì),可以去掉濒募,這里調(diào)試用
echo "use toolchain: $TOOLCHAIN"
echo "use system root: $SYSROOT"

# 輸出文件的前綴鞭盟,也就是指定最后靜態(tài)庫輸出到那里
PREFIX=$(pwd)/lib/x264/$CPU
# 優(yōu)化參數(shù)
OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "
ADDI_CFLAGS=""
ADDI_LDFLAGS=""

# 因?yàn)楫?dāng)前目錄在 build 目錄,需要切換到 x264 去執(zhí)行 config
cd ../x264
function build_x264
{
./configure \
    --prefix=$PREFIX \
    # 不編譯動(dòng)態(tài)庫
    --disable-shared \
    --disable-asm \
    # 編譯靜態(tài)庫
    --enable-static \
    --enable-pic \
    --enable-strip \
    --host=arm-linux-androideabi \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --sysroot=$SYSROOT \
    --extra-cflags="-Os -fpic $ADDI_CFLAGS $OPTIMIZE_CFLAGS" \
    --extra-ldflags="$ADDI_LDFLAGS" \
    $ADDITIONAL_CONFIGURE_FLAG
make clean
make -j4
make install
}

# 執(zhí)行編譯指令
build_x264

寫完之后就可以編譯 x264 庫了瑰剃,編譯之前還有一點(diǎn)要注意的是齿诉,默認(rèn)編譯出來的文件后綴并不是 *.so,這 Android 是識(shí)別不了的晌姚,需要對(duì) x264 源碼里面的 config 做如下修改:

echo "SOSUFFIX=so" >> config.mak
echo "SONAME=libx264.so.$API" >> config.mak
echo "SOFLAGS=-shared -Wl,-soname,\$(SONAME) $SOFLAGS" >> config.mak

修改成

echo "SOSUFFIX=so" >> config.mak
echo "SONAME=libx264-$API.so" >> config.mak
echo "SOFLAGS=-shared -Wl,-soname,\$(SONAME) $SOFLAGS" >> config.mak

別忘了給 build_x264.shsetting.sh 賦予可執(zhí)行權(quán)限 (chmod +x build_x264.sh setting.sh)

修改完后就可以執(zhí)行腳本命令了

./build_x264.sh

等待一段時(shí)間后粤剧,build 文件夾目錄下應(yīng)該有個(gè) lib 目錄(build 腳本里面 prefix 指定的目錄),里面存放了 x264 的靜態(tài)庫

這里為什么編譯成靜態(tài)庫而不是動(dòng)態(tài)庫呢挥唠?靜態(tài)庫可以把內(nèi)容編譯到待會(huì)兒要編譯 ffmpeg 的so庫里去抵恋,不需要單獨(dú)加載 libx264.so 了,如果你硬要編譯成動(dòng)態(tài)庫也可以宝磨,加載 ffmpeg.so 的時(shí)候加載 libx264.so 就可以

至此弧关,x264編碼器編譯完畢

編譯 FFmpeg

同樣在 build 文件夾下建立編譯腳本 build_ffmpeg.sh,編譯 ffmpeg 比編譯 x264 略微麻煩點(diǎn)唤锉,首先肯定不能全功能編譯世囊,那還不如直接去網(wǎng)上找一個(gè)編譯好的,要自己定制哪些組件需要窿祥,哪些組件不需要

FFmpeg它主要含有以下幾個(gè)核心庫:

  • libavcodec-提供了更加全面的編解碼實(shí)現(xiàn)的合集
  • libavformat-提供了更加全面的音視頻容器格式的封裝和解析以及所支持的協(xié)議
  • libavutil-提供了一些公共函數(shù)
  • libavfilter-提供音視頻的過濾器株憾,如視頻加水印、音頻變聲等
  • libavdevice-提供支持眾多設(shè)備數(shù)據(jù)的輸入與輸出,如讀取攝像頭數(shù)據(jù)嗤瞎、屏幕錄制
  • libswresample,libavresample-提供音頻的重采樣工具
  • libswscale-提供對(duì)視頻圖像進(jìn)行色彩轉(zhuǎn)換墙歪、縮放以及像素格式轉(zhuǎn)換,如圖像的YUV轉(zhuǎn)換
  • libpostproc-多媒體后處理器

如果不修改什么配置贝奇,直接編譯的話虹菲,我發(fā)現(xiàn) libavcodec.so 有 7.8MB,我可以在這方面下手掉瞳,指定 decoderencoder届惋,因?yàn)槲倚枰氖且曨l壓縮,所以編碼器(encoder)我就只需要 x264(視頻編碼) 和 aac(音頻編碼)菠赚,至于解碼器,挑幾個(gè)常用的就可以了

查看編碼器和解碼器種類郑藏,可以通過 ./config --list-decoders 或 ./config --list-encoers 命令實(shí)現(xiàn)(ffmpeg目錄下)
./config 內(nèi)的# 注釋必須在運(yùn)行的時(shí)候去掉

#!/bin/bash

# 導(dǎo)入環(huán)境變量
. setting.sh

# 輸出衡查,調(diào)試用
echo "use toolchain: $TOOLCHAIN"
echo "use system root: $SYSROOT"

# x264庫所在的位置,ffmpeg 需要鏈接 x264
LIB_DIR=$(pwd)/lib;

# ffmpeg編譯輸出前綴
PREFIX=$LIB_DIR/ffmpeg/$CPU
# x264的頭文件地址
INC="$LIB_DIR/x264/$CPU/include"
# x264的靜態(tài)庫地址
LIB="$LIB_DIR/x264/$CPU/lib"
# 輸出調(diào)試
echo "include dir: $INC"
echo "lib dir: $LIB"
# 編譯優(yōu)化參數(shù)
FF_EXTRA_CFLAGS="-march=$CPU -mfpu=vfpv3-d16 -mfloat-abi=softfp -mthumb" 
# 編譯優(yōu)化參數(shù)必盖,-I$INC 指定 x264 頭文件路徑
FF_CFLAGS="-O3 -Wall -pipe \
-ffast-math \
-fstrict-aliasing -Werror=strict-aliasing \
-Wno-psabi -Wa,--noexecstack \
-DANDROID  \
-I$INC"

cd ../ffmpeg
function build_arm
{
./configure \
    # 這里需要啟動(dòng)生成動(dòng)態(tài)庫
    --enable-shared \
    # 靜態(tài)庫就不生成了
    --disable-static \
    --disable-ffmpeg \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-ffserver \
    --disable-symver \
    # 禁用全部的編碼
    --disable-encoders \
    # 啟用 x264 這個(gè)庫
    --enable-libx264 \
    # 啟用 x264 編碼
    --enable-encoder=libx264 \
    # 啟用 aac 音頻編碼
    --enable-encoder=aac \
    # 啟用幾個(gè)圖片編碼拌牲,由于生成視頻預(yù)覽
    --enable-encoder=mjpeg \
    --enable-encoder=png \
    # 禁用全部的解碼器
    --disable-decoders \
    # 啟用幾個(gè)常用的解碼
    --enable-decoder=aac \
    --enable-decoder=aac_latm \
    --enable-decoder=h264 \
    --enable-decoder=mpeg4 \
    --enable-decoder=mjpeg \
    --enable-decoder=png \
    --disable-demuxers \
    --enable-demuxer=image2 \
    --enable-demuxer=h264 \
    --enable-demuxer=aac \
    --enable-demuxer=avi \
    --enable-demuxer=mpc \
    --enable-demuxer=mov \
    --disable-parsers \
    --enable-parser=aac \
    --enable-parser=ac3 \
    --enable-parser=h264 \
    # 這幾個(gè)庫應(yīng)該需要,沒怎么測(cè)試歌粥,反正很小就加上了
    --enable-avresample \
    --enable-small \
    --enable-avfilter \
    # 這兩個(gè)是鏈接 x264 靜態(tài)庫需要
    --enable-gpl \
    --enable-yasm \
    # 編譯輸出前綴
    --prefix=$PREFIX \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --target-os=linux \
    --arch=arm \
    --enable-cross-compile \
    --sysroot=$SYSROOT \
    --extra-cflags="$FF_CFLAGS $FF_EXTRA_CFLAGS" \
    # 指定 x264 靜態(tài)庫位置
    --extra-ldflags="-Wl,-L$LIB"
make clean
make -j16
make install
}

build_arm

這次編譯不用靜態(tài)庫的原因是塌忽,靜態(tài)庫鏈接是有順序要求的,這里模塊太多失驶,我也不知道哪個(gè)模塊依賴哪個(gè)模塊土居,所以直接上動(dòng)態(tài)庫

腳本寫完后,就可以 run 了嬉探,編譯時(shí)間有點(diǎn)久擦耀,可以學(xué)學(xué)我的某個(gè)同學(xué),一編譯就起來泡泡妹子涩堤,有說有笑眷蜓。

編譯完成后你的目錄應(yīng)該是下面那個(gè)樣子:

--- FFmpegAndroid
    |-- ffmpeg
    |-- x264
    |-- build
        |-- build_ffmpeg.sh
        |-- build_x264.sh
        |-- lib
            |-- ffmpeg/armv7-a
                |-- include (ffmpeg so庫的頭文件)
                |-- lib (ffmpeg so庫)
                    |-- libavcodec-57.so
                    |-- libavdevice-57.so
                    |-- libavcodec-57.so
                    |-- libavfilter-6.so
                    |-- libavformat-57.so
                    |-- libavresample-3.so
                    |-- libavutil-55.so
                    |-- libpostproc-54.so
                    |-- libresample-2.so
                    |-- libswscale-4.so
            |-- x264 (x264的靜態(tài)庫和頭文件)

后面的版本號(hào)不一樣沒關(guān)系,這由 ffmpeg 版本決定的

庫編譯完了胎围,這些 so 庫就是在 Android 可用的動(dòng)態(tài)庫吁系,接下來就可以準(zhǔn)備 JNI 編程了

三、在 Android 里使用 FFmpeg

前面已經(jīng)把 FFmpeg 各個(gè)核心庫編譯出來了白魂,但是我肯定不會(huì)在里面直接用核心庫內(nèi)的函數(shù)來用汽纤,ffmpeg 本來是一個(gè)在 pc 端的命令,命令里面可以填寫各種參數(shù)碧聪,比如 ffmpeg -i a.mp4 -c:v x264 -c:a aac b.mp4冒版,就是把 a.mp4 用 x264(視頻)、aac(音頻) 編碼成 b.mp4逞姿。

ffmpeg 是由 ffmpeg.c 編譯出來的辞嗡,想要在 Android 里面用 ffmpeg 命令捆等,只要修改 ffmpeg.c 里面的 main 函數(shù),比如修改成 int run_ffmpeg_command(int args, char **argv)续室,然后用 JNI 暴露給 java 調(diào)用栋烤,就可以在 Android 使用 ffmpeg 命令了

在 FFmpegAndroid 建立一個(gè) Android 工程,然后新建一個(gè) ffmpeg 的 lib module
對(duì)于 NDK 開發(fā)挺狰,AndroidStudio 2.2 以后就有較好的支持明郭,直接修改支持庫的 build.gradle 文件

apply plugin: 'com.android.library'

android {
    ...

    defaultConfig {
        ...
        // 啟用 c++ 支持
        externalNativeBuild {
            cmake {
                cppFlags "-std=c++11"
            }
            ndk {
                abiFilters "armeabi-v7a"
            }
        }
    }

    ...

    // 指定 CMakeList 文件
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

這樣 lib module 就支持 c++ 了,方便吧丰泊!比以前的 Android.mk 不知道方便多少

然后在模塊的 src/main 下面新建一個(gè) cpp 目錄薯定,用于存放 c++ 代碼,從ffmpeg拷貝以下文件:

cmdutils_common_opts.h
cmdutils.c
cmdutils.h
config.h
ffmpeg_filter.c
ffmpeg_opt.c
ffmpeg-lib.c
ffmpeg.c
ffmpeg.h

然后在 CMakeList.txt 里面配置這些文件瞳购,好讓 AndroidStudio 認(rèn)識(shí)它們

# 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.4.1)

# 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.
            ffmpeg-lib

            # Sets the library as a shared library.
            SHARED

            # Provides a relative path to your source file(s).
             src/main/cpp/cmdutils.c
             src/main/cpp/ffmpeg.c
             src/main/cpp/ffmpeg_filter.c
             src/main/cpp/ffmpeg_opt.c
            # 此文件是用于暴露 ffmpeg.c 的 main 函數(shù)用
             src/main/cpp/ffmpeg-lib.c)

set(FFMPEG_LIB_DIR /Users/qigengxin/Documents/Github/FFmpegAndroid/build/lib/ffmpeg/armv7-a/lib)

add_library(
    avcodec
    SHARED
    IMPORTED
)
set_target_properties(
    avcodec
    PROPERTIES IMPORTED_LOCATION
    ${FFMPEG_LIB_DIR}/libavcodec-57.so
)

add_library(
    avdevice
    SHARED
    IMPORTED
)
set_target_properties(
    avdevice
    PROPERTIES IMPORTED_LOCATION
    ${FFMPEG_LIB_DIR}/libavdevice-57.so
)

add_library(
    avfilter
    SHARED
    IMPORTED
)
set_target_properties(
    avfilter
    PROPERTIES IMPORTED_LOCATION
    ${FFMPEG_LIB_DIR}/libavfilter-6.so
)

add_library(
    avformat
    SHARED
    IMPORTED
)
set_target_properties(
    avformat
    PROPERTIES IMPORTED_LOCATION
    ${FFMPEG_LIB_DIR}/libavformat-57.so
)

add_library(
    avresample
    SHARED
    IMPORTED
)
set_target_properties(
    avresample
    PROPERTIES IMPORTED_LOCATION
    ${FFMPEG_LIB_DIR}/libavresample-3.so
)

add_library(
    avutil
    SHARED
    IMPORTED
)
set_target_properties(
    avutil
    PROPERTIES IMPORTED_LOCATION
    ${FFMPEG_LIB_DIR}/libavutil-55.so
)

add_library(
    postproc
    SHARED
    IMPORTED
)
set_target_properties(
    postproc
    PROPERTIES IMPORTED_LOCATION
    ${FFMPEG_LIB_DIR}/libpostproc-54.so
)

add_library(
    swresample
    SHARED
    IMPORTED
)
set_target_properties(
    swresample
    PROPERTIES IMPORTED_LOCATION
    ${FFMPEG_LIB_DIR}/libswresample-2.so
)

add_library(
    swscale
    SHARED
    IMPORTED
)
set_target_properties(
    swscale
    PROPERTIES IMPORTED_LOCATION
    ${FFMPEG_LIB_DIR}/libswscale-4.so
)

include_directories(
    ../../ffmpeg
)

# 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.
target_link_libraries( # Specifies the target library.
            ffmpeg-lib
            avcodec
            avutil
            avfilter
            swscale
            swresample
            avresample
            postproc
            avformat
            avdevice

            # Links the target library to the log library
            # included in the NDK.
            ${log-lib} )

刷新下 gradle话侄,就可以寫 c++ 代碼了。先看下 ffmpeg.c 這個(gè)文件学赛,原先的指令其實(shí)調(diào)用的就是 main 函數(shù)年堆,我們先把 main 函數(shù)改成自己自定義的函數(shù) run_ffmpeg_command:

int run_ffmpeg_command(int argc, char **argv){
    ...
}

改了以后,我們就可以調(diào)用 run_ffmpeg_command 然后傳入?yún)?shù)盏浇,相當(dāng)于在 pc 執(zhí)行 ffmpeg 命令变丧。不過現(xiàn)在還不能執(zhí)行,這是個(gè)坑點(diǎn)绢掰,仔細(xì)看 run_ffmpeg_command 函數(shù)痒蓬,在程序結(jié)束的時(shí)候,或者中途出現(xiàn)錯(cuò)誤的時(shí)候滴劲,都會(huì)調(diào)用 exit_program(int)谊却,這個(gè)函數(shù):

int run_ffmpeg_command(int argc, char **argv){
    ...

    /* parse options and open all input/output files */
    ret = ffmpeg_parse_options(argc, argv);
    if (ret < 0){
        exit_program(1);
    }

    ...

    if (nb_output_files <= 0 && nb_input_files == 0) {
        show_usage();
        av_log(NULL, AV_LOG_WARNING, "Use -h to get full help or, even better, run 'man %s'\n", program_name);
        exit_program(1);
    }

    exit_program(received_nb_signals ? 255 : main_return_code);
    return main_return_code;
}

exit_program(int) 函數(shù)是什么,跳過去看一下發(fā)現(xiàn)里面就是清理資源然后 exit(int)哑芹,這里就要注意這個(gè) exit 函數(shù)了炎辨,除非我們是多進(jìn)程方式調(diào)用 run_ffmpeg_command,如果我們?cè)?app 的進(jìn)程調(diào)用聪姿,執(zhí)行了 exit 就會(huì)結(jié)束 app 的進(jìn)程碴萧!

這不是我想看到的,最好的方法是另開一個(gè)進(jìn)程調(diào)用末购,但是這樣就涉及到了進(jìn)程間的通信問題破喻,麻煩,不想寫盟榴!反正只是跑一個(gè)壓縮指令嘛曹质,直接改 ffmpeg.c,首先把 exit(int) 函數(shù)給注釋掉,然后返回一個(gè) code羽德,run_ffmpeg_command 函數(shù)里面只要涉及到 exit_program(int) 函數(shù)調(diào)用的地方都寫成 return exit_program(int)几莽,不過要注意,有如下幾個(gè)坑點(diǎn):

修改 ffmpeg.c 坑點(diǎn)一

調(diào)試的時(shí)候發(fā)現(xiàn) return exit_program(int); 語句并不會(huì)結(jié)束當(dāng)前函數(shù)并返回宅静,而是繼續(xù)往下執(zhí)行了章蚣,當(dāng)時(shí)一臉楞逼,我艸R碳小纤垂!這是什么鬼?磷账?為什么我 return 了沒有用峭沦?找了半天后才發(fā)現(xiàn)是 exit_program(int) 這個(gè)函數(shù)聲明的鍋!看下面這個(gè)函數(shù)的聲明:

/**
 * Wraps exit with a program-specific cleanup routine.
 */
int exit_program(int ret) av_noreturn;

函數(shù)后面有個(gè)奇怪的 av_noreturn 聲明逃糟,網(wǎng)上查了一下才知道熙侍,這個(gè)是給編譯器的注解,這貨的鍋履磨,去掉就好了。

修改 ffmpeg.c 坑點(diǎn)二

其實(shí) exit_program(int) 這個(gè)函數(shù)不只是在 run_ffmpeg_command 里面調(diào)用庆尘,其它各種函數(shù)里面都有剃诅,如果都要修改的話必須一層一層的 return (C語言里面沒有異常啊),很麻煩驶忌,但是如果沒有改好的話就很容易 crash矛辕,這是個(gè)要解決的問題,首先 run_ffmpeg_command 里面的 exit_program 都要改成 return 方式

然后因?yàn)樽罱K目的是壓縮視頻付魔,參數(shù)集是固定的聊品,所以不用考慮編碼不支持,或參數(shù)匹配不到的情況几苍,只需要考慮文件讀寫的問題翻屈,就是輸入文件不存在的時(shí)候,或者輸出路徑不合法的時(shí)候妻坝,不能讓程序異常退出伸眶,而是返回錯(cuò)誤碼,這個(gè)需要改 ffmpeg_opt.c 這個(gè)文件
ffmpeg_opt.c

static int open_files(OptionGroupList *l, const char *inout, int (*open_file)(OptionsContext*, const char*)){
    ...
}

static int open_input_file(OptionsContext *o, const char *filename){
    ...
}

static int open_outout_file(OptionsContext *o, const char *filename){
    ...
}

static int init_output_filter(OutputFilter *ofilter, OptionsContext *o, AVFormatContext *oc){
    ...
}

目前我項(xiàng)目中就只改了這幾個(gè)函數(shù)內(nèi)的 exit_program刽宪,測(cè)試可行厘贼,也可以參考本項(xiàng)目的代碼,鏈接在文末

最后就是暴露 run_ffmpeg_command 方法給 java 調(diào)用了圣拄,這個(gè)和普通的 JNI 編程一樣嘴秸,建一個(gè) native 的方法,創(chuàng)建 cpp 代碼。岳掐。凭疮。沒啥東西,直接上代碼

FFmpegNativeBridge

public class FFmpegNativeBridge {

    static {
        System.loadLibrary("ffmpeg-lib");
    }

    /**
     * 執(zhí)行指令
     * @param command
     * @return 命令返回結(jié)果
     */
    public static native int runCommand(String[] command);
}

ffmpeg-lib.c

#include <jni.h>
#include "ffmpeg.h"

JNIEXPORT jint JNICALL
Java_org_voiddog_ffmpeg_FFmpegNativeBridge_runCommand(JNIEnv *env, jclass type,
                                                      jobjectArray command) {
    int argc = (*env)->GetArrayLength(env, command);
    char *argv[argc];
    jstring jsArray[argc];
    int i;
    for (i = 0; i < argc; i++) {
        jsArray[i] = (jstring) (*env)->GetObjectArrayElement(env, command, i);
        argv[i] = (char *) (*env)->GetStringUTFChars(env, jsArray[i], 0);
    }
    int ret = run_ffmpeg_command(argc,argv);
    for (i = 0; i < argc; ++i) {
        (*env)->ReleaseStringUTFChars(env, jsArray[i], argv[i]);
    }
    return ret;
}

運(yùn)行前先需要把 ffmpeg 編譯出來的一堆 so 庫放到 jniLibs 內(nèi)岩四,不然運(yùn)行的時(shí)候會(huì)出現(xiàn)動(dòng)態(tài)庫無法加載的異常哭尝。最后就可以在 Android 內(nèi)用 ffmpeg 的命令了:

 int ret = FFmpegNativeBridge.runCommand(new String[]{"ffmpeg",
                    "-i", "/storage/emulated/0/DCIM/Camera/VID_20170527_175421.mp4",
                    "-y",
                    "-c:v", "libx264",
                    "-c:a", "aac",
                    "-vf", "scale=480:-2",
                    "-preset", "ultrafast",
                    "-crf", "28",
                    "-b:a", "128k",
                    "/storage/emulated/0/Download/a.mp4"});

關(guān)于這些參數(shù),可以去查 FFmpeg官網(wǎng)剖煌,本項(xiàng)目源碼地址Github

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末材鹦,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子耕姊,更是在濱河造成了極大的恐慌桶唐,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茉兰,死亡現(xiàn)場(chǎng)離奇詭異尤泽,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)规脸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門坯约,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人莫鸭,你說我怎么就攤上這事闹丐。” “怎么了被因?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵卿拴,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我梨与,道長(zhǎng)堕花,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任粥鞋,我火速辦了婚禮缘挽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘呻粹。我一直安慰自己到踏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布尚猿。 她就那樣靜靜地躺著窝稿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凿掂。 梳的紋絲不亂的頭發(fā)上伴榔,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天纹蝴,我揣著相機(jī)與錄音,去河邊找鬼踪少。 笑死塘安,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的援奢。 我是一名探鬼主播兼犯,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼集漾!你這毒婦竟也來了切黔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤具篇,失蹤者是張志新(化名)和其女友劉穎纬霞,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驱显,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡诗芜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了埃疫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伏恐。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖栓霜,靈堂內(nèi)的尸體忽然破棺而出翠桦,到底是詐尸還是另有隱情,我是刑警寧澤叙淌,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站愁铺,受9級(jí)特大地震影響鹰霍,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茵乱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一茂洒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瓶竭,春花似錦督勺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至荧恍,卻和暖如春瓷叫,著一層夾襖步出監(jiān)牢的瞬間屯吊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工摹菠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盒卸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓次氨,卻偏偏與公主長(zhǎng)得像蔽介,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子煮寡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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