天天看點

ffmpeg開發播放器學習筆記 - Hello FFmpeg

作者:音視訊流媒體技術

該節是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開發播放器學習筆記 - Hello FFmpeg

下載下傳完成後解壓,然後進入到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           

生成後的産物目錄結構是這樣的

ffmpeg開發播放器學習筆記 - Hello FFmpeg

相關學習資料推薦,點選下方連結免費報名,先碼住不迷路~】

【免費分享】音視訊學習資料包、大廠面試題、技術視訊和學習路線圖,資料包括(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工程,并建立以下目錄結構

ffmpeg開發播放器學習筆記 - Hello FFmpeg

resources: 存放音頻、視訊等

vender: 存儲ffmpeg靜态庫與頭檔案

FFmpegPlayer: Objective-C工程

FFmpegPlayer-Swift: Swift工程

建立好工程之後,導入ffmpeg庫與頭檔案

ffmpeg開發播放器學習筆記 - Hello FFmpeg

2.設定頭檔案搜尋目錄

ffmpeg開發播放器學習筆記 - Hello FFmpeg

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

了解 ffmpeg 初始化流程

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

ffmpeg開發播放器學習筆記 - Hello 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,它直譯出來就是時間基。

ffmpeg開發播放器學習筆記 - Hello FFmpeg

它表示将1秒分成11988,每一分代表0.000083416750083416754秒,而stream->duration的值表示基于這個time_base它總共有2490800份,将2490800 * 0.000083416750083416754就得到了這個視訊總共有多少秒。

而av_q2d函數就是num/den。

最終列印出視訊資訊如下:

ffmpeg開發播放器學習筆記 - Hello FFmpeg

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

繼續閱讀