FFMpeg框架代碼閱讀

簡介
FFmpeg是一個集錄制、轉換、音/視頻編碼解碼功能為一體的完整的開源解決方案。FFmpeg的開發是基于Linux操作系統,但是可以在大多數操作系統中編譯和使用。FFmpeg支持MPEGDivXMPEG4AC3DVFLV40多種編碼,AVIMPEGOGGMatroskaASF90多種解碼.
TCPMP, VLC, MPlayer等開源播放器都用到了FFmpeg。
FFmpeg主目錄下主要有libavcodec、libavformat和libavutil等子目錄。其中libavcodec用于存放各個encode/decode模塊,libavformat用于存放muxer/demuxer模塊,libavutil用于存放內存操作等常用模塊。
以flash movie的flv文件格式為例, muxer/demuxer的flvenc.c和flvdec.c文件在libavformat目錄下,encode/decode的mpegvideo.c和h263de.c在libavcodec目錄下。
muxer/demuxer與encoder/decoder定義與初始化
muxer/demuxer和encoder/decoder在FFmpeg中的實現代碼里,有許多相同的地方,而二者最大的差別是muxer和demuxer分別是不同的結構AVOutputFormat與AVInputFormat,而encoder和decoder都是用的AVCodec結構。
muxer/demuxer和encoder/decoder在FFmpeg中相同的地方有:
l???????? 二者都是在main()開始的av_register_all()函數內初始化的。
l???????? 二者都是以鏈表的形式保存在全局變量中的。
muxer/demuxer是分別保存在全局變量AVOutputFormat *first_oformat與AVInputFormat *first_iformat中的。
encoder/decoder都是保存在全局變量AVCodec *first_avcodec中的。
l???????? 二者都用函數指針的方式作為開放的公共接口。
demuxer開放的接口有:
int (*read_probe)(AVProbeData *);
?????? int(*read_header)(struct AVFormatContext *, AVFormatParameters *ap);
int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);
int (*read_close)(struct AVFormatContext *);
int (*read_seek)(struct AVFormatContext *, int stream_index, int64_t timestamp, int flags);
???????? muxer開放的接口有:
?????????????????? int (*write_header)(struct AVFormatContext *);
?????????????????? int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);
??????????????? int (*write_trailer)(struct AVFormatContext *);
encoder/decoder的接口都是一樣的,只不過二者分別只實現encoder和decoder函數:
int (*init)(AVCodecContext *);
????? ???????? int (*encode)(AVCodecContext *, uint8_t *buf, int buf_size, void *data);
????? ???????? int (*close)(AVCodecContext *);
??????? ???????? int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, uint8_t *buf, int buf_size);
仍以flv文件為例來說明muxer/demuxer的初始化。
在libavformat/allformats.c文件的av_register_all(void)函數中,通過執行
REGISTER_MUXDEMUX(FLV, flv);
將支持flv 格式的flv_muxerflv_demuxer變量分別注冊到全局變量first_oformatfirst_iformat鏈表的最后位置。
其中flv_muxer在libavformat/flvenc.c中定義如下:
AVOutputFormat flv_muxer = {
??? "flv",
??? "flv format",
??? "video/x-flv",
??? "flv",
??? sizeof(FLVContext),
#ifdef CONFIG_LIBMP3LAME
??? CODEC_ID_MP3,
#else // CONFIG_LIBMP3LAME
??? CODEC_ID_NONE,
??? CODEC_ID_FLV1,
??? flv_write_header,
??? flv_write_packet,
??? flv_write_trailer,
??? .codec_tag= (const AVCodecTag*[]){flv_video_codec_ids, flv_audio_codec_ids, 0},
}
AVOutputFormat結構的定義如下:
typedef struct AVOutputFormat {
??? const char *name;
??? const char *long_name;
??? const char *mime_type;
??? const char *extensions; /**< comma separated filename extensions */
??? /** size of private data so that it can be allocated in the wrapper */
??? int priv_data_size;
??? /* output support */
??? enum CodecID audio_codec; /**< default audio codec */
??? enum CodecID video_codec; /**< default video codec */
??? int (*write_header)(struct AVFormatContext *);
??? int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);
??? int (*write_trailer)(struct AVFormatContext *);
??? /** can use flags: AVFMT_NOFILE, AVFMT_NEEDNUMBER, AVFMT_GLOBALHEADER */
??? int flags;
??? /** currently only used to set pixel format if not YUV420P */
??? int (*set_parameters)(struct AVFormatContext *, AVFormatParameters *);
??? int (*interleave_packet)(struct AVFormatContext *, AVPacket *out, AVPacket *in, int flush);
?
??? /**
???? * list of supported codec_id-codec_tag pairs, ordered by "better choice first"
??? ?* the arrays are all CODEC_ID_NONE terminated
???? */
??? const struct AVCodecTag **codec_tag;
??? /* private fields */
??? struct AVOutputFormat *next;
} AVOutputFormat;
AVOutputFormat結構的定義可知,flv_muxer變量初始化的第一、第二個成員分別為該muxer的名稱與長名稱,第三、第四個成員為所對應MIMIE Type和后綴名,第五個成員是所對應的私有結構的大小,第六、第七個成員為所對應的音頻編碼和視頻編碼類型ID,接下來就是三個重要的接口函數,該muxer的功能也就是通過調用這三個接口實現的。
flv_demuxer在libavformat/flvdec.c中定義如下, 與flv_muxer類似,在這兒主要也是設置了5個接口函數,其中flv_probe接口用途是測試傳入的數據段是否是符合當前文件格式,這個接口在匹配當前demuxer的時候會用到。
AVInputFormat flv_demuxer = {
??? "flv",
??? "flv format",
??? 0,
??? flv_probe,
??? flv_read_header,
??? flv_read_packet,
??? flv_read_close,
??? flv_read_seek,
??? .extensions = "flv",
??? .value = CODEC_ID_FLV1,
};
在上述av_register_all(void)函數中通過執行libavcodec/allcodecs.c文件里的avcodec_register_all(void)函數來初始化全部的encoder/decoder。
因為不是每種編碼方式都支持encode和decode,所以有以下三種注冊方式:
#define REGISTER_ENCODER(X,x) /
???????? ????????? ?if(ENABLE_##X##_ENCODER)?register_avcodec(&x##_encoder)
#define REGISTER_DECODER(X,x) /
????????? ??????? if(ENABLE_##X##_DECODER)?register_avcodec(&x##_decoder)
#define REGISTER_ENCDEC(X,x)
?REGISTER_ENCODER(X,x); REGISTER_DECODER(X,x)
如支持flv的flv_encoderflv_decoder變量就分別是在libavcodec/mpegvideo.c和libavcodec/h263de.c中創建的。
當前muxer/demuxer的匹配
在FFmpeg的文件轉換過程中,首先要做的就是根據傳入文件和傳出文件的后綴名匹配合適的demuxer和muxer。
匹配上的demuxer和muxer都保存在如下所示,定義在ffmpeg.c里的全局變量file_iformatfile_oformat中:
static AVInputFormat *file_iformat;
static AVOutputFormat *file_oformat;
1.???????? demuxer匹配
在libavformat/utils.c中的static AVInputFormat *av_probe_input_format2(AVProbeData *pd, int is_opened, int *score_max)函數用途是根據傳入的probe data數據,依次調用每個demuxer的read_probe接口,來進行該demuxer是否和傳入的文件內容匹配的判斷。其調用順序如下:
void parse_options(int argc, char **argv, const OptionDef *options)
static void opt_input_file(const char *filename)
static void opt_input_file(const char *filename)
int av_open_input_file(…… )
???????????????????????????????????? AVInputFormat *av_probe_input_format(AVProbeData *pd, int is_opened)
static AVInputFormat *av_probe_input_format2(……)
opt_input_file函數是在保存在const OptionDef options[]數組中,用于void parse_options(int argc, char **argv, const OptionDef *options)中解析argv里的“-i” 參數,也就是輸入文件名時調用的。
2.???????? muxer匹配
與demuxer的匹配不同,muxer的匹配是調用guess_format函數,根據main( ) 函數的argv里的輸出文件后綴名來進行的。
void parse_options(int argc, char **argv, const OptionDef *options)
void parse_arg_file(const char *filename)
static void opt_output_file(const char *filename)
AVOutputFormat *guess_format(const char *short_name, const char *filename,
???????????????????????????? const char *mime_type)
當前encoder/decoder的匹配
main( )函數中除了解析傳入參數并初始化demuxer與muxer的parse_options( )函數以外,其他的功能都是在av_encode( )函數里完成的。
在libavcodec/utils.c中有如下二個函數。
AVCodec *avcodec_find_encoder(enum CodecID id)
?????????????????? AVCodec *avcodec_find_decoder(enum CodecID id)
他們的功能就是根據傳入的CodecID,找到匹配的encoder和decoder。
av_encode( )函數的開頭,首先初始化各個AVInputStreamAVOutputStream,然后分別調用上述二個函數,并將匹配上的encoder與decoder分別保存在AVInputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codecAVOutputStream->AVStream *st->AVCodecContext *codec->struct AVCodec *codec變量中。
其他主要數據結構
1.??????? AVFormatContext
AVFormatContext是FFMpeg格式轉換過程中實現輸入和輸出功能、保存相關數據的主要結構。每一個輸入和輸出文件,都在如下定義的指針數組全局變量中有對應的實體。
static AVFormatContext *output_files[MAX_FILES];
static AVFormatContext *input_files[MAX_FILES];
對于輸入和輸出,因為共用的是同一個結構體,所以需要分別對該結構中如下定義的iformatoformat成員賦值。
struct AVInputFormat *iformat;
??? struct AVOutputFormat *oformat;
對一個AVFormatContext來說,二個成員不能同時有值,即一個AVFormatContext不能同時含有demuxer和muxer。
main( )函數開頭的parse_options( )函數中找到了匹配的muxer和demuxer之后,根據傳入的argv參數,初始化每個輸入和輸出的AVFormatContext結構,并保存在相應的output_filesinput_files指針數組中。
av_encode( )函數中,output_filesinput_files是作為函數參數傳入后,在其他地方就沒有用到了。
2.???????? AVCodecContext
保存AVCodec指針和與codec相關的數據,如video的width、height,audio的sample rate等。AVCodecContext中的codec_typecodec_id二個變量對于encoder/decoder的匹配來說,最為重要。
enum CodecType codec_type; /* see CODEC_TYPE_xxx */
?? ???????????? enum CodecID codec_id; /* see CODEC_ID_xxx */
?
如上所示,codec_type保存的是CODEC_TYPE_VIDEOCODEC_TYPE_AUDIO等媒體類型,
codec_id保存的是CODEC_ID_FLV1CODEC_ID_VP6F等編碼方式。
以支持flv格式為例,在前述的av_open_input_file(…… ) 函數中,匹配到正確的AVInputFormat demuxer后,通過av_open_input_stream( )函數中調用AVInputFormatread_header接口來執行flvdec.c中的flv_read_header( )函數。在flv_read_header( )函數內,根據文件頭中的數據,創建相應的視頻或音頻AVStream,并設置AVStreamAVCodecContext的正確的codec_type值。codec_id值是在解碼過程中flv_read_packet( )函數執行時根據每一個packet頭中的數據來設置的。
3.???????? AVStream
AVStream結構保存與數據流相關的編解碼器,數據段等信息。比較重要的有如下二個成員:
AVCodecContext *codec; /**< codec context */
void *priv_data;
其中codec指針保存的就是上節所述的encoder或decoder結構。priv_data指針保存的是和具體編解碼流相關的數據,如下代碼所示,在ASF的解碼過程中,priv_data保存的就是ASFStream結構的數據。
AVStream *st;
ASFStream *asf_st;???
???? … …
st->priv_data = asf_st;
4.???????? AVInputStream/ AVOutputStream
根據輸入和輸出流的不同,前述的AVStream結構都是封裝在AVInputStreamAVOutputStream結構中,在av_encode( )函數中使用。
AVInputStream中還保存的有與時間有關的信息。
AVOutputStream中還保存有與音視頻同步等相關的信息。
5.???????? AVPacket
AVPacket結構定義如下,其是用于保存讀取的packet數據。
typedef struct AVPacket {
??? int64_t pts;??????????? ///< presentation time stamp in time_base units
??? int64_t dts;??????????? ///< decompression time stamp in time_base units
??? uint8_t *data;
??? int?? size;
??? int?? stream_index;
??? int?? flags;
??? int?? duration;??????? ///< presentation duration in time_base units (0 if not available)
??? void?(*destruct)(struct AVPacket *);
??? void?*priv;
??? int64_t pos;?????????? ///< byte position in stream, -1 if unknown
} AVPacket;
av_encode( )函數中,調用AVInputFormat(*read_packet)(struct AVFormatContext *, AVPacket *pkt);接口,讀取輸入文件的一幀數據保存在當前輸入AVFormatContextAVPacket成員中。
av_encode函數主要流程
av_encode( )函數是FFMpeg中最重要的函數,編解碼和輸出等大部分功能都在此函數內完成,因此有必要詳細描述一下這個函數的主要流程。
1.???????? input streams initializing
2.???????? output streams initializing
3.???????? encoders and decoders initializing
4.???????? set meta data information from input file if required.
5.???????? write output files header
6.???????? loop of handling each frame
a.?????? read frame from input file:
b.?????? decode frame data
c.?????? encode new frame data
d.?????? write new frame to output file
7.???????? write output files trailer
8.???????? close each encoder and decoder

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/254793.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/254793.shtml
英文地址,請注明出處:http://en.pswp.cn/news/254793.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C#DotNetBar TabControl將水平標簽設置成豎直

小哥哥小姐姐覺得有用點個贊唄&#xff01; 首先選中整個TabControl控件 更改屬性&#xff1a; 完成

WCF調試異常信息:ServiceHost 僅支持類服務類型

“/CommonHelpServices”應用程序中的server錯誤。ServiceHost 僅支持類服務類型。說明: 運行當前 Web 請求期間&#xff0c;出現未經處理的異常。請檢查堆棧跟蹤信息&#xff0c;以了解有關該錯誤以及代碼中導致錯誤的出處的具體信息。異常具體信息: System.ArgumentException…

使用 Drone 構建 Coding 項目

2019獨角獸企業重金招聘Python工程師標準>>> 使用 Drone 構建 Coding 項目 Drone 是一個輕量級的持續集成工具。它具備許多現代持續集成工具的特性&#xff1a;輕巧&#xff08;Docker 鏡像不到 10M&#xff09;、部署方便&#xff08;docker-compose 一鍵部署&…

C++類的構造函數 后單冒號加基類 例如:CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)

CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD) 比如兩個類 class A{A(int *x);...}class B : public A{B(int *x);...}然后B在構造的時候 B::B(int *x) : A(x){...}B是A的子類&#xff0c;B類對象在構造過程中必須先構造出一個A類對象&#xff0c;而A類的構造函數需要一…

C#InI文件讀寫剖析

C#InI文件讀寫剖析 1.讀取ini文件 StringBuilder stringBuilder new StringBuilder();GetPrivateProfileString(section, key, "err", stringBuilder, 1024, filePath);para_value stringBuilder.ToString();GetPrivateProfileString( LPCTSTR lpAppName, //配置文…

bzoj1385: [Baltic2000]Division expression

歐幾里得算法。可以發現規律&#xff0c;a[2]作為分母&#xff0c;其他作為分子&#xff0c;必定是最好的選擇。判斷是否為整數即可。 #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define rep(…

成員變量 局部變量 類變量

成員變量和局部變量的區別 成員變量&#xff1a; 1、成員變量定義在類中&#xff0c;在整個類中都可以被訪問。 2、成員變量隨著對象的建立而建立&#xff0c;隨著對象的消失而消失&#xff0c;存在于對象所在的堆內存中。 3、成員變量有默認初始化值。 局部變量&#xff1a; 1…

從DevOps到Cloud Native,應用上云姿勢全解鎖

本文由網易云 發布作者&#xff1a;林帆序文伴隨著IaaS、PaaS等云端基礎設施技術的成熟&#xff0c;“應用上云”成為許多企業軟件部門的心頭大事。通過把傳統軟件系統搬到云上&#xff0c;一方面可以讓業務方獲得更多的資源靈活性&#xff0c;另一方面也可以緩解運營方的成本壓…

foreach

foreach 遍歷數組內每個元素&#xff0c;迭代一次執行一次循環語句。 相當于增前后的for循環

深入理解C++對象模型-對象的內存布局,vptr,vtable

vtpr的位置: 為了支持多態,C引入了vtpr和vtable這兩個概念.對于每個有虛函數的類,C都會為其生成一個vtable,并在類中添加一個隱含的數據成員vptr. 對于vptr在對象中的位置,跟類的數據成員的布局一樣,C標準里面并沒有做出任何的規定.但是對于特定的編譯器,我們還是可以通…

Visual Studio Code 常用插件整理

常用插件說明&#xff1a; 一、HTML Snippets 超級使用且初級的H5代碼片段以及提示 二、HTML CSS Support 讓HTML標簽上寫class智能提示當前項目所支持的樣式 三、Debugger for Chrome 讓vscode映射chrome的debug功能&#xff0c;靜態頁面都可以用vscode來打斷點調試、配飾稍…

sublime 正則搜索日語字符

sublime 正則搜索日語字符 [\x{3041}-\x{3096}\x{30A0}-\x{30FF}\x{3400}-\x{4DB5}\x{4E00}-\x{9FCB}\x{F900}-\x{FA6A}\x{2E80}-\x{2FD5}\x{FF5F}-\x{FF9F}\x{3000}-\x{303F}\x{31F0}-\x{31FF}\x{3220}-\x{3243}\x{3280}-\x{337F}\x{FF01}-\x{FF5E}] 參考: http://www.localiz…

函數域中申請堆空間出函數后不會自動釋放

#include<iostream> using std::cout; using std::endl; char *scat(char *s1,char *s2) { char *snew char[strlen(s1)strlen(s2)1]; strcpy(s,s1); strcat(s,s2); return s;//返回剛申請的堆空間的首址&#xff0c;出了函數后變量s就不復存在了&#xff0c;但申請的…

/home文件夾重新劃分獨立分區

1. 備份home文件夾&#xff0c;數據丟失的時候可以使用cp -av /home/* /data/2. 磁盤分區2.1 虛擬機中添加磁盤并讓系統識別&#xff0c;使用# echo - - - >> /sys/class/scsi_host/host0/scan# echo - - - >> /sys/class/scsi_host/host1/scan# echo - - - >…

使用halcon將一個圓上的點擬合成圓形并且求出圓心

我們在自動化貼裝機標定過程中&#xff0c;需要計算吸頭的旋轉中心位置。我們一般使用的方法是使用模板匹配&#xff0c;做一個模板&#xff0c;吸頭旋轉一個角度尋找模板一次&#xff0c;通過多次旋轉求取吸頭的旋轉中心。 使用halcon實現 public bool FitCircle(double[] X…

This 指針

如果你期望衍生類別重新定義一個成員函數&#xff0c;那么你應該在基礎類別中把此函數設為 virtual。 以單一指令喚起不同函數&#xff0c;這種性質稱為Polymorphism&#xff0c;意思是"the ability toassume many forms"&#xff0c;也就是多態。 虛擬函…

史上最全亞歷山大大帝名言

&#xff08;1&#xff09;把財富分給他人&#xff0c;把希望留給自己&#xff0c;他將帶給我無窮的財富。——亞歷山大大帝 &#xff08;2&#xff09;山不走到我這里來&#xff0c;我就走到他那里去。——亞歷山大大帝 &#xff08;3&#xff09;能夠戰勝恐懼就能戰勝死亡。—…

jdbcmysql

做java開發難免會用到數據庫,操作數據庫也是java開發的核心技術。那我們現在就來談談javajdbc來操作mysql數據庫吧 第一步&#xff1a;我們需要把mysql的驅動引進來這里引驅動就是把mysql-connector-java-5.1.37-bin.jar加到項目中來&#xff0c;下面附jar包 第二步&#xff1a…

char **p, char a[16][8]; 問:p=a 是否會導致程序在以后出現問題?為什么?

int (*v)[10]; 在 Visual C 里面不能與 int **v 等同起來。 舉個例子&#xff1a; int **p; int (*v)[10]; int a[10][10]; 如果寫 v a 是可以的。 如果寫 p a 將會引起一個類型不匹配的編譯錯誤。 二級指針&#xff08;int **p&#xff09;需要自己指向一個一級指針&#x…

draw_circle_mod預生成交互式圓形

目錄draw_circle_mod&#xff08;算子&#xff09;描述參數draw_circle_mod&#xff08;算子&#xff09; draw_circle_mod - 圓的交互式繪圖。 draw_circle_mod&#xff08;:: WindowHandle&#xff0c;RowIn&#xff0c;ColumnIn&#xff0c;RadiusIn&#xff1a;Row&#…