播放器框架
常用音視頻術語
? 容器/文件(Conainer/File):即特定格式的多媒體文件,
比如mp4、flv、mkv等。
? 媒體流(Stream):表示時間軸上的一段連續(xù)數(shù)據(jù),如一
段聲音數(shù)據(jù)、一段視頻數(shù)據(jù)或一段字幕數(shù)據(jù),可以是壓縮
的,也可以是非壓縮的,壓縮的數(shù)據(jù)需要關聯(lián)特定的編解
碼器(有些碼流音頻他是純PCM)。
? 數(shù)據(jù)幀/數(shù)據(jù)包(Frame/Packet):通常,一個媒體流是
由大量的數(shù)據(jù)幀組成的,對于壓縮數(shù)據(jù),幀對應著編解碼
器的最小處理單元,分屬于不同媒體流的數(shù)據(jù)幀交錯存儲
于容器之中。
? 編解碼器:編解碼器是以幀為單位實現(xiàn)壓縮數(shù)據(jù)和原始數(shù)
據(jù)之間的相互轉(zhuǎn)換的。
常用概念-復用器
常用概念-編解碼器
FFmpeg的整體結(jié)構(gòu)
FFMPEG有8個常用庫
? AVUtil:核心工具庫,下面的許多其他模塊都會依賴該庫做一些基本的音視頻處理操作。
? AVFormat:文件格式和協(xié)議庫,該模塊是最重要的模塊之一,封裝了Protocol層和Demuxer、Muxer層,使得協(xié)議和格式對于開發(fā)者來說是透明的。
? AVCodec:編解碼庫,封裝了Codec層,但是有一些Codec是具備自己的License的,F(xiàn)Fmpeg是不會默認添加像libx264、FDK-AAC等庫的,但是FFmpeg就像一個平臺一樣,可以將其他的第三方的Codec以插件的方式添加進來,然后為開發(fā)者提供統(tǒng)一的接口。
? AVFilter:音視頻濾鏡庫,該模塊提供了包括音頻特效和視頻特效的處理,在使用FFmpeg的API進行編解碼的過程中,直接使用該模塊為音視頻數(shù)據(jù)做特效處理是非常方便同時也非常高效的一種方式。
? AVDevice:輸入輸出設備庫,比如,需要編譯出播放聲音或者視頻的工具ffplay,就需要確保該模塊是打開的,同時也需要SDL的預先編譯,因為該設備模塊播放聲音與播放視頻使用的都是SDL庫。
? SwrRessample:該模塊可用于音頻重采樣,可以對數(shù)字音頻進行聲道數(shù)、數(shù)據(jù)格式、采樣率等多種基本信息的轉(zhuǎn)換。
? SWScale:該模塊是將圖像進行格式轉(zhuǎn)換的模塊,比如,可以將YUV的數(shù)據(jù)轉(zhuǎn)換為RGB的數(shù)據(jù),縮放尺寸由1280720變?yōu)?00480。
? PostProc:該模塊可用于進行后期處理,當我們使用AVFilter的時候需要打開該模塊的開關,因為Filter中會使用到該模塊的一些基礎函數(shù)。
FFmpeg函數(shù)簡介
? av_register_all():注冊所有組件,4.0已經(jīng)棄用
? avdevice_register_all()對設備進行注冊,比如V4L2等。
? avformat_network_init();初始化網(wǎng)絡庫以及網(wǎng)絡加密協(xié)議相關的庫(比如openssl)
FFmpeg函數(shù)簡介-封裝格式相關
? avformat_alloc_context();負責申請一個AVFormatContext結(jié)構(gòu)的內(nèi)存,并進行簡單初始化
? avformat_free_context();釋放該結(jié)構(gòu)里的所有東西以及該結(jié)構(gòu)本身
? avformat_close_input();關閉解復用器。關閉后就不再需要使用avformat_free_context 進行釋放。
? avformat_open_input();打開輸入視頻文件
? avformat_find_stream_info():獲取音視頻文件信息
? av_read_frame(); 讀取音視頻包
? avformat_seek_file(); 定位文件
? av_seek_frame():定位文件
FFmpeg解碼函數(shù)簡介-解碼器相關
? avcodec_alloc_context3(): 分配解碼器上下文
? avcodec_find_decoder():根據(jù)ID查找解碼器
? avcodec_find_decoder_by_name():根據(jù)解碼器名字
? avcodec_open2(): 打開編解碼器
? avcodec_decode_video2():解碼一幀視頻數(shù)據(jù)
? avcodec_decode_audio4():解碼一幀音頻數(shù)據(jù)
? avcodec_send_packet(): 發(fā)送編碼數(shù)據(jù)包
? avcodec_receive_frame(): 接收解碼后數(shù)據(jù)
? avcodec_free_context():釋放解碼器上下文,包含了avcodec_close()
? avcodec_close():關閉解碼器
FFmpeg 3.x 組件注冊方式
我們使用ffmpeg,首先要執(zhí)行av_register_all,把全局的解碼器、編碼器等結(jié)構(gòu)體注冊到各自全局的對象鏈表里,以便后面查找調(diào)用。
FFmpeg 4.x 組件注冊方式
FFmpeg內(nèi)部去做,不需要用戶調(diào)用API去注冊。
以codec編解碼器為例:
-
在configure的時候生成要注冊的組件./configure:7203:print_enabled_components libavcodec/codec_list.c AVCodec codec_list $CODEC_LIST這里會生成一個codec_list.c 文件,里面只有static const AVCodec *
const codec_list[]數(shù)組。 -
在libavcodec/allcodecs.c將static const AVCodec * const codec_list[]的編解碼器用鏈表的方式組織起來。
Ffmpeg 4.0.2 組件注冊方式
FFmepg內(nèi)部去做,不需要用戶調(diào)用API去注冊。
對于demuxer/muxer(解復用器,也稱容器)則對應
-
libavformat/muxer_list.c libavformat/demuxer_list.c 這兩個文件也是在configure的時候生成,也就是說直接下載源碼是沒有這兩個文件的。
-
在libavformat/allformats.c將demuxer_list[]和muexr_list[]以鏈表的方式組織。
其他組件也是類似的方式
FFmpeg數(shù)據(jù)結(jié)構(gòu)簡介
AVFormatContext
封裝格式上下文結(jié)構(gòu)體,也是統(tǒng)領全局的結(jié)構(gòu)體,保存了視頻文件封裝格式相關信息。
AVInputFormat demuxer
每種封裝格式(例如FLV, MKV, MP4, AVI)對應一個該結(jié)構(gòu)體。
AVOutputFormat muxer
AVStream
視頻文件中每個視頻(音頻)流對應一個該結(jié)構(gòu)體。
AVCodecContext
編解碼器上下文結(jié)構(gòu)體,保存了視頻(音頻)編解碼相關信息。
AVCodec
每種視頻(音頻)編解碼器(例如H.264解碼器)對應一個該結(jié)構(gòu)體。
AVPacket
存儲一幀壓縮編碼數(shù)據(jù)。
AVFrame
存儲一幀解碼后像素(采樣)數(shù)據(jù)。
如果上下文數(shù)據(jù)保存在解碼器里面?
多路解碼的時候數(shù)據(jù)肯定有沖突。
FFmpeg數(shù)據(jù)結(jié)構(gòu)之間的關系
AVFormatContext和AVInputFormat之間的關系
AVFormatContext API調(diào)用
AVInputFormat 主要是FFMPEG內(nèi)部調(diào)用
AVCodecContext和AVCodec之間的關系
AVCodecContext 編碼器上下文結(jié)構(gòu)體
struct AVCodec *codec;
AVCodec 每種視頻(音頻)編解碼器
int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt);
int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr);
AVFormatContext, AVStream和AVCodecContext之間的關系
區(qū)分不同的碼流
? AVMEDIA_TYPE_VIDEO視頻流
video_index = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,-1,-1, NULL, 0)
? AVMEDIA_TYPE_AUDIO音頻流
audio_index = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO,-1,-1, NULL, 0)
AVPacket 里面也有一個index的字段
FFmpeg數(shù)據(jù)結(jié)構(gòu)分析
? AVFormatContext
? iformat:輸入媒體的AVInputFormat,比如指向AVInputFormat
ff_flv_demuxer
? nb_streams:輸入媒體的AVStream 個數(shù)
? streams:輸入媒體的AVStream []數(shù)組
? duration:輸入媒體的時長(以微秒為單位),計算方式可以參
考av_dump_format()函數(shù)。
? bit_rate:輸入媒體的碼率
? AVInputFormat
? name:封裝格式名稱
? extensions:封裝格式的擴展名
? id:封裝格式ID
? 一些封裝格式處理的接口函數(shù),比如read_packet()
? AVStream
? index:標識該視頻/音頻流
? time_base:該流的時基,PTS*time_base=真正的時間(秒)
? avg_frame_rate: 該流的幀率
? duration:該視頻/音頻流長度
? codecpar:編解碼器參數(shù)屬性
? AVCodecParameters
? codec_type:媒體類型,比如AVMEDIA_TYPE_VIDEO
AVMEDIA_TYPE_AUDIO等
? codec_id:編解碼器類型, 比如AV_CODEC_ID_H264
AV_CODEC_ID_AAC等。
? AVCodecContext
? codec:編解碼器的AVCodec,比如指向AVCodec
ff_aac_latm_decoder
? width, height:圖像的寬高(只針對視頻)
? pix_fmt:像素格式(只針對視頻)
? sample_rate:采樣率(只針對音頻)
? channels:聲道數(shù)(只針對音頻)
? sample_fmt:采樣格式(只針對音頻)
? AVCodec
? name:編解碼器名稱
? type:編解碼器類型
? id:編解碼器ID
? 一些編解碼的接口函數(shù),比如int (*decode)()
? AVCodecContext
? codec:編解碼器的AVCodec,比如指向AVCodec
ff_aac_latm_decoder
? width, height:圖像的寬高(只針對視頻)
? pix_fmt:像素格式(只針對視頻)
? sample_rate:采樣率(只針對音頻)
? channels:聲道數(shù)(只針對音頻)
? sample_fmt:采樣格式(只針對音頻)
? AVCodec
? name:編解碼器名稱
? type:編解碼器類型
? id:編解碼器ID
? 一些編解碼的接口函數(shù),比如int (*decode)()
AVPacket
? pts:顯示時間戳
? dts:解碼時間戳
? data:壓縮編碼數(shù)據(jù)
? size:壓縮編碼數(shù)據(jù)大小
? pos:數(shù)據(jù)的偏移地址
? stream_index:所屬的AVStream
AVFrame
? data:解碼后的圖像像素數(shù)據(jù)(音頻采樣數(shù)據(jù))
? linesize:對視頻來說是圖像中一行像素的大小;對音頻來說是整個音頻幀的大小
? width, height:圖像的寬高(只針對視頻)
? key_frame:是否為關鍵幀(只針對視頻) 。
? pict_type:幀類型(只針對視頻) 。例如I, P, B
? sample_rate:音頻采樣率(只針對音頻)
? nb_samples:音頻每通道采樣數(shù)(只針對音頻)
? pts:顯示時間
FFmpeg內(nèi)存模型
? 從現(xiàn)有的Packet拷貝一個新Packet的時候,有兩種情況:
? ①兩個Packet的buf引用的是同一數(shù)據(jù)緩存空間,這時
候要注意數(shù)據(jù)緩存空間的釋放問題;
? ②兩個Packet的buf引用不同的數(shù)據(jù)緩存空間,每個
Packet都有數(shù)據(jù)緩存空間的copy;
? 對于多個AVPacket共享同一個緩存空間,F(xiàn)Fmpeg使用的引用計數(shù)的機制(reference-count):
?? 初始化引用計數(shù)為0,只有真正分配AVBuffer的時候,引用計數(shù)初始化為1;
?? 當有新的Packet引用共享的緩存空間時,就將引用計數(shù)+1;
?? 當釋放了引用共享空間的Packet,就將引用計數(shù)-1;引用計數(shù)為0時,就釋放掉引用的緩存空間AVBuffer。
? AVFrame也是采用同樣的機制。
AVPacket常用API
AVPacket *av_packet_alloc(void); 分配AVPacket
這個時候和buffer沒有關系
void av_packet_free(AVPacket **pkt); 釋放AVPacket
和_alloc對應
void av_init_packet(AVPacket *pkt); 初始化AVPacket
只是單純初始化pkt字段
int av_new_packet(AVPacket *pkt, int size); 給AVPacket的buf分配內(nèi)存,引用計數(shù)初始化為1
int av_packet_ref(AVPacket *dst, const AVPacket *src); 增加引用計數(shù)
void av_packet_unref(AVPacket *pkt); 減少引用計數(shù)
void av_packet_move_ref(AVPacket *dst, AVPacket *src); 轉(zhuǎn)移引用計數(shù)
AVPacket *av_packet_clone(const AVPacket *src); 等于av_packet_alloc()+av_packet_ref()
AVFrame *av_frame_alloc(void); 分配AVFrame
void av_frame_free(AVFrame **frame); 釋放AVFrame
int av_frame_ref(AVFrame *dst, const AVFrame *src); 增加引用計數(shù)
void av_frame_unref(AVFrame *frame); 減少引用計數(shù)
void av_frame_move_ref(AVFrame *dst, AVFrame *src); 轉(zhuǎn)移引用計數(shù)
int av_frame_get_buffer(AVFrame *frame, int align); 根據(jù)AVFrame分配內(nèi)存文章來源:http://www.zghlxwxcb.cn/news/detail-426788.html
AVFrame *av_frame_clone(const AVFrame *src); 等于av_frame_alloc()+av_frame_ref()文章來源地址http://www.zghlxwxcb.cn/news/detail-426788.html
到了這里,關于音視頻八股文(6)-- ffmpeg大體介紹和內(nèi)存模型的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!