?該節(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í)的深入可能會有增加或者刪除。
??第一節(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-C
與Swift
兩種實現(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下載最新的源碼灰蛙。
下載完成后解壓,然后進入到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)是這樣的
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)resources
: 存放音頻、視頻等
vender
: 存儲ffmpeg靜態(tài)庫與頭文件
FFmpegPlayer
: Objective-C實現(xiàn)
FFmpegPlayer-Swift
: Swift實現(xiàn)
2.設(shè)置頭文件搜索目錄
到此,編碼前的準備工作就做好了??????驾霜。
了解 ffmpeg 初始化流程
在編碼前,需要搞清楚ffmpeg的基本解碼流程,下圖大致描述了ffmpeg軟解碼的流程,接下來對每一步進行一個說明
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,它直譯出來就是時間基煌茴。
它表示將1秒分成11988,每一分代表0.000083416750083416754秒,而stream->duration的值表示基于這個time_base它總共有2490800份,將2490800 * 0.000083416750083416754就得到了這個視頻總共有多少秒。
而av_q2d函數(shù)就是num/den日川。
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)注公眾號: <<程序猿搬磚>>