《Android音視頻系列-3》Android項(xiàng)目中使用FFmpeg

上一篇已經(jīng)編譯出FFmpeg動(dòng)態(tài)庫,這一篇就來介紹如何在Android項(xiàng)目中使用動(dòng)態(tài)庫州泊。

新建一個(gè)項(xiàng)目丧蘸,在Android Studio 3.4.1中發(fā)現(xiàn)新建項(xiàng)目發(fā)生了一些改變

如果是要support c++,那么應(yīng)該選Native c++,然后一路next力喷,發(fā)現(xiàn)AS幫我們把cmake配置好了刽漂,cmake是c++代碼構(gòu)建工具,以前的老項(xiàng)目可能還是用mk的構(gòu)建方式弟孟,我們要與時(shí)俱進(jìn)贝咙,用cmake準(zhǔn)沒錯(cuò)。


我電腦已經(jīng)裝好cmake工具了拂募,如果沒有裝的打開setting->sdk 進(jìn)行打鉤下載

LLDB是c++調(diào)試需要的
CMake 不要用最新版庭猩,會(huì)報(bào)錯(cuò)

然后這樣就可以編譯跑到手機(jī)上了,這一步就不演示了陈症。

直接進(jìn)入正題

一蔼水、拷貝動(dòng)態(tài)庫

怎么把FFmpeg的幾個(gè)so搞進(jìn)去

1、新建jniLibs目錄录肯,把上一篇編譯成功的FFmpeg 的8個(gè)so放到armeabi目錄趴腋,把頭文件include目錄拷貝到j(luò)niLibs目錄下

然后需要修改CMakeLists.txt,為了看起來清晰點(diǎn)论咏,先把CMakeLists.txt移動(dòng)到app目錄下优炬,(默認(rèn)是在app/src/main/cpp),然后修改一下app下的build.gradle

android {
    compileSdkVersion 28
    buildToolsVersion = '28.0.3'
    defaultConfig {
        applicationId "com.lanshifu.ffmpegdemo"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        externalNativeBuild {
            cmake {
                cppFlags "-fexceptions" //使用的c++標(biāo)準(zhǔn)
                abiFilters "armeabi"
            }
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt" //CMakeLists 配置文件路徑
        }
    }

}

就是修改 externalNativeBuild ,有注釋的兩個(gè)地方

進(jìn)入下一步

二厅贪、修改MakeLists.txt

直接加注釋貼上來吧

# cmake參考 http://www.reibang.com/p/528eeb266f83

cmake_minimum_required(VERSION 3.4.1)


# 需要引入我們頭文件,以這個(gè)配置的目錄為基準(zhǔn)
include_directories(src/main/jniLibs/include)

# 添加共享庫(so)搜索路徑
LINK_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/main/jniLibs/armeabi)

# 指定源文件目錄穿剖,把當(dāng)前工程目錄下的 src/main/cpp 目錄的下的所有 .cpp 和 .c 文件賦值給 SRC_LIST
AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR}/src/main/cpp SRC_LIST)


#這個(gè)一般不用怎么改,最終把cpp編譯成共享庫 native-lib
add_library(
        # 編譯生成的庫的名稱叫native-lib卦溢,對(duì)應(yīng)System.loadLibrary("native-lib");
        native-lib
        # Sets the library as a shared library.
        SHARED
        # Provides a relative path to your source file(s).
        ${SRC_LIST}
)


#為native-lib 鏈接額外的庫糊余,例如ffmpeg、ndk庫
target_link_libraries(
        native-lib
        # 編解碼(最重要的庫)
        avcodec-57
        # 設(shè)備信息
        avdevice-57
        # 濾鏡特效處理庫
        avfilter-6
        # 封裝格式處理庫
        avformat-57
        # 工具庫(大部分庫都需要這個(gè)庫的支持)
        avutil-55
        # 后期處理
        postproc-54
        # 音頻采樣數(shù)據(jù)格式轉(zhuǎn)換庫
        swresample-2
        # 視頻像素?cái)?shù)據(jù)格式轉(zhuǎn)換
        swscale-4
        # 鏈接 android ndk 自帶的一些庫
        android
        # Links the target library to the log library
        # included in the NDK.
        log)

當(dāng)然单寂,這些是最簡單的贬芥,實(shí)際項(xiàng)目應(yīng)該會(huì)很復(fù)雜。

然后直接run宣决,如果沒有報(bào)錯(cuò)蘸劈,說明FFmpeg動(dòng)態(tài)庫已經(jīng)集成到Android項(xiàng)目里了

驗(yàn)證一下:
使用 APK Analyzer查看。
選擇 Build > Analyze APK > 選擇apk打開

發(fā)現(xiàn)里so已經(jīng)打包進(jìn)去了

然后就可以通過JNI來調(diào)用FFmpeg了

三尊沸、來個(gè)簡單的例子

一個(gè)mp3文件威沫,使用FFmpeg來獲取音頻信息

1、在sd卡中放一個(gè) input1.mp3
2洼专、Activity中處理棒掠,讀取sd卡動(dòng)態(tài)權(quán)限就不貼了

 File mMusicFile = new File(Environment.getExternalStorageDirectory(), "input1.mp3");

//onCreate調(diào)用
printAudioInfo(mMusicFile.getAbsolutePath());


public native void printAudioInfo(String url);

// Used to load the 'native-lib' library on application startup.
static {
     System.loadLibrary("native-lib");
}

printAudioInfo 是 native方法,按option+enter屁商,自動(dòng)生成對(duì)應(yīng)c++方法


extern "C"
JNIEXPORT void JNICALL
Java_com_lanshifu_ffmpegdemo_MainActivity_printAudioInfo(JNIEnv *env,jobject instance,jstring url_) {

}

用cmake 的好處之一就是c++方法可以自動(dòng)生成烟很,不用什么javah獲取頭文件,然后復(fù)制啥的...

上代碼吧

#include <jni.h>
#include <string>
#include <android/log.h>
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)

extern "C"
JNIEXPORT void JNICALL
Java_com_lanshifu_ffmpegdemo_MainActivity_printAudioInfo(JNIEnv *env, jobject instance,
                                                         jstring url_) {
    const char *url = env->GetStringUTFChars(url_, 0);

    //1、初始化所有組件雾袱,只有調(diào)用了該函數(shù)恤筛,才能使用復(fù)用器和編解碼器(源碼)
    av_register_all();
    AVFormatContext *avFormatContext = NULL;
    int audio_stream_idx;
    AVStream *audio_stream;
    //讀文件頭,對(duì) mp4 文件而言芹橡,它會(huì)解析所有的 box毒坛。但它知識(shí)把讀到的結(jié)果保存在對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)下。
    // 這個(gè)時(shí)候林说,AVStream 中的很多字段都是空白的粘驰。
    int open_res = avformat_open_input(&avFormatContext, url, NULL, NULL);
    if (open_res != 0) {
        LOGE("lxb->Can't open file: %s", av_err2str(open_res));
        return ;
    }
    //獲取文件信息
    //讀取一部分視音頻數(shù)據(jù)并且獲得一些相關(guān)的信息,會(huì)檢測一些重要字段述么,如果是空白的蝌数,就設(shè)法填充它們。
    // 因?yàn)槲覀兘馕鑫募^的時(shí)候度秘,已經(jīng)掌握了大量的信息顶伞,avformat_find_stream_info 就是通過這些信息來填充自己的成員,
    // 當(dāng)重要的成員都填充完畢后剑梳,該函數(shù)就返回了唆貌。這中情況下,該函數(shù)效率很高垢乙。但對(duì)于某些文件锨咙,單純的從文件頭中獲取信息是不夠的,
    // 比如 video 的 pix_fmt 是需要調(diào)用 h264_decode_frame 才可以獲取其pix_fmt的追逮。
    int find_stream_info_res = avformat_find_stream_info(avFormatContext, NULL);
    if (find_stream_info_res < 0) {
        LOGE("lxb->Find stream info error: %s", av_err2str(find_stream_info_res));
        goto __avformat_close;
    }

    // 獲取采樣率和通道
    //av_find_best_stream:獲取音視頻及字幕的 stream_index , 以前沒有這個(gè)函數(shù)時(shí)酪刀,我們一般都是寫的 for 循環(huán)。
    audio_stream_idx = av_find_best_stream(avFormatContext, AVMediaType::AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (audio_stream_idx < 0) {
        LOGE("lxb->Find audio stream info error: %s", av_err2str(find_stream_info_res));
        goto __avformat_close;
    }
    audio_stream = avFormatContext->streams[audio_stream_idx];
    LOGE("采樣率:%d", audio_stream->codecpar->sample_rate);
    LOGE("通道數(shù): %d", audio_stream->codecpar->channels);
    LOGE("format: %d", audio_stream->codecpar->format);
    LOGE("extradata_size: %d", audio_stream->codecpar->extradata_size);

    __avformat_close:
    avformat_close_input(&avFormatContext);
}

解碼流程參考


解碼流程

FFmpeg API說明:
1:av_register_all() 初始化所有組件钮孵,只有調(diào)用了該函數(shù)骂倘,才能使用復(fù)用器和編解碼器
2:avformat_open_input:讀文件頭,對(duì) mp4 文件而言巴席,它會(huì)解析所有的 box历涝。但它只是把讀到的結(jié)果保存在對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)下
3:avformat_find_stream_info:讀取一部分視音頻數(shù)據(jù)并且獲得一些相關(guān)的信息。因?yàn)槲覀兘馕鑫募^的時(shí)候漾唉,已經(jīng)掌握了大量的信息荧库,avformat_find_stream_info 就是通過這些信息來填充自己的成員
4:av_find_best_stream:獲取音視頻及字幕的 stream_index , 以前沒有這個(gè)函數(shù)時(shí),我們一般都是寫的 for 循環(huán)
5:AVStream中保存了音視頻文件的一堆信息赵刑,在內(nèi)部AVCodecParameters 結(jié)構(gòu)體中

然后run一下分衫,可以看到打印

06-19 21:36:57.732 29875-29875/? E/JNI_TAG: 采樣率:44100
06-19 21:36:57.732 29875-29875/? E/JNI_TAG: 通道數(shù): 2
06-19 21:36:57.732 29875-29875/? E/JNI_TAG: format: 6
06-19 21:36:57.732 29875-29875/? E/JNI_TAG: extradata_size: 0

也就說明已經(jīng)成功在項(xiàng)目中使用FFmpeg了。

參考:http://www.reibang.com/p/d8300535bbf0

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末料睛,一起剝皮案震驚了整個(gè)濱河市丐箩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恤煞,老刑警劉巖屎勘,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異居扒,居然都是意外死亡概漱,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門喜喂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瓤摧,“玉大人,你說我怎么就攤上這事玉吁≌彰郑” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵进副,是天一觀的道長这揣。 經(jīng)常有香客問我,道長影斑,這世上最難降的妖魔是什么给赞? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮矫户,結(jié)果婚禮上片迅,老公的妹妹穿的比我還像新娘。我一直安慰自己皆辽,他們只是感情好柑蛇,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著驱闷,像睡著了一般唯蝶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遗嗽,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天粘我,我揣著相機(jī)與錄音,去河邊找鬼痹换。 笑死征字,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的娇豫。 我是一名探鬼主播匙姜,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼冯痢!你這毒婦竟也來了氮昧?” 一聲冷哼從身側(cè)響起框杜,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎袖肥,沒想到半個(gè)月后咪辱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡椎组,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年油狂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寸癌。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡专筷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蒸苇,到底是詐尸還是另有隱情磷蛹,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布溪烤,位于F島的核電站弦聂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏氛什。R本人自食惡果不足惜莺葫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望枪眉。 院中可真熱鬧捺檬,春花似錦、人聲如沸贸铜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蒿秦。三九已至烤镐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間棍鳖,已是汗流浹背炮叶。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留渡处,地道東北人镜悉。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像医瘫,于是被迫代替她去往敵國和親侣肄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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