ffmpeg開發(fā)播放器學(xué)習(xí)筆記 - Hello FFmpeg


?該節(jié)是ffmpeg開發(fā)播放器學(xué)習(xí)筆記的開篇《Hello FFmpeg》

ffmpeg是一個跨平臺的音視頻錄制、轉(zhuǎn)換搬俊、編解碼的庫莹桅。使用C語言編寫而成,可在主流移動與PC平臺上使用腾么。ffmpeg不僅提供可嵌入到App中的庫,還提供了可以直接使用的工具。掌握必要的ffmpeg基礎(chǔ)使用與基本的音視頻信息對于端開發(fā)者還是很有必要的砚蓬。本系列記錄了學(xué)習(xí)ffmpeg開發(fā)播放器的過程,目前規(guī)劃的小節(jié)并不是全部,隨著學(xué)習(xí)的深入可能會有增加或者刪除。

image

??第一節(jié) - Hello FFmpeg
?? 第二節(jié) - 軟解視頻流,渲染 RGB24
?? 第三節(jié) - 認識YUV
?? 第四節(jié) - 硬解碼,OpenGL渲染YUV
?? 第五節(jié) - Metal 渲染YUV
?? 第六節(jié) - 解碼音頻,使用AudioQueue 播放
?? 第七節(jié) - 音視頻同步
?? 第八節(jié) - 完善播放控制
?? 第九節(jié) - 倍速播放
?? 第十節(jié) - 增加視頻過濾效果
?? 第十一節(jié) - 音頻變聲

該節(jié) Demo 地址: https://github.com/czqasngit/ffmpeg-player/releases/tag/Hello-FFmpeg
實例代碼提供了Objective-CSwift兩種實現(xiàn),為了方便說明,文章引用的是Objective-C代碼,因為Swift代碼指針看著不簡潔盆色。


目標


  • 編譯 ffmpeg,生成x86_64靜態(tài)庫
  • 搭建 macOS+ffmpeg 開發(fā)環(huán)境
  • 了解 ffmpeg 初始化流程
  • 使用 ffmpeg 并打印音視頻信息

編譯 ffmpeg


1.下載

ffmpeg下載地址是 https://ffmpeg.org/download.html#get-sources,點擊Download Source Code下載最新的源碼灰蛙。

image

下載完成后解壓,然后進入到ffmpeg源碼根目錄祟剔。

2.配置編譯參數(shù)

終端進入到ffmpeg源碼目錄,輸入以下配置命令配置一個基礎(chǔ)版本的編譯參數(shù)

./configure  --prefix=./macos --enable-gpl --enable-nonfree --enable-libfdk-aac

--prefix指定了編譯產(chǎn)物的輸出目錄,這里指定輸出到當(dāng)前目錄下的macos目錄,提前創(chuàng)建好這個目錄摩梧。
--enable-gpl允許使用GPL代碼物延,生成的庫和二進制文件將在GPL下
--enable-nofree允許使用非自由代碼,生成的庫和二進制文件將是不可分發(fā)的
--enable-libfdk-aac 使用libfdk-acc對ACC音頻流進行編碼或者解碼

3.編譯ffmpeg

使用以下命令編譯并將產(chǎn)物輸出到macos目錄

make && make install

生成后的產(chǎn)物目錄結(jié)構(gòu)是這樣的

image

bin: 可直接使用的工具ffmpeg與ffprobe
include: 集成ffmpeg庫時的頭文件
lib: 集成ffmpeg的靜態(tài)庫
share: 文檔障本、實例程序等

4.編譯libfdk-acc

由于啟用了fdk-acc,需要單獨編譯libfdk-acc教届。
下載地址:http://www.linuxfromscratch.org/blfs/view/svn/multimedia/fdk-aac.html
配置編譯參數(shù),需要先創(chuàng)建macos目錄

./configure --prefix=./macos --disable-static
make && make install

搭建 macOS+ffmpeg 開發(fā)環(huán)境


1.新建工程

新建macOS工程,并創(chuàng)建以下目錄結(jié)構(gòu)
image

resources: 存放音頻、視頻等
vender: 存儲ffmpeg靜態(tài)庫與頭文件
FFmpegPlayer: Objective-C實現(xiàn)
FFmpegPlayer-Swift: Swift實現(xiàn)

創(chuàng)建好工程之后,導(dǎo)入ffmpeg庫與頭文件
image

2.設(shè)置頭文件搜索目錄

image

到此,編碼前的準備工作就做好了??????驾霜。

了解 ffmpeg 初始化流程


在編碼前,需要搞清楚ffmpeg的基本解碼流程,下圖大致描述了ffmpeg軟解碼的流程,接下來對每一步進行一個說明
image

1.打開文件/數(shù)據(jù)流

/// formatContet: AVFormatContext,保存了音視頻文件信息
/// url: 需要打開的音視頻文件地址
/// fmt: 指定打開的音視頻文件的格式,如果不指定則自動推導(dǎo)
/// options: 設(shè)置AVFormatContext的options,它的默認值定義在:libavformat/options_table.h
/// 說明: AVFormatContext是一個AVClass,可以通過鍵值讀取與設(shè)置定義的相關(guān)屬性int ret = avformat_open_input(&formatContext, url, NULL, NULL);

2.找到音案训、視頻流信息

/// formatContet: AVFormatContext,保存了音視頻文件信息
/// options: 如果配置了,則流信息會被保存到里面,這里不需要保存輸入NULL
ret = avformat_find_stream_info(formatContext, NULL);

這個函數(shù)會讀取少量的數(shù)據(jù)包方便找到精確的音視頻流信息,這些包會被緩存起來,解碼的時候不會重復(fù)讀取。

3.遍歷流信息

從已經(jīng)讀取到的流信息中遍歷查找到音頻與視頻的流信息

for(int i = 0; i < formatContext->nb_streams; i ++) {
    AVStream *stream = formatContext->streams[i];
    AVMediaType mediaType = stream->codecpar->codec_type;
    if(mediaType == AVMEDIA_TYPE_VIDEO) {
        _mediaVideo = [[FFMediaVideoContext alloc] initWithAVStream:stream formatContext:formatContext];
        if(!_mediaVideo) goto fail;
    } else if(mediaType == AVMEDIA_TYPE_AUDIO) {
        _mediaAudio = [[FFMediaAudioContext alloc] initWithAVStream:stream formatContext:formatContext];
        if(!_mediaAudio) goto fail;
    }
}

4.初始化音視頻解碼器

從AVStream中可以讀取到解碼器的參數(shù)信息,這個結(jié)構(gòu)體里包括了視頻的寬高粪糙、FPS强霎、視頻長度等信息;如果是音頻它包括了采樣率蓉冈、聲道城舞、音頻數(shù)據(jù)包、音頻格式等信息寞酿。

AVCodecParameters *codecParameters = stream->codecpar;

AVCodec定義了解碼器的功能,首先找到解碼對應(yīng)流信息的解碼器

AVCodec *codec = avcodec_find_decoder(codecParameters->codec_id);

找到解碼器之后,需要實例化一個解碼器上下文

AVCodecContext *codecContext = avcodec_alloc_context3(codec);

AVCodecContext持有AVCodec,在解碼時還需要運行時的一 些參數(shù),這些參數(shù)保存在AVCodecParameters中家夺。AVCodecContext可以看作運行時的AVCodec, 使用之前還需要將參數(shù)填充到AVCodeContext中,后續(xù)的解碼操作都使用AVCodecContext。

初始化完成之后還需要將流信息參數(shù)填充到AVCodecContext伐弹。

avcodec_parameters_to_context(codecContext, codecParameters);

解碼器默認是關(guān)閉的,在解碼使用前需要先打開

avcodec_open2(codecContext, codec, NULL);

到此,就完成了音視頻解碼器的初始化,這個是軟解碼的基本流程拉馋。

打印相關(guān)音視頻信息


1.打印視頻流信息

NSLog(@"=================== Video Information ===================");
NSLog(@"FPS: %f", av_q2d(stream->avg_frame_rate));
NSLog(@"Duration: %d Seconds", (int)(stream->duration * av_q2d(stream->time_base)));
NSLog(@"Size: (%d, %d)", self->codecContext->width, self->codecContext->height);
NSLog(@"Decodec: %s", self->codec->long_name);
NSLog(@"=========================================================");

這里需要注意在ffmpeg中經(jīng)常使用到的一個結(jié)構(gòu)體: AVRational。
它的定義很簡單,如下:

typedef struct AVRational{    int num; ///< Numerator    int den; ///< Denominator} AVRational;

分母與分子兩個參數(shù)定義了一個值,使用這樣一個結(jié)構(gòu)可以很靈活的表達任意一個小數(shù)惨好。
比如這里的在AVStream中的變量time_base,它直譯出來就是時間基煌茴。


image

它表示將1秒分成11988,每一分代表0.000083416750083416754秒,而stream->duration的值表示基于這個time_base它總共有2490800份,將2490800 * 0.000083416750083416754就得到了這個視頻總共有多少秒。
而av_q2d函數(shù)就是num/den日川。

最終打印出視頻信息如下:
image

2.打印音頻信息

NSLog(@"=================== Audio Information ===================");
NSLog(@"Sample Rate: %d", codecContext->sample_rate);
NSLog(@"FMT: %d, %s", codecContext->sample_fmt, av_get_sample_fmt_name(codecContext->sample_fmt));
NSLog(@"Channels: %d", codecContext->channels);
NSLog(@"Channel Layout: %llu", codecContext->channel_layout);
NSLog(@"Decodec: %s", self->codec->long_name);
NSLog(@"=========================================================");

音頻的這幾個信息很重要,音頻播放器初始化也需要用到這幾個信息蔓腐。

總結(jié):


  • 了解了ffmpeg的功能,它不僅可以單獨使用現(xiàn)成的工具,還可以集成到App中使用其API
  • 編譯了ffmpeg并認識了編譯產(chǎn)物
  • 搭建了macOS+ffmpeg的開發(fā)環(huán)境
  • 使用ffmpeg并打印了音視頻流信息

如果你對文章感興趣,還可以關(guān)注公眾號: <<程序猿搬磚>>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市龄句,隨后出現(xiàn)的幾起案子回论,更是在濱河造成了極大的恐慌,老刑警劉巖分歇,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件透葛,死亡現(xiàn)場離奇詭異,居然都是意外死亡卿樱,警方通過查閱死者的電腦和手機僚害,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人萨蚕,你說我怎么就攤上這事靶草。” “怎么了岳遥?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵奕翔,是天一觀的道長。 經(jīng)常有香客問我浩蓉,道長派继,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任捻艳,我火速辦了婚禮驾窟,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘认轨。我一直安慰自己绅络,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布嘁字。 她就那樣靜靜地躺著恩急,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纪蜒。 梳的紋絲不亂的頭發(fā)上衷恭,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天,我揣著相機與錄音纯续,去河邊找鬼随珠。 笑死,一個胖子當(dāng)著我的面吹牛杆烁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播简卧,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼兔魂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了举娩?” 一聲冷哼從身側(cè)響起析校,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎铜涉,沒想到半個月后智玻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡芙代,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年吊奢,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纹烹。...
    茶點故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡页滚,死狀恐怖召边,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情裹驰,我是刑警寧澤隧熙,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站幻林,受9級特大地震影響贞盯,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜沪饺,卻給世界環(huán)境...
    茶點故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一躏敢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧随闽,春花似錦父丰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至魏滚,卻和暖如春镀首,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背鼠次。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工更哄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腥寇。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓成翩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赦役。 傳聞我的和親對象是個殘疾皇子麻敌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,490評論 2 348

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