1. FFmpeg介紹與裁剪
1.1 FFmpeg簡介
FFmpeg(Fast forword mpeg,音視頻轉換器)是一個開源免費跨平臺的視頻和音頻流方案,它提供了錄製/音視頻編解碼、轉換以及流化音視頻的完整解決方案。ffmpeg4.0.2源碼目錄結構如下:
目錄說明:
FFmpeg
|—compat 該目錄存放的是兼容文件,以便兼容早期版本
|—doc 說明文檔
|—ffbuild
|—libavcodec 音視頻編解碼核心庫
|—libavdevice 各種設備的輸入輸出,比如Video4Linux2, VfW, DShow以及 ALSA
|—libavfilter 濾鏡特效處理
|—libavformat I/O操作和封裝格式(muxer/demuxer)處理
|—libavswresample 音頻重採樣,格式轉換和混音
|— (1) 重採樣:改變音頻的採樣率,比如從44100HZ降低到8000HZ
|— (2)重新矩陣化:改變音頻通道數量,比如從立體聲道(stereo )變爲單身道(mono)
|— (3)格式轉換:改變音頻採樣大小,比如將每個樣本大小從16bits降低到8bits
|—libavutil 工具庫,比如算數運算、字符操作等
|—libpostproc 後期效果處理,如圖像的去塊效應
|—libswscale 視頻像素處理,包括縮放圖像尺寸、色彩映射轉換、像素顏色空間轉換等
|—presets
|—tests 測試實例
|—configure 配置文件,編譯ffmpeg時用到
1.2 命令行工具
FFmpeg框架中還提供了幾個用於執行命令行完成音視頻數據處理工具,包括ffplay、ffprobe、ffserver,具體解釋如下:
ffplay
Fast forword play,用ffmpeg實現的播放器
ffserver
Fast forword server,用ffmpeg實現的rtsp服務器
ffprobe
Fat forword probe,用來輸入分析輸入流
2. FFmpeg架構分析
在1.1小節中,我們對FFmpeg整體架構進行了簡單介紹,闡述了框架中各個模塊的功能。本節將在此基礎上,重點闡述在利用FFmpeg進行音視頻開發中牽涉到的重要步驟,數據結構體以及相關函數。
2.1 FFmpeg處理要點
總體來說,FFmpeg框架主要的作用在於對多媒體數據進行解協議、解封裝、解碼以及轉碼等操作,爲了對FFmpeg在視音頻中的應用有個更直觀理解,下面給出解析rtsp網絡流的流程圖,該圖演示了從打開rtsp流,到最終提取出解碼數據或轉碼的大概過程,如下所示:
術語解釋:
muxer:視音頻複用器(封裝器),即將視頻文件、音頻文件和字幕文件(如果有的話)合併爲某一個視頻格式,比如講a.avi、a.mp3、a.srt合併爲mkv格式的視頻文件;
demuxer:視音頻分離器(解封裝器),即muxer的逆過程;
transcode:轉碼,即將視音頻數據從某一種格式轉換成另一種格式;
RTP包:Real-time Transport Protocol,實時傳輸協議,是一種基於UDP的網絡傳輸協議,它介於應用層和傳輸層之間,負責對流媒體數據進行封包並實現媒體流的實時傳輸;
ES流:Elementary Streams,即原始流,也稱視/音頻裸流,是直接從編碼器輸出的數據流,可爲視頻數據流(如H.264、MJPEG等)或音頻數據流(如AAC等);
PES流:Packetized Elementary Streams,分組ES流,PES流是ES流經過PES打包器將ES分組、打包、加入包頭信息等處理後形成的數據流,是用來傳遞ES的一種數據結構。
解協議:取出網絡數據流無關報文信息,以獲取真正的視音頻數據,常見的協議有rtsp、rtmp、http和mms等;
解封裝:即demuxer,封裝格式可以爲.mp4/.avi/.flv/.mkv等;
解碼:將編碼數據還原成原始內容,比如將H.264解碼爲YUV、AAC解碼爲PCM等;
2.1 FFmpeg重要的結構體
FFmpeg中有很多比較重要的結構體,比如與輸入輸出(I/O)有關的結構體AVIOContext、URLContext、URLProtocol ,與封裝格式有關的結構體AVFormatContext、AVInputFormat、AVOutputFormat,與編解碼有關的結構體AVCodec、AVCodecContext,以及與音視頻數據有關的結構體AVStream、AVPacket、AVFrame等等。剛開始接觸FFmpeg時,個人感覺一時間要理解區分這些結構體還是有點困難的,好在這些結構體當中有個“老大哥”-AVFormatContext,AVFormatContext可以說是貫穿整個FFmpeg開發,“猶如神一般的存在”。下面我們就在分析AVFormatContext結構體的基礎上,闡述上述結構體的作用與區別。
AVFormatContext
AVFormatContext結構體描述了一個多媒體文件或流的構成和基本信息,是FFmpeg中最爲基本的一個結構體,也是其他所有結構的根。其中,成員變量iformat和oformat爲指向對應的demuxing(解封裝)和muxing(封裝)指針,變量類型分別爲AVInputFormat、AVOutputFormat;pb爲指向控制底層數據讀寫的指針,變量類型爲AVIOContext;nb_streams表示多媒體文件或多媒體流中數據流的個數;streams爲指向所有流存儲的二級指針,變量類型AVStream;video_codec和audio_codec分別表示視頻和音頻編解碼器,變量類型爲AVCodec等等。AVFormatContext結構體(位於libavformat/avformat.h中)部分源碼如下:
typedef struct AVFormatContext {
const AVClass *av_class;
// 輸入容器格式
// 只在調用avformat_open_input()時被設置,且僅限Demuxing
struct AVInputFormat *iformat;
// 輸出容器格式
// 只在調用avformat_alloc_output_context2()函數時被設置,且僅限封裝(Muxing)
struct AVOutputFormat *oformat;
/**
* Format private data. This is an AVOptions-enabled struct
* if and only if iformat/oformat.priv_class is not NULL.
*
* - muxing: set by avformat_write_header()
* - demuxing: set by avformat_open_input()
*/
void *priv_data;
// 輸/入輸出(I/O)的緩存
// 說明:解封裝(demuxing):值由avformat_open_input()設置
// 封裝(muxing): 值由avio_open2設置,需在avformat_write_header()之前
AVIOContext *pb;
// stream info
int ctx_flags;
// AVFormatContext.streams中數據流的個數
// 說明:值由avformat_new_stream()設置
unsigned int nb_streams;
// 文件中所有流stream列表。創建一個新stream,調用avformat_new_stream()函數實現
// 當調用avformat_free_context()後,streams所佔資源被釋放
// 說明:解封裝(demuxing):當調用avformat_open_input()時,streams值被填充
// 封裝(muxing):streams在調用avformat_write_header()之前被用戶創建
//
AVStream **streams;
// 輸入或輸出文件名,如輸入:rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov
// 說明:demuxing:當調用avformat_open_input()後被設置
// muxing: 當調用avformat_alloc_output_context2()後被設置,且需要調用avformat_write_header()之前
char filename[1024];
// component的第一幀位置,僅限Demuxing時由libavformat設置
int64_t start_time;
// stream的時長,僅限Demuxing時由libavformat設置
int64_t duration;
// 總比特率(bit/s),包括音頻、音頻
int64_t bit_rate;
...
// 視頻編解碼器ID
// 說明:Demuxing時由用戶設置
enum AVCodecID video_codec_id;
// 音頻編解碼器ID
// 說明:Demuxing時由用戶設置
enum AVCodecID audio_codec_id;
// 字幕(subtitle)編解碼器ID
// 說明:Demuxing時由用戶設置
enum AVCodecID subtitle_codec_id;
...
// 文件元數據,即Metadata
// 說明:demuxing:當調用avformat_open_input時被設置
// muxing:在調用avformat_write_header()之前被設置
// 注:當調用avformat_free_context()時metadata的資源被libavformat釋放
AVDictionary *metadata;
// 實時流啓動的真實時間
int64_t start_time_realtime;
...
// 視頻編解碼器,Demuxing時由用戶指定
AVCodec *video_codec;
// 音頻編解碼器,Demuxing時由用戶指定
AVCodec *audio_codec;
// 字幕編解碼器,Demuxing時由用戶指定
AVCodec *subtitle_codec;
// 數據編解碼器,Demuxing時由用戶指定
AVCodec *data_codec;
...
// 數據編解碼器ID
enum AVCodecID data_codec_id;
...
} AVFormatContext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
1. 複用(muxing)/解複用(demuxing)
(1) AVInputFormat結構體
AVInputFormat爲解複用/解封裝(demuxing)器對象,它包含了解複用器的相關信息和操作函數,比如name成員變量爲指定封裝格式的名稱,如"aac"、"mov"等;read_header成員函數爲讀取封裝頭部數據;read_packet成員函數爲讀取一個AVPacket等等。AVInputFormat結構體(位於libavformat/avformat.h中)部分源碼如下:
typedef struct AVInputFormat {
// 封裝格式名稱,如"mp4"、"mov"等
const char *name;
// 封裝格式別稱
const char *long_name;
int flags;
const char *extensions;
const struct AVCodecTag * const *codec_tag;
const AVClass *priv_class;
const char *mime_type;
//
struct AVInputFormat *next;
int raw_codec_id;
// 具體format對應Context的size,如MovContext
int priv_data_size;
int (*read_probe)(AVProbeData *);
// 讀取format header,同時初始化AVFormatContext結構
// 若成功,返回0
int (*read_header)(struct AVFormatContext *);
// 讀取packet大小數據,並將其存放到pkt指向的內存中
// 若成功,返回0;若失敗,返回負數且pkt不會被分配內存
int (*read_packet)(struct AVFormatContext *, AVPacket *pkt);
// 關閉流,但不釋放AVFormatContext和AVStreams所佔內存
int (*read_close)(struct AVFormatContext *);
/**
* seek相對於流索引中幀的時間戳
* @param stream_index 流index,不能爲-1
* @param flags 用於方向,如果麼有精確的匹配
* @return >= 0 操作成功
*/
int (*read_seek)(struct AVFormatContext *,
int stream_index, int64_t timestamp, int flags);
/**
* 獲取流[stream_index]的下一個時間戳
* @return 時間戳或AV_NOPTS_VALUE(當發生錯誤時)
*/
int64_t (*read_timestamp)(struct AVFormatContext *s, int stream_index,
int64_t *pos, int64_t pos_limit);
// Start/resume playing -只適用於RTSP
int (*read_play)(struct AVFormatContext *);
// Pause playing - 只適用於RTSP
int (*read_pause)(struct AVFormatContext *);
// 獲取設備列表,詳解avdevice_list_devices()
int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);
// 初始化設備功能子模塊,詳見avdevice_capabilities_create()函數
int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
// 釋放設備功能子模塊,詳見avdevice_capabilities_free()函數
int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
} AVInputFormat;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
通過調用av_register_all()函數,FFmpeg所有的解複用器保存在以first_iformat爲頭部指針、last_iformat爲尾部指針的鏈表中。這裏以AAC(音頻壓縮編碼格式)解複用器爲例,來分析AVInputFormat結構體的初始化流程,相關源碼詳見libavformat/Aacdec.c:
AVInputFormat ff_aac_demuxer = {
.name = "aac", // 指定解複用器名稱
.long_name = NULL_IF_CONFIG_SMALL("raw ADTS AAC (Advanced Audio Coding)"), // 指定AAC對應的文件格式
.read_probe = adts_aac_probe, // 探測函數
.read_header = adts_aac_read_header, // 讀取頭部數據函數
.read_packet = adts_aac_read_packet, // 讀取數據包函數
.flags = AVFMT_GENERIC_INDEX,
.extensions = "aac", // 後綴
.mime_type = "audio/aac,audio/aacp,audio/x-aac",
.raw_codec_id = AV_CODEC_ID_AAC,// AAC解碼器ID
};
1
2
3
4
5
6
7
8
9
10
11
(2) AVOutputFormat結構體
與AVInputFormat相反,AVOtputFormat爲複用/封裝(muxing)器對象,它包含了複用器的相關信息和操作函數,比如name成員變量爲指定封裝格式的名稱,如"mp4"、"3gp"等;write_header成員函數爲讀取封裝頭部數據;write_packet成員函數爲寫入一個AVPacket等等。AVOutputFormat結構體(位於libavformat/avformat.h中)部分源碼如下:
typedef struct AVOutputFormat {
// 封裝格式名稱,如"mp4"
const char *name;
// 文件格式
const char *long_name;
// mime類型
const char *mime_type;
const char *extensions; /**< 逗號分隔的文件擴展名 */
/* output support */
enum AVCodecID audio_codec; /**< 默認音頻codec(編解碼器) */
enum AVCodecID video_codec; /**< 默認視頻codec */
enum AVCodecID subtitle_codec; /**< 默認subtitle codec */
/**
* flags可取值:AVFMT_NOFILE, AVFMT_NEEDNUMBER,
* AVFMT_GLOBALHEADER, AVFMT_NOTIMESTAMPS, AVFMT_VARIABLE_FPS,
* AVFMT_NODIMENSIONS, AVFMT_NOSTREAMS, AVFMT_ALLOW_FLUSH,
* AVFMT_TS_NONSTRICT, AVFMT_TS_NEGATIVE
*/
int flags;
const struct AVCodecTag * const *codec_tag;
const AVClass *priv_class; ///< AVClass for the private context
struct AVOutputFormat *next;
// private data的大小
int priv_data_size;
// 寫header
int (*write_header)(struct AVFormatContext *);
// 寫一個packet。如果flags=AVFMT_ALLOW_FLUSH,pkt可爲NULL,以便flush muxer中的緩衝數據
// 返回0,表示緩衝區仍還有數據可flush;返回1,表示緩衝區無可flush得數據
int (*write_packet)(struct AVFormatContext *, AVPacket *pkt);
int (*write_trailer)(struct AVFormatContext *);
// 如果不是YUV420P,目前只支持設置像素格式
int (*interleave_packet)(struct AVFormatContext *, AVPacket *out,
AVPacket *in, int flush);
// 測試給定的編解碼器是否可以存儲在這個容器中
int (*query_codec)(enum AVCodecID id, int std_compliance);
void (*get_output_timestamp)(struct AVFormatContext *s, int stream,
int64_t *dts, int64_t *wall);
int (*control_message)(struct AVFormatContext *s, int type,
void *data, size_t data_size);
// 寫未編碼的AVFrame幀數據,詳見av_write_uncoded_frame()
int (*write_uncoded_frame)(struct AVFormatContext *, int stream_index,
AVFrame **frame, unsigned flags);
/**
* Returns device list with it properties.
* @see avdevice_list_devices() for more details.
*/
int (*get_device_list)(struct AVFormatContext *s, struct AVDeviceInfoList *device_list);
/**
* Initialize device capabilities submodule.
* @see avdevice_capabilities_create() for more details.
*/
int (*create_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
// 釋放設備功能子模塊,詳見avdevice_capabilities_free()
int (*free_device_capabilities)(struct AVFormatContext *s, struct AVDeviceCapabilitiesQuery *caps);
enum AVCodecID data_codec; /**< default data codec */
/**
* 初始化format. 分配數據內存,設置AVFormatContext或 AVStream參數,與deinit()配合使用,釋放分配的內存資源
* 返回0,配置成功;返回1,配置失敗。
*/
int (*init)(struct AVFormatContext *);
/** 釋放init分配的內存資源,無論調用init()是否成功
*/
void (*deinit)(struct AVFormatContext *);
/** 檢測比特流
* 如果返回0,表示需要檢測流的更多packets;返回-1,則不需要
*/
int (*check_bitstream)(struct AVFormatContext *, const AVPacket *pkt);
} AVOutputFormat;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
同樣,通過調用av_register_all()函數,FFmpeg所有的複用器保存在以first_oformat爲頭部指針、last_oformat爲尾部指針的鏈表中。這裏以mp4(視頻壓縮編碼格式)複用器爲例,來分析AVOutputFormat結構體的初始化流程,相關源碼詳見libavformat/Movenc.c:
AVOutputFormat ff_mp4_muxer = {
.name = "mp4", //複用器名稱
.long_name = NULL_IF_CONFIG_SMALL("MP4 (MPEG-4 Part 14)"), //mp4對應的文件格式
.mime_type = "video/mp4",// MIME類型
.extensions = "mp4", // 文件擴展名
.priv_data_size = sizeof(MOVMuxContext),
.audio_codec = AV_CODEC_ID_AAC,// 音頻編碼器ID
.video_codec = CONFIG_LIBX264_ENCODER ?
AV_CODEC_ID_H264 : AV_CODEC_ID_MPEG4,// 視頻編碼器ID
.init = mov_init, // 初始化函數
.write_header = mov_write_header, // 寫入頭部
.write_packet = mov_write_packet, // 寫入Packet
.write_trailer = mov_write_trailer,
.deinit = mov_free, // 釋放資源
.flags = AVFMT_GLOBALHEADER | AVFMT_ALLOW_FLUSH | AVFMT_TS_NEGATIVE,
.codec_tag = (const AVCodecTag* const []){ codec_mp4_tags, 0 },
.check_bitstream = mov_check_bitstream,
.priv_class = &mp4_muxer_class,
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2. 輸入/輸出(I/O)
(1) AVIOContext結構體
AVIOContext是FFmpeg管理輸入輸出(I/O)數據的結構體,它是協議(文件)操作的頂層結構,提供帶緩衝的讀寫操作。有關讀寫操作和成員變量的含義,可見如下源碼中給出的註釋示意圖:
讀取數據:
寫入數據:
AVIOContext結構體位於libavformat/avio.h中,部分源碼如下:
typedef struct AVIOContext {
const AVClass *av_class;
unsigned char *buffer; // 數據緩衝區
int buffer_size; // 緩存的大小
unsigned char *buf_ptr; // 指針指向緩存區的當前位置,可小於buffer+buffer.size
unsigned char *buf_end; // 讀取/寫入緩存區數據的末尾位置
// 私有指針,關聯URLContext結構,作爲read/write/seek/...函數參數
// 用於完成對廣義輸入文件的讀寫等操作,指向一個URLContext對象
void *opaque;
// 讀packet數據
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
// 寫數據到packet
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);
// 定位
int64_t (*seek)(void *opaque, int64_t offset, int whence);
int64_t pos; // 當前緩存區域在文件中的位置
int eof_reached; // 是否到達文件末尾,true表示已經到末尾
int write_flag; // 是否可寫標誌,true表示open可寫
int max_packet_size; // packet最大尺寸
unsigned long checksum;
unsigned char *checksum_ptr;
unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
// 錯誤代碼,0表示沒有錯誤出現
int error;
//網絡流媒體協議暫停或恢復播放
int (*read_pause)(void *opaque, int pause);
int64_t (*read_seek)(void *opaque, int stream_index,
int64_t timestamp, int flags);
// 0表示網絡流不可seek
int seekable;
// 寫入緩衝區中向後查找之前的最大到達位置,用於跟蹤已寫入的數據,以便稍後刷新
unsigned char *buf_ptr_max;
// packet最小尺寸
int min_packet_size;
// 以下字段大部分僅限libavformat內部使用或用的不多,這裏不作解釋
int64_t maxsize;
int direct;
int64_t bytes_read;
int seek_count;
int writeout_count;
int orig_buffer_size;
int short_seek_threshold;
const char *protocol_whitelist;
const char *protocol_blacklist;
int (*write_data_type)(void *opaque, uint8_t *buf, int buf_size,
enum AVIODataMarkerType type, int64_t time);
int ignore_boundary_point;
enum AVIODataMarkerType current_type;
int64_t last_time;
int (*short_seek_get)(void *opaque);
int64_t written;
} AVIOContext;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
其中,AVIOContext的成員變量opaque指向一個URLContext對象,URLContext中是對具體資源文件進行操作的上下文,它包括一個URLProtocol結構體類型的指針變量prot。URLProtocol則是在將資源進行分類的基礎上,對某一類資源操作的函數集。URLContext結構體源碼如下:
typedef struct URLContext {
const AVClass *av_class;
// 關聯/指向相應的廣義輸入文件
const struct URLProtocol *prot;
// 關聯具體廣義輸入文件的句柄,如fd爲文件句柄,socket爲網絡句柄
void *priv_data;
char *filename; // 指定的URL
int flags;
int max_packet_size;
int is_streamed; // true爲流,默認爲false
int is_connected;
AVIOInterruptCB interrupt_callback;
int64_t rw_timeout; // read/write操作超時時間
const char *protocol_whitelist;
const char *protocol_blacklist;
int min_packet_size;
} URLContext;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(2) URLProtocol結構體
URLProtocol結構體表示廣義的輸入文件,是FFmpeg操作I/O的結構,包括文件(file)、網絡數據流(tcp、rtp、… )等等,每種協議都對應着一個URLProtocol結構。該結構位於libavformat/url.h文件中,包括open、close、read、write、seek等操作,部分源碼如下:
typedef struct URLProtocol {
// 協議名稱
const char *name;
int (*url_open)( URLContext *h, const char *url, int flags);
int (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
int (*url_accept)(URLContext *s, URLContext **c);
int (*url_handshake)(URLContext *c);
/**
* Read data from the protocol.
*/
int (*url_read)( URLContext *h, unsigned char *buf, int size);
int (*url_write)(URLContext *h, const unsigned char *buf, int size);
int64_t (*url_seek)( URLContext *h, int64_t pos, int whence);
int (*url_close)(URLContext *h);
int (*url_read_pause)(URLContext *h, int pause);
int64_t (*url_read_seek)(URLContext *h, int stream_index,
int64_t timestamp, int flags);
int (*url_get_file_handle)(URLContext *h);
int (*url_get_multi_file_handle)(URLContext *h, int **handles,
int *numhandles);
int (*url_get_short_seek)(URLContext *h);
int (*url_shutdown)(URLContext *h, int flags);
int priv_data_size;
const AVClass *priv_data_class;
int flags;
int (*url_check)(URLContext *h, int mask);
int (*url_open_dir)(URLContext *h);
int (*url_read_dir)(URLContext *h, AVIODirEntry **next);
int (*url_close_dir)(URLContext *h);
int (*url_delete)(URLContext *h);
int (*url_move)(URLContext *h_src, URLContext *h_dst);
const char *default_whitelist;
} URLProtocol;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
接下來,這裏以HTTP協議爲例,闡述URLProtocol結構體的初始化流程,同時也證明了每一種協議(包括文件)相對應一個URLProtocol對象。具體源碼如下,位於libavformat/Http.c:
const URLProtocol ff_http_protocol = {
.name = "http", //協議名稱
.url_open2 = http_open, // open操作
.url_accept = http_accept,//accept操作
.url_handshake = http_handshake,// 握手操作
.url_read = http_read, // 讀取數據操作
.url_write = http_write, // 寫入數據操作
.url_seek = http_seek, // seek操作
.url_close = http_close, // close操作
.url_get_file_handle = http_get_file_handle,
.url_get_short_seek = http_get_short_seek,
.url_shutdown = http_shutdown,
.priv_data_size = sizeof(HTTPContext),
.priv_data_class = &http_context_class,
.flags = URL_PROTOCOL_FLAG_NETWORK,
.default_whitelist = "http,https,tls,rtp,tcp,udp,crypto,httpproxy"
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
3.編/解碼
(1) AVCodec結構體
AVCodec是與編解碼器(codec)息息相關的數據結構體,它包含了與codec相關的屬性參數以及編解碼操作函數等,比如name爲codec的名稱、pix_fmts爲codec的視頻幀像素格式等等,每一個codec都對應着一個AVCodec結構體。
AVCodec結構體源碼如下:
typedef struct AVCodec {
// 編解碼器名稱
const char *name;
// 描述編解碼器的名稱
const char *long_name;
// media type
enum AVMediaType type;
// 該codec的ID
enum AVCodecID id;
int capabilities;
// 該codec相關的參數
const AVRational *supported_framerates;
// 該codec支持的像素格式,針對視頻幀/圖像而言
const enum AVPixelFormat *pix_fmts;
// 該codec支持的採樣率,針對音頻而言
const int *supported_samplerates;
// 該codec支持的採樣格式,針對音頻而言
const enum AVSampleFormat *sample_fmts;
// 該codec的通道佈局
const uint64_t *channel_layouts;
// 解碼器支持的低分辨率的最大值
uint8_t max_lowres;
const AVClass *priv_class;
const AVProfile *profiles;
const char *wrapper_name;
int priv_data_size;
struct AVCodec *next;
int (*init_thread_copy)(AVCodecContext *);
int (*update_thread_context)(AVCodecContext *dst, const AVCodecContext *src);
const AVCodecDefault *defaults;
// 執行avcodec_register()函數被調用,
// 用於初始化codec的靜態數據
void (*init_static_data)(struct AVCodec *codec);
// 初始化
int (*init)(AVCodecContext *);
int (*encode_sub)(AVCodecContext *, uint8_t *buf, int buf_size,
const struct AVSubtitle *sub);
/**
* 編碼操作:將編碼後的數據保存到AVPacket
*
* @param avctx codec上下文(context)
* @param avpkt 輸出的AVPacket
* @param[in] frame AVFrame存儲的是要被壓縮編碼的裸數據
* @param[out] got_packet_ptr 編碼器設置爲0或1,以指示avpkt中返回的非空包
* @return 0 操作成功
*/
int (*encode2)(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame,
int *got_packet_ptr);
// 解碼操作
int (*decode)(AVCodecContext *, void *outdata, int *outdata_size, AVPacket *avpkt);
// 關閉codec
int (*close)(AVCodecContext *);
// Encode API with decoupled packet/frame dataflow.
int (*send_frame)(AVCodecContext *avctx, const AVFrame *frame);
int (*receive_packet)(AVCodecContext *avctx, AVPacket *avpkt);
// Decode API with decoupled packet/frame dataflow.
int (*receive_frame)(AVCodecContext *avctx, AVFrame *frame);
// flush緩衝區,執行seeking操作是被調用
void (*flush)(AVCodecContext *);
...
} AVCodec;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
(2) AVCodecContext結構體
也許你會發現,對於編解碼而言,除了AVCodec這個非常重要的結構體,在AVCodec的成員函數中還有一個出現頻率非常高的結構體,可以這麼說大部分與編解碼有關的函數都需要傳入一個結構體參數,這個結構體就是AVCodecContext。AVCodecContext結構體存儲視頻流或音頻流使用的編解碼相關信息,比如codec_type表示編解碼器的類型、codec表示採用的編解碼器等等。AVCodecContext結構體源碼如下:
typedef struct AVCodecContext {
enum AVMediaType codec_type; /* 編解碼器的類型(視頻,音頻...) */
const struct AVCodec *codec;// 採用的解碼器AVCodec(H.264,MPEG2...)
enum AVCodecID codec_id; /* see AV_CODEC_ID_xxx */
// 比特率(音頻和視頻的平均比特率)
int64_t bit_rate;
// 壓縮編碼的等級
int compression_level;
// 針對特定編碼器包含的附加信息(例如對於H.264解碼器來說,存儲SPS,PPS等)
uint8_t *extradata;
int extradata_size;
// 時基
// 根據該參數,可以把PTS轉化爲實際的時間(單位爲秒s)
AVRational time_base;
// 圖像寬、高,針對視頻而言
int width, height;
// 像素格式,針對視頻而言
enum AVPixelFormat pix_fmt;
// 獲取像素格式
enum AVPixelFormat (*get_format)(struct AVCodecContext *s, const enum AVPixelFormat * fmt);
// 非B幀之間的最大B幀數
int max_b_frames;
// I/P幀和B幀之間的qscale因子
float b_quant_factor;
// 採樣縱橫比
AVRational sample_aspect_ratio;
// 音頻一幀採樣樣本個數
int frame_size;
// 音頻通道佈局
uint64_t channel_layout;
// 幀率
AVRational framerate;
...
} AVCodecContext;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
4.數據相關結構體
(1) AVStream結構體
AVStream結構體用於存儲一個視頻或音頻流信息,其中,字段nb_frames表示該流包含多少幀數據、字段duration表示該流的長度、字段index標誌是音頻流還是視頻流等等。
typedef struct AVStream {
// 標誌視頻流或音頻流,存儲在AVFormatContext中
int index; /**< stream index in AVFormatContext */
// 指向該視頻/音頻流的AVCodecContext
// @deprecated use the codecpar struct instead
AVCodecContext *codec;
// 時基。通過該值可以把PTS,DTS轉化爲真正的時間
AVRational time_base;
// 該視頻/音頻流的長度
int64_t duration;
// 該視頻/音頻流的幀數
int64_t nb_frames;
// 元數據信息
AVDictionary *metadata;
// 幀率(對視頻來說很重要)
AVRational avg_frame_rate;
// 附帶的圖片。比如說一些MP3,AAC音頻文件附帶的專輯封面
AVPacket attached_pic;
...
// 與該視頻流或音頻流相關的Codec參數
// 由avformat_new_stream()分配、avformat_free_context()釋放
AVCodecParameters *codecpar;
} AVStream;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
(2) AVPacket結構體
AVPacket結構體用於存儲壓縮編碼的視頻或音頻數據相關信息,其中,字段stream_index標誌AVPacket所屬的是音頻流還是視頻流。比如對於H.264來說,通常一個AVPacket的data對應着一個NAL,而一個NAL存儲着一幀圖像。
AVPacket結構體源碼如下:
typedef struct AVPacket {
AVBufferRef *buf;
/**
* Presentation timestamp in AVStream->time_base units; the time at which
* the decompressed packet will be presented to the user.
*/
// Presentation timestamp,即顯示時間戳
int64_t pts;
/**
* Decompression timestamp in AVStream->time_base units; the time at which
* the packet is decompressed.
*/
// Decompression timestamp,即解碼時間戳
int64_t dts;
// 壓縮編碼的視頻或音頻數據
uint8_t *data;
// data的大小
int size;
// 標誌該AVPacket所屬的是音頻流還是視頻流
int stream_index;
int flags;
AVPacketSideData *side_data;
int side_data_elems;
/**
* Duration of this packet in AVStream->time_base units, 0 if unknown.
* Equals next_pts - this_pts in presentation order.
*/
// 該AVPacket的長度
int64_t duration;
// 該AVPacket在流中的字節位置,-1表示未知
int64_t pos;
} AVPacket;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
(3) AVFrame結構體
AVFrame結構體用於存儲解碼後的視/音頻數據相關信息,表示一幀數據。如果AVFrame爲視頻幀數據結構體,字段data數組存儲的是一幀圖像、字段width、height爲圖像的寬、高、key_frame爲是否爲關鍵幀標誌等等;如果AVFrame爲音頻數據結構體,字段data數組存儲的是音頻數據,可包含多幀音頻、字段sample_rate爲音頻的採樣率、字段channels爲音頻通道數量等等。
AVFrame結構體源碼如下:
typedef struct AVFrame {
// 解碼後的原始數據(視頻-YUV或RGB;音頻-PCM)
// 對於packed格式的數據(如RGB24),會存儲在data[0]
// 對於plannar格式的數據(如YUV420P),Y分量存儲在data[0]、U分量存儲在data[1]、V分量存儲在data[2]
uint8_t *data[AV_NUM_DATA_POINTERS];
// data一行數據的長度
// 注意:如果是圖像不一定等於圖像的寬度,往往大於圖像的寬
int linesize[AV_NUM_DATA_POINTERS];
// 視頻幀的寬、高
int width, height;
// 該AVFrame包含幾個音頻幀
int nb_samples;
// 解碼後原始數據類型,比如YUV420、RGB..
// 音頻,詳見AVSampleFormat
// 視頻,詳見AVPixelFormat
int format;
// 是否爲關鍵幀,對視頻來說非常重要
// 1 -> keyframe, 0-> not
int key_frame;
// 幀類型,比如I幀、B幀、P幀...
enum AVPictureType pict_type;
// 視頻幀寬高比,如16:9、4:3...
AVRational sample_aspect_ratio;
// 顯示時間戳
int64_t pts;
// 編碼圖像幀序號
int coded_picture_number;
// 顯示圖像幀序號
int display_picture_number;
// 音頻採樣率
int sample_rate;
// 音頻通道layout
uint64_t channel_layout;
// YUV顏色空間類型
enum AVColorSpace colorspace;
// 元數據
AVDictionary *metadata;
// 音頻通道數量
int channels;
...
} AVFrame;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
至此,FFmpeg框架中最爲重要的結構體,我們基本講解梳理完畢。最後,再借用雷神的FFmpeg關鍵結構體關係圖作爲結尾,一是使得本文能夠前後呼應,二是向大神致敬!
Github實戰項目:https://github.com/jiangdongguo/FFMPEG4Android
————————————————
版權聲明:本文爲CSDN博主「無名之輩FTER」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/AndrExpert/article/details/83268563