一、播放器框架
常用音視頻術語
? 容器/文件(Conainer/File):即特定格式的多媒體文件,比如mp4、flv、mkv等。
? 媒體流(Stream):表示時間軸上的一段連續數據,如一段聲音數據、一段視頻數據或一段字幕數據,可以是壓縮的,也可以是非壓縮的,壓縮的數據需要關聯特定的編解碼器(有些碼流音頻他是純PCM)。
? 數據幀/數據包(Frame/Packet):通常,一個媒體流是由大量的數據幀組成的,對于壓縮數據,幀對應著編解碼器的最小處理單元,分屬于不同媒體流的數據幀交錯存儲于容器之中。
? 編解碼器:編解碼器是以幀為單位實現壓縮數據和原始數據之間的相互轉換的。
二、常用概念
三、FFmpeg庫簡介
FFMPEG有8個常用庫:
? AVUtil:核心工具庫,下面的許多其他模塊都會依賴該庫做一些基本的音視頻處理操作。
? AVFormat:文件格式和協議庫,該模塊是最重要的模塊之一,封裝了Protocol層和Demuxer、Muxer層,使得協議和格式對于開發者來說是透明的。
? AVCodec:編解碼庫,封裝了Codec層,但是有一些Codec是具備自己的License的,FFmpeg是不會默認添加像libx264、FDK-AAC等庫的,但是FFmpeg就像一個平臺一樣,可以將其他的第三方的Codec以插件的方式添加進來,然后為開發者提供統一的接口。
? AVFilter:音視頻濾鏡庫,該模塊提供了包括音頻特效和視頻特效的處理,在使用FFmpeg的API進行編解碼的過程中,直接使用該模塊為音視頻數據做特效處理是非常方便同時也非常高效的一種方式。
? AVDevice:輸入輸出設備庫,比如,需要編譯出播放聲音或者視頻的工具ffplay,就需要確保該模塊是打開的,同時也需要SDL的預先編譯,因為該設備模塊播放聲音與播放視頻使用的都是SDL庫。
? SwrRessample:該模塊可用于音頻重采樣,可以對數字音頻進行聲道數、數據格式、采樣率等多種基本信息的轉換。
? SWScale:該模塊是將圖像進行格式轉換的模塊,比如,可以將YUV的數據轉換為RGB的數據,縮放尺寸由1280720變為800480。 ? PostProc:該模塊可用于進行后期處理,當我們使用AVFilter的時候需要打開該模塊的開關,因為Filter中會使用到該模塊的一些基礎函數。
? av_register_all():注冊所有組件,4.0已經棄用
? avdevice_register_all()對設備進行注冊,比如V4L2等。
? avformat_network_init();初始化網絡庫以及網絡加密協議相關的庫(比如openssl)
封裝格式相關
? avformat_alloc_context();負責申請一個AVFormatContext結構的內存,并進行簡單初始化
? avformat_free_context();釋放該結構里的所有東西以及該結構本身
? avformat_close_input();關閉解復用器。關閉后就不再需要使用avformat_free_context 進行釋放。
? avformat_open_input();打開輸入視頻文件
? avformat_find_stream_info():獲取音視頻文件信息
? av_read_frame(); 讀取音視頻包
? avformat_seek_file(); 定位文件
? av_seek_frame():定位文件
解碼器相關
? avcodec_alloc_context3(): 分配解碼器上下文
? avcodec_find_decoder():根據ID查找解碼器
? avcodec_find_decoder_by_name():根據解碼器名字
? avcodec_open2(): 打開編解碼器
? avcodec_decode_video2():解碼一幀視頻數據
? avcodec_decode_audio4():解碼一幀音頻數據
? avcodec_send_packet(): 發送編碼數據包
? avcodec_receive_frame(): 接收解碼后數據
? avcodec_free_context():釋放解碼器上下文,包含了avcodec_close()
? avcodec_close():關閉解碼器
FFmpeg3.x組件注冊方式
我們使用ffmpeg,首先要執行av_register_all,把全局的解碼器、編碼器等結構體注冊到各自全局的對象鏈表里,以便后面查找調用。
FFmpeg4.x組件注冊方式
FFmpeg內部去做,不需要用戶調用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[]數組。 - 在libavcodec/allcodecs.c將static const AVCodec * const codec_list[]的編解碼器用鏈表的方式組織起來。
FFmpeg4.0.2組件注冊方式
FFmepg內部去做,不需要用戶調用API去注冊。
對于demuxer/muxer(解復用器,也稱容器)則對應
- libavformat/muxer_list.c
libavformat/demuxer_list.c 這兩個文件也是在configure的時候生成,也就是說直接下載源碼是沒有這兩個文件的。 - 在libavformat/allformats.c將demuxer_list[]和muexr_list[]以鏈表的方式組織。
其他組件也是類似的方式。
FFmpeg常用結構體簡介
AVFormatContext
封裝格式上下文結構體,也是統領全局的結構體,保存了視頻文件封裝格式相關信息。
AVInputFormat demuxer
每種封裝格式(例如FLV, MKV, MP4, AVI)對應一個該結構體。
AVOutputFormat muxer
AVStream
視頻文件中每個視頻(音頻)流對應一個該結構體。
AVCodecContext
編解碼器上下文結構體,保存了視頻(音頻)編解碼相關信息。
AVCodec
每種視頻(音頻)編解碼器(例如H.264解碼器)對應一個該結構體。
AVPacket
存儲一幀壓縮編碼數據。
AVFrame
存儲一幀解碼后像素(采樣)數據。
FFmpeg數據結構之間的關系
AVFormatContext和AVInputFormat之間的關系
AVFormatContext API調用
AVInputFormat 主要是FFMPEG內部調用
AVFormatContext 封裝格式上下文結構體
struct AVInputFormat iformat;
所有的方法可重入的
AVInputFormat 每種封裝格式(例如FLV, MKV, MP4)
int (read_header)(struct AVFormatContext * );
int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);
int avformat_open_input(AVFormatContext **ps, const char *filename,AVInputFormat *fmt, AVDictionary **options)
AVCodecContext和AVCodec之間的關系
AVCodecContext 編碼器上下文結構體
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);
區分不同的碼流
? 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的字段
數據結構分析
? AVFormatContext
? iformat:輸入媒體的AVInputFormat,比如指向AVInputFormat ff_flv_demuxer
? nb_streams:輸入媒體的AVStream 個數
? streams:輸入媒體的AVStream []數組
? duration:輸入媒體的時長(以微秒為單位),計算方式可以參考av_dump_format()函數。
? bit_rate:輸入媒體的碼率
? AVInputFormat
? name:封裝格式名稱
? extensions:封裝格式的擴展名
? id:封裝格式ID
? 一些封裝格式處理的接口函數,比如read_packet()
? AVStream
? index:標識該視頻/音頻流
? time_base:該流的時基,PTS*time_base=真正的時間(秒)
? avg_frame_rate: 該流的幀率
? duration:該視頻/音頻流長度
? codecpar:編解碼器參數屬性
? 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:聲道數(只針對音頻)
? sample_fmt:采樣格式(只針對音頻)
? AVCodec
? name:編解碼器名稱
? type:編解碼器類型
? id:編解碼器ID
? 一些編解碼的接口函數,比如int (*decode)()
? AVCodecContext
? codec:編解碼器的AVCodec,比如指向AVCodec
ff_aac_latm_decoder
? width, height:圖像的寬高(只針對視頻)
? pix_fmt:像素格式(只針對視頻)
? sample_rate:采樣率(只針對音頻)
? channels:聲道數(只針對音頻)
? sample_fmt:采樣格式(只針對音頻)
? AVCodec
? name:編解碼器名稱
? type:編解碼器類型
? id:編解碼器ID
? 一些編解碼的接口函數,比如int (*decode)()
AVPacket
? pts:顯示時間戳
? dts:解碼時間戳
? data:壓縮編碼數據
? size:壓縮編碼數據大小
? pos:數據的偏移地址
? stream_index:所屬的AVStream
AVFrame
? data:解碼后的圖像像素數據(音頻采樣數據)
? linesize:對視頻來說是圖像中一行像素的大小;對音頻來說是整個音頻幀的大小
? width, height:圖像的寬高(只針對視頻)
? key_frame:是否為關鍵幀(只針對視頻) 。
? pict_type:幀類型(只針對視頻) 。例如I, P, B
? sample_rate:音頻采樣率(只針對音頻)
? nb_samples:音頻每通道采樣數(只針對音頻)
? pts:顯示時間戳
通過ffmpeg的學習,我覺得講的非常好,現在有免費課程,推薦給大家學習下:C/C++Linux服務器開發/后臺架構師【零聲教育】