【FFmpeg视频播放器开发】解封装解码流程、常用API和结构体简介(一)
一、前言
在正式編寫 FFmpeg 播放器前,我們需要先簡單了解下所要用到的 FFmpeg 庫、播放與解碼流程、函數(shù)和相關(guān)結(jié)構(gòu)體。
二、FFmpeg 庫簡介
| avcodec | 音視頻編解碼核心庫 |
| avformat | 音視頻容器格式的封裝和解析 |
| avutil | 核心工具庫 |
| swscal | 圖像格式轉(zhuǎn)換的模塊 |
| swresampel | 音頻重采樣 |
| avfilter | 音視頻濾鏡庫 如視頻加水印、音頻變聲 |
| avdevice | 輸入輸出設(shè)備庫,提供設(shè)備數(shù)據(jù)的輸入與輸出 |
FFmpeg 就是依靠以上幾個庫,實現(xiàn)了強(qiáng)大的音視頻編碼、解碼、編輯、轉(zhuǎn)換、采集等能力。這里實現(xiàn)視頻播放就除了 avfilter 庫沒用到。
三、FFmpeg播放流程
通常情況下,視頻文件如 MP4,MKV、FLV 等都屬于封裝格式,就是將已經(jīng)壓縮編碼的視頻數(shù)據(jù)和音頻數(shù)據(jù)按照一定的格式放到一起。當(dāng)我們播放一個媒體文件時,通常需要經(jīng)過以下幾個步驟:
可以看到這個視頻播放器的實現(xiàn)需要涉及到以下內(nèi)容:
-
解封裝(Demuxing):就是將輸入的封裝格式的數(shù)據(jù),分離成為音頻流壓縮編碼數(shù)據(jù)和視頻流壓縮編碼數(shù)據(jù)。例如,FLV 格式的數(shù)據(jù),經(jīng)過解封裝操作后,輸出 H.264 編碼的視頻碼流和 AAC 編碼的音頻碼流。
-
軟硬件解碼(Decode):就是將視頻/音頻壓縮編碼數(shù)據(jù),解碼成為非壓縮的視頻/音頻原始數(shù)據(jù)。通過解碼,將壓縮編碼的視頻數(shù)據(jù) H.264,MPEG2 解碼成為非壓縮的顏色數(shù)據(jù),例如 YUV 等等;將壓縮編碼的音頻數(shù)據(jù) AAC,MP3 解碼成為非壓縮的音頻抽樣數(shù)據(jù),例如 PCM 數(shù)據(jù)。解碼分為硬編碼和軟編碼。
-
像素格式轉(zhuǎn)換:將 YUV 數(shù)據(jù)格式轉(zhuǎn)換成 RGB 數(shù)據(jù)格式。
-
重采樣:對音頻重新采樣。
-
dts/pts:dts 是解碼的時間戳,而 pts 是顯示的時間戳。pts 用于獲取當(dāng)前播放進(jìn)度。進(jìn)度條移動需要用到av_seek_frame函數(shù)。
-
音視頻同步:就是根據(jù)解封裝模塊處理過程中獲取到的參數(shù)信息,同步解碼出來的音頻和視頻數(shù)據(jù),并將音視頻頻數(shù)據(jù)送至系統(tǒng)的顯卡和聲卡播放出來(Render)。
其中解碼是最重要的,下面介紹一下解碼的流程以及用到的 API 和結(jié)構(gòu)體。
★文末名片可以免費領(lǐng)取音視頻開發(fā)學(xué)習(xí)資料,內(nèi)容包括(C/C++,Linux?服務(wù)器開發(fā),FFmpeg?,webRTC?,rtmp?,hls?,rtsp?,ffplay?,srs)以及音視頻學(xué)習(xí)路線圖等等。
見下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
?
四、FFmpeg解碼流程
五、使用到的FFmpeg API說明
5.1 av_register_all()
-
注冊 FFmpeg 的所有組件。
-
在 4.0 版本以后已經(jīng)被棄用,所以實際不加也可以正常編解碼音視頻。
5.2 avformat_alloc_context()
用于初始化 AVFormatContext 對象。其原型如下:
AVFormatContext *avformat_alloc_context(void)-
由于 AVFormatContext 必須初始化為 NULL 或者用avformat_alloc_context()進(jìn)行初始化。
5.3 avformat_open_input()
打開媒體文件,并獲得解封裝上下文。其原型如下:
int avformat_open_input(AVFormatContext **ps, const char *url, AVInputFormat *fmt, AVDictionary **options)-
ps:AVFormatContext 雙重指針,函數(shù)調(diào)用成功之后將解封裝上下文賦值給 ps。
-
url:可以是 rtsp、http 網(wǎng)絡(luò)流地址,或者本地視頻文件路徑。
-
fmt:指定輸入音視頻的封裝格式,一般情況下可以設(shè)置為 nullptr,則會自動探索。
-
fmt:強(qiáng)制指定 AVFormatContext 的成員 AVInputFormat,即輸入音視頻的封裝格式。一般情況下可以設(shè)置為 NULL,這樣會自動探索 AVInputFormat。
-
options:附加的一些選項,一般情況下可以設(shè)置為 nullptr,但有時候播放 rtsp 時需要設(shè)置下。
5.4 avformat_find_stream_info()
探測獲取流信息。其原型如下:
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)-
因為在一些格式當(dāng)中沒有頭部信息,如 flv 格式,h264 格式,調(diào)用avformat_open_input()在打開文件之后就沒有參數(shù),也就無法獲取到里面的信息。
-
這個時候就可以調(diào)用此函數(shù),因為它會試著去探測文件的格式,但是如果格式當(dāng)中沒有頭部信息,那么它只能獲取到編碼、寬高這些信息,還是無法獲得總時長。
-
如果總時長無法獲取到,則需要把整個文件讀一遍,獲取一下它的總幀數(shù)來計算。
5.5 avcodec_find_decoder()
查找解碼器。函數(shù)的參數(shù)是所要用解碼器的ID,成功返回查找到的解碼器(沒有找到就返回 NULL)。其原型如下:
AVCodec *avcodec_find_decoder(enum AVCodecID id);-
id:查找到的解碼器
5.6 avcodec_open2()
用于初始化一個音視頻編解碼器的 AVCodecContext,聲明位于 libavcodec\utils.c。其原型如下:
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options)-
avctx:需要初始化的 AVCodecContext。
-
codec:輸入的AVCodec。
-
options:一些選項。例如使用 libx264 編碼的時候,“preset”,“tune” 等都可以通過該參數(shù)設(shè)置。
5.7 av_read_frame()
讀取碼流中的音頻若干幀或者視頻一幀。例如,解碼視頻的時候,每解碼一個視頻幀,需要先調(diào)用av_read_frame()獲得一幀視頻的壓縮數(shù)據(jù),然后才能對該數(shù)據(jù)進(jìn)行解碼。其原型如下:
int av_read_frame(AVFormatContext *s, AVPacket *pkt)-
s:解封裝上下文。
-
pkt:存儲一幀視頻的壓縮數(shù)據(jù)。
5.8 avcodec_decode_video2()
解碼一幀視頻數(shù)據(jù)。輸入一個壓縮編碼的結(jié)構(gòu)體 AVPacket,輸出一個解碼后的結(jié)構(gòu)體 AVFrame。其原型如下:
int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,int *got_picture_ptr,const AVPacket *avpkt);-
avctx:需要初始化的 AVCodecContext。
-
codec:輸入的AVCodec。
-
options:一些選項。例如使用libx264編碼的時候,“preset”,“tune”等都可以通過該參數(shù)設(shè)置。
5.9 avformat_close_input()
關(guān)閉釋放解封裝上下文,并且設(shè)置為 0。其原型如下:
void avformat_close_input(AVFormatContext **s)-
s:解封裝上下文。
六、使用到的FFmpeg結(jié)構(gòu)體說明
6.1 AVFormatContext
解封裝上下文,是存儲音視頻封裝格式中包含信息的結(jié)構(gòu)體。
char filename[1024] // 保存打開的文件名,一般用在 rtsp、rtmp 斷開重連 unsigned int nb_streams // 音視頻流的個數(shù) AVStream **streams // 存儲視頻流、音頻流、字幕流信息 int64_t duration // 媒體文件的總時長,單位是把 1 秒切成 AV_TIME_BASE(1000000)份,即單位。為 us,注意不一定每個視頻都能獲取到 duration int64_t bit_rate // 比特率(單位bps,轉(zhuǎn)換為kbps需要除以1000)6.2 AVStream
AVStream 是存儲每一個音頻/視頻流信息的結(jié)構(gòu)體。其重要的變量如下所示:
int index // 標(biāo)識該視頻/音頻流 AVCodecContext *codec // 解碼器,4.0 版本后已棄用 AVRational time_base // 時基。通過該值可以把PTS,DTS轉(zhuǎn)化為實際的時間(單位為秒s) int64_t duration // 該視頻/音頻流時長,單位為 ms AVRational avg_frame_rate // 幀率(注:對視頻來說,這個挺重要的) AVPacket attached_pic // 附帶的圖片。比如說一些 MP3,AAC 音頻文件附帶的專輯封面 AVCodecParameters *codecpar // 音視頻參數(shù),新增用來替換AVCodecContext *codec6.3 AVCodecContext
AVCodecContext 是一個描述編解碼器上下文的結(jié)構(gòu)體,包含了眾多編解碼器需要的參數(shù)信息。下面挑一些關(guān)鍵的變量來看看(這里只考慮解碼)。
enum AVMediaType codec_type // 編解碼器的類型(視頻,音頻...) struct AVCodec *codec // 采用的解碼器AVCodec(H.264,MPEG2...) ? ? enum AVCodecID codec_id // 標(biāo)示特定的編解碼器(H.264,MPEG2...) int format // 視頻像素格式/音頻采樣數(shù)據(jù)格式 int width, height // 表示視頻的寬和高 int bit_rate // 平均比特率 ? ? int channels // 聲道數(shù)(音頻) uint64_t channel_layout // 聲道格式 int sample_rate // 采樣率(音頻) AVRational time_base; // 時基。通過該值可以把PTS,DTS轉(zhuǎn)化為實際的時間(單位為秒s) uint8_t *extradata; int extradata_size; // 針對特定編碼器包含的附加信息(例如對于H.264解碼器來說,存儲SPS,PPS等)6.4 AVCodec
AVCodec 是存儲編碼器信息的結(jié)構(gòu)體。其重要的變量如下所示:
const char *name; // 編解碼器的名字的簡稱 const char *long_name; // 編解碼器名字的全稱 enum AVMediaType type; // 指明了類型,是視頻,音頻,還是字幕 enum AVCodecID id; // ID,不重復(fù) const AVRational *supported_framerates; // 支持的幀率(僅視頻) const enum AVPixelFormat *pix_fmts; // 支持的像素格式(僅視頻),如RGB24、YUV420P等。 const int *supported_samplerates; // 支持的采樣率(僅音頻) const enum AVSampleFormat *sample_fmts; // 支持的采樣格式(僅音頻) const uint64_t *channel_layouts; // 支持的聲道數(shù)(僅音頻) int priv_data_size; // 私有數(shù)據(jù)的大小6.5 AVCodecParameters
新增用來替換AVCodecContext *codec。因為 AVCodecContext 結(jié)構(gòu)體包含的參數(shù)太多,AVCodecParameters 將編碼器的參數(shù)從 AVCodecContext 分離出來,AVCodecParameters 結(jié)構(gòu)體中部分重要的參數(shù)如下:
enum AVMediaType codec_type // 編解碼器的類型(視頻,音頻...) ? enum AVCodecID codec_id // 標(biāo)示特定的編解碼器(H.264,MPEG2...) int format // 視頻像素格式/音頻采樣數(shù)據(jù)格式 int width, height // 表示視頻的寬和高 int bit_rate // 平均比特率 ? ? int channels // 聲道數(shù)(音頻) uint64_t channel_layout // 聲道格式 int sample_rate // 采樣率(音頻) AVRational time_base; // 時基。通過該值可以把PTS,DTS轉(zhuǎn)化為實際的時間(單位為秒s) uint8_t *extradata; int extradata_size; // 針對特定編碼器包含的附加信息(例如對于H.264解碼器來說,存儲SPS,PPS等)可以看到兩者的成員基本一致。
avcodec_decode_video2():解碼一幀視頻數(shù)據(jù) sws_scale():轉(zhuǎn)換視頻數(shù)據(jù)格式 ? ? av_frame_free():釋放xx上下文申請的內(nèi)存 avcodec_close():關(guān)閉解碼器6.6 AVPacket
AVPacket 是存儲壓縮編碼數(shù)據(jù)相關(guān)信息的結(jié)構(gòu)體。其重要的變量如下所示:
uint8_t *data; // 壓縮編碼的數(shù)據(jù)。 /* 例如對于H.264來說。1個AVPacket的data通常對應(yīng)一個NAL。 注意:在這里只是對應(yīng),而不是一模一樣。他們之間有微小的差別:使用FFMPEG類庫分離出多媒體文件中的H.264碼流。因此在使用FFMPEG進(jìn)行音視頻處理的時候,常??梢詫⒌玫降腁VPacket的data數(shù)據(jù)直接寫成文件,從而得到音視頻的碼流文件。*/ int size; // data的大小 int64_t pts; // 顯示時間戳 int64_t dts; // 解碼時間戳 int stream_index; // 標(biāo)識該AVPacket所屬的視頻/音頻流。6.7 AVFrame
AVFrame 結(jié)構(gòu)體一般用于存儲原始數(shù)據(jù)(即非壓縮數(shù)據(jù),例如對視頻來說是 YUV,RGB,對音頻來說是 PCM),此外還包含了一些相關(guān)的信息。比如說,解碼的時候存儲了宏塊類型表,QP 表,運動矢量表等數(shù)據(jù)。編碼的時候也存儲了相關(guān)的數(shù)據(jù)。因此在使用 FFmpeg 進(jìn)行碼流分析的時候,AVFrame 是一個很重要的結(jié)構(gòu)體。
下面看幾個主要變量的作用(在這里考慮解碼的情況):
uint8_t *data[AV_NUM_DATA_POINTERS]; // 解碼后原始數(shù)據(jù)(對視頻來說是YUV,RGB,對音頻來說是PCM) int linesize[AV_NUM_DATA_POINTERS]; // data中“一行”數(shù)據(jù)的大小。注意:未必等于圖像的寬,一般大于圖像的寬。 int width, height; // 視頻幀寬和高(1920x1080,1280x720...) int nb_samples; // 音頻的一個AVFrame中可能包含多個音頻幀,在此標(biāo)記包含了幾個 int format; // 解碼后原始數(shù)據(jù)類型(YUV420,YUV422,RGB24...) int key_frame; // 是否是關(guān)鍵幀 enum AVPictureType pict_type; // 幀類型(I,B,P...) AVRational sample_aspect_ratio; // 寬高比(16:9,4:3...) int64_t pts; // 顯示時間戳 int coded_picture_number; // 編碼幀序號 int display_picture_number; // 顯示幀序號參考:
【雷神 - 解碼】
圖解FFMPEG打開媒體的函數(shù)avformat_open_input
FFmpeg 源代碼簡單分析:avformat_open_input()
FFmpeg 源代碼簡單分析:avformat_find_stream_info()
FFmpeg 源代碼簡單分析:av_read_frame()
FFmpeg 源代碼簡單分析:avcodec_decode_video2()
FFmpeg 源代碼簡單分析:avformat_close_input()
【雷神 - FFmpeg結(jié)構(gòu)體】
FFMPEG中最關(guān)鍵的結(jié)構(gòu)體之間的關(guān)系
FFMPEG結(jié)構(gòu)體分析:AVFrame
FFMPEG結(jié)構(gòu)體分析:AVFormatContext
FFMPEG結(jié)構(gòu)體分析:AVCodecContext
FFMPEG結(jié)構(gòu)體分析:AVIOContext
FFMPEG結(jié)構(gòu)體分析:AVCodec
FFMPEG結(jié)構(gòu)體分析:AVStream
FFMPEG結(jié)構(gòu)體分析:AVPacket
作者:fengMisaka
★文末名片可以免費領(lǐng)取音視頻開發(fā)學(xué)習(xí)資料,內(nèi)容包括(C/C++,Linux?服務(wù)器開發(fā),FFmpeg?,webRTC?,rtmp?,hls?,rtsp?,ffplay?,srs)以及音視頻學(xué)習(xí)路線圖等等。
見下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
?
總結(jié)
以上是生活随笔為你收集整理的【FFmpeg视频播放器开发】解封装解码流程、常用API和结构体简介(一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 转:钉钉群直播提取视频文件-电脑版
- 下一篇: 2021年新版本下载钉钉群直播回放视频方