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


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

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

image

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

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


目標(biāo)


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

編譯 ffmpeg


1.下載

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

image

下載完成后解壓,然后進(jìn)入到ffmpeg源碼根目錄。

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

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

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

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

3.編譯ffmpeg

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

make && make install

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

image

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

4.編譯libfdk-acc

由于啟用了fdk-acc,需要單獨(dú)編譯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: 存儲(chǔ)ffmpeg靜態(tài)庫(kù)與頭文件
FFmpegPlayer: Objective-C實(shí)現(xiàn)
FFmpegPlayer-Swift: Swift實(shí)現(xiàn)

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

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

image

到此,編碼前的準(zhǔn)備工作就做好了??????。

了解 ffmpeg 初始化流程


在編碼前,需要搞清楚ffmpeg的基本解碼流程,下圖大致描述了ffmpeg軟解碼的流程,接下來(lái)對(duì)每一步進(jìn)行一個(gè)說(shuō)明
image

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

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

2.找到音、視頻流信息

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

這個(gè)函數(shù)會(huì)讀取少量的數(shù)據(jù)包方便找到精確的音視頻流信息,這些包會(huì)被緩存起來(lái),解碼的時(shí)候不會(huì)重復(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ù)信息,這個(gè)結(jié)構(gòu)體里包括了視頻的寬高、FPS、視頻長(zhǎng)度等信息;如果是音頻它包括了采樣率、聲道、音頻數(shù)據(jù)包、音頻格式等信息。

AVCodecParameters *codecParameters = stream->codecpar;

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

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

找到解碼器之后,需要實(shí)例化一個(gè)解碼器上下文

AVCodecContext *codecContext = avcodec_alloc_context3(codec);

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

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

avcodec_parameters_to_context(codecContext, codecParameters);

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

avcodec_open2(codecContext, codec, NULL);

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

打印相關(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)常使用到的一個(gè)結(jié)構(gòu)體: AVRational。
它的定義很簡(jiǎn)單,如下:

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

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


image

它表示將1秒分成11988,每一分代表0.000083416750083416754秒,而stream->duration的值表示基于這個(gè)time_base它總共有2490800份,將2490800 * 0.000083416750083416754就得到了這個(gè)視頻總共有多少秒。
而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(@"=========================================================");

音頻的這幾個(gè)信息很重要,音頻播放器初始化也需要用到這幾個(gè)信息。

總結(jié):


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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容