該節是ffmpeg開發播放器學習記要的開篇《Hello FFmpeg》
ffmeg是一個跨平台的音視訊錄制、轉換、編解碼的庫。使用C語言編寫而成,可在主流移動與PC平台上使用。ffmeg不僅提供可嵌入到App中的庫,還提供了可以直接使用的工具。掌握必要的ffmpeg基礎使用與基本的音視訊資訊對于端開發者還是很有必要的。本系列記錄了ffmpeg開發播放器的過程,目前規劃的小節并不是全部,随着學習的深入可能會有增加或者删除。
第一節 - Hello FFmpeg
第二節 - 軟解視訊流,渲染 RGB24
第三節 - 認識YUV
第四節 - 硬解碼,OpenGL渲染YUV
第五節 - Metal 渲染YUV
第六節 - 解碼音頻,使用AudioQueue 播放
第七節 - 音視訊同步
第八節 - 完善播放控制
第九節 - 倍速播放
第十節 - 增加視訊過濾效果
第十一節 - 音頻變聲
該小節 Demo 地
址:github.com/czqasngit/f…
執行個體代碼提供了Objective-C與Swift兩種實作
目标
- 編譯 ffmpeg,生成x86_64靜态庫
- 搭建 macOS+ffmpeg 開發環境
- 了解 ffmpeg 初始化流程
- 使用 ffmpeg 并列印音視訊資訊
編譯 ffmpeg
1.下載下傳
ffmpeg下載下傳位址是 ffmpeg.org/download.ht… Source Code下載下傳最新的源碼。
下載下傳完成後解壓,然後進入到ffmpeg源碼根目錄。
2.配置編譯參數
終端進入到ffmpeg源碼目錄,輸入以下配置指令配置一個基礎版本的編譯參數
./configure --prefix=./macos --enable-gpl --enable-nonfree --enable-libfdk-aac
--prefix 指定了編譯産物的輸出目錄,這裡指定輸出到目前目錄下的macos目錄,提前建立好這個目錄。
--enable-gpl 允許使用GPL代碼,生成的庫和二進制檔案将在GPL下
--enable-nofree 允許使用非自由代碼,生成的庫和二進制檔案将是不可分發的
--enable-libfdk-aac 使用libfdk-acc對ACC音頻流進行編碼或者解碼
3.編譯ffmpeg
使用以下指令編譯并将産物輸出到macos目錄
make && make install
生成後的産物目錄結構是這樣的
相關學習資料推薦,點選下方連結免費報名,先碼住不迷路~】
【免費分享】音視訊學習資料包、大廠面試題、技術視訊和學習路線圖,資料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以點選加群免費領取~
4.編譯libfdk-acc
由于啟用了fdk-acc,需要單獨編譯libfdk-acc。
下載下傳位址:www.linuxfromscratch.org/blfs/view/s…
配置編譯參數,需要先建立macos目錄
./configure --prefix=./macos --disable-static
make && make install
搭建 macOS+ffmpeg 開發環境
1.建立工程
建立macOS工程,并建立以下目錄結構
resources: 存放音頻、視訊等
vender: 存儲ffmpeg靜态庫與頭檔案
FFmpegPlayer: Objective-C工程
FFmpegPlayer-Swift: Swift工程
建立好工程之後,導入ffmpeg庫與頭檔案
2.設定頭檔案搜尋目錄
到此,編碼前的準備工作就做好了 。
了解 ffmpeg 初始化流程
在編碼前,需要搞清楚ffmpeg的基本解碼流程,下圖大緻描述了ffmpeg軟解碼的流程,接下來對每一步進行一個說明
1.打開檔案/資料流
/// formatContet: AVFormatContext,儲存了音視訊檔案資訊
/// url: 需要打開的音視訊檔案位址
/// fmt: 指定打開的音視訊檔案的格式,如果不指定則自動推導
/// options: 設定AVFormatContext的options,它的預設值定義在:libavformat/options_table.h
/// 說明: AVFormatContext是一個AVClass,可以通過鍵值讀取與設定定義的相關屬性
int ret = avformat_open_input(&formatContext, url, NULL, NULL);
2.找到音、視訊流資訊
/// formatContet: AVFormatContext,儲存了音視訊檔案資訊
/// options: 如果配置了,則流資訊會被儲存到裡面,這裡不需要儲存輸入NULL
ret = avformat_find_stream_info(formatContext, NULL);
這個函數會讀取少量的資料包友善找到精确的音視訊流資訊,這些包會被緩存起來,解碼的時候不會重複讀取。
3.周遊流資訊
從已經讀取到的流資訊中周遊查找到音頻與視訊的流資訊
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中可以讀取到解碼器的參數資訊,這個結構體裡包括了視訊的寬高、FPS、視訊長度等資訊;如果是音頻它包括了采樣率、聲道、音頻資料包、音頻格式、解碼器等資訊。
AVCodecParameters *codecParameters = stream->codecpar;
AVCodec定義了解碼器的功能,首先找到解碼對應流資訊的解碼器
AVCodec *codec = avcodec_find_decoder(codecParameters->codec_id);
找到解碼器之後,需要執行個體化一個解碼器上下文
AVCodecContext *codecContext = avcodec_alloc_context3(codec);
AVCodec定義了功能,AVCodecContext結構表示程式運作的目前 AVCodec 使用的上下文,着重于所有 AVCodec 共有的屬性(并且是在程式運作時才能确定其值)和關聯其他結構的字段。
AVCodecContext可以看作是AVCodec的一個具體實體表現,後續的解碼操作都使用AVCodecContext。AVCodecContext不僅包含了AVCodec定義的功能,還需要通過特定的參數來完成功能的調用,這些參數存儲在AVCodecParameters中。
初始化完成之後還需要将流資訊參數填充到AVCodecContext。
avcodec_parameters_to_context(codecContext, codecParameters);
AVCodecContext預設是關閉的,在解碼使用前需要先打開
avcodec_open2(codecContext, codec, NULL);
到此,就完成了音視訊解碼器的初始化,這個是軟解碼的基本流程。
列印相關音視訊資訊
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中經常使用到的一個結構體: AVRational。
它的定義很簡單,如下:
typedef struct AVRational{
int num; ///< Numerator
int den; ///< Denominator
} AVRational;
分母與分子兩個參數定義了一個值,使用這樣一個結構可以很靈活的表達任意一個小數。
比如這裡的在AVStream中的變量time_base,它直譯出來就是時間基。
它表示将1秒分成11988,每一分代表0.000083416750083416754秒,而stream->duration的值表示基于這個time_base它總共有2490800份,将2490800 * 0.000083416750083416754就得到了這個視訊總共有多少秒。
而av_q2d函數就是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(@"=========================================================");
音頻的這幾個資訊很重要,音頻播放器初始化也需要用到這幾個資訊。
總結:
- 了解了ffmpeg的功能,它不僅可以單獨使用現成的工具,還可以內建到App中使用其API
- 編譯了ffmpeg并認識了編譯産物
- 搭建了macOS+ffmpeg的開發環境
- 使用ffmpeg并列印了音視訊流資訊
更多内容請關注微信公衆号<<程式猿搬磚>>
原文 https://juejin.cn/post/6917170943676645390