ffmpeg中MPEG2 TS 流解碼的流程分析

 

一、FFMPEG 中MPEG2 TS 流解碼的流程分析

其實MPEG2是一族協議,至少已經成為ISO標準的就有以下幾部分: ISO/IEC-13818-1:系統部分; ISO/IEC-13818-2:視頻編碼格式; ISO/IEC-13818-3:音頻編碼格式; ISO/IEC-13818-4:一致性測試; ISO/IEC-13818-5:軟件部分; ISO/IEC-13818-6:數字存儲媒體命令與控制; ISO/IEC-13818-7:高級音頻編碼; ISO/IEC-13818-8:系統解碼實時接口; 我不是很想說實際的音視頻編碼格式,畢竟協議已經很清楚了,我主要想說說這些部分 怎麼組合起來在實際應用中工作的。 第一部分(系統部分)很重要,是構成以MPEG2為基礎的應用的基礎.很繞口,是吧, 我簡單解釋一下:比如DVD實際上是以系統部分定義的PS流為基礎,加上版權管理等其 他技術構成的。
而我們的故事主角,則是另外種流格式,TS流, 它在現階段最大的應用是在數字電視節目的傳輸存儲上,因此,你可以理解TS實際上是種傳輸協議, 實際傳輸的負載關係不大,只是在TS中傳輸了音頻,視頻或者其他數據。
先說一下為什麼會有這兩種格式的出現, PS適用於沒有損耗的環境下面存儲,而TS則適用於可能出現損耗或者錯誤的各種物理網絡環境, 比如你在公交上看的電視,很有可能就是基於TS的DVB-T的應用
我們再來看MPEG2協議中的一些概念,為理解代碼做好功課: l ES(Elementary Stream):
wiki上 說 An elementary stream (ES) is defined by MPEG communication protocol is usually the output of an audio or video encoder. 恩,很簡單吧,就是編碼器編出的組數據,可能是音頻的,視頻的,或者其他數據。 說到著,其實可以對編碼器的流程思考一下,無非是執行: 採樣,量化,編碼這3個步驟中的編碼而已(有些設備可能會包含前面的採樣和量化)。關於視頻編碼的基本理論,還是請參考其它的資料。
l PES (Packetized Elementary Stream): wiki上說allows an Elementary stream to be divided into packets” 其實可以理解成,把個源源不斷的數據(音頻,視頻或者其他)流,打斷成段段,以便處理. l TS(Transport Stream): l PS(Program Stream): 這兩個上面已經有所提及,後面會詳細分析TS,我對PS格式興趣不大. 步入正題 才進入正題,恩,看來閒話太多了,直接看Code. 前面說過,TS是種傳輸協議,因此,對應FFmpeg,可以認為他是種封裝格式。

因此,對應的代碼應該先去libavformat裡面找,很容易找,就是mpegts.c:)。 還是逐步看 過來:

[libavformat/utils.c]

01 int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
02 AVInputFormat *fmt,
03 int buf_size,
04 AVFormatParameters *ap)
05 {
06 int err, probe_size;
07 AVProbeData probe_data, *pd = &probe_data;
08 ByteIOContext *pb = NULL;
09 pd->filename = "" ;
10 if (filename)
11 pd->filename = filename;
12 pd->buf = NULL;
13 pd->buf_size = 0;
14 ################################################## ##############################
15 【1】這段代碼其實是為了針對不需要Open文件的容器Format 的探測,其實就是使用
16 AVFMT_NOFILE標記的容器格式單獨處理,現在只有使用了該標記的Demuxer很少,
17 只有image2_demuxer,rtsp_demuxer,因此我們分析TS時候可以不考慮這部分
18 ################################################## ##############################
19 if (!fmt) {
20 /* guess format if no file can be opened */
21 fmt = av_probe_input_format(pd, 0);
22 }
23 /* Do not open file if the format does not need it. XXX: specific
24 hack needed to handle RTSP/TCP */
25 if (!fmt || !(fmt->flags & AVFMT_NOFILE)) {
26 /* if no file needed do not try to open one */
27 ################################################## #######################
28 【2】這個函數似乎很好理解,無非是帶緩衝的IO的封裝,不過我們既然此了,
29 不妨跟?下去,看看別人對帶緩衝的IO 操作封裝的實現:)
30 ################################################## #######################
31 if ((err=url_fopen(&pb, filename, URL_RDONLY)) < 0) {
32 goto fail;
33 }
34 if (buf_size > 0) {
35 url_setbufsize(pb, buf_size);
36 }
37 for (probe_size= PROBE_BUF_MIN; probe_size<=PROBE_BUF_MAX && !fmt; probe_size<<=1){
38 int score= probe_size < PROBE_BUF_MAX ? AVPROBE_SCORE_MAX/4 : 0;
39 /* read probe data */
40 pd->buf= av_realloc(pd->buf, probe_size + AVPROBE_PADDING_SIZE);
41 ################################################## #####################
42 【3】真正將文件讀入pd的buffer的地方,實際上最終調用 FILE protocol
43 的file_read(),將內容讀入pd 的buf,具體代碼如果有興趣可以自己跟?
44 ################################################## #####################
45 pd->buf_size = get_buffer(pb, pd->buf, probe_size);
46 memset (pd->buf+pd->buf_size, 0, AVPROBE_PADDING_SIZE);
47 if (url_fseek(pb, 0, SEEK_SET) < 0) {
48 url_fclose(pb);
49 if (url_fopen(&pb, filename, URL_RDONLY) < 0) {
50 pb = NULL;
51 err = AVERROR(EIO);
52 goto fail;
53 }
54 }
55 ################################################## ###################
56 【4】此時的pd已經有了需要分析的原始文件,只需要查找相應容器format
57 的Tag 比較,以判斷讀入的究竟為什麼容器格式,這裡
58 ################################################## ###################
59 /* guess file format */
60 fmt = av_probe_input_format2(pd, 1, &score);
61 }
62 av_freep(&pd->buf);
63 }
64 /* if still no format found, error */
65 if (!fmt) {
66 err = AVERROR_NOFMT;
67 goto fail;
68 }
69 /* check filename in case an image number is expected */
70 if (fmt->flags & AVFMT_NEEDNUMBER) {
71 if (!av_filename_number_test(filename)) {
72 err = AVERROR_NUMEXPECTED;
73 goto fail;
74 }
75 }
76 err = av_open_input_stream(ic_ptr, pb, filename, fmt, ap);
77 if (err)
78 goto fail;
79 return 0;
80 fail:
81 av_freep(&pd->buf);
82 if (pb)
83 url_fclose(pb);
84 *ic_ptr = NULL;
85 return err;
86 }
【2】帶緩衝IO的封裝的實現[liavformat/aviobuf.c]
01 int url_fopen(ByteIOContext **s, const char *filename, int flags)
02 {
03 URLContext *h;
04 int err;
05 err = url_open(&h, filename, flags);
06 if (err < 0)
07 return err;
08 err = url_fdopen(s, h);
09 if (err < 0) {
10 url_close(h);
11 return err;
12 }
13 return 0;
14 }

可以看,下面的這個函數,先查找是否是FFmpeg支持的protocol的格式,如果文件 名 ??不符合,則默認是FILE protocol格式,很顯然,這裡protocol判斷是以URL的方式判讀 的, ??因此基本上所有的IO接口函數都是url_xxx的形式。 在這也可以看,FFmpeg支持的protocol有: /* protocols */ REGISTER_PROTOCOL (FILE, file); REGISTER_PROTOCOL (HTTP, http); REGISTER_PROTOCOL (PIPE, pipe); REGISTER_PROTOCOL (RTP, rtp); REGISTER_PROTOCOL (TCP, tcp ); REGISTER_PROTOCOL (UDP, udp); 而大部分情況下,如果你不指明類似 file://xxx,http://xxx 格式,它都以FILE protocol 來處理。

[liavformat/avio.c]

01 int url_open(URLContext **puc, const char *filename, int flags)
02 {
03 URLProtocol *up;
04 const char *p;
05 char proto_str[128], *q;
06 p = filename;
07 q = proto_str;
08 while (*p != '\0' && *p != ':' ) {
09 /* protocols can only contain alphabetic chars */
10 if (! isalpha (*p))
11 goto file_proto;
12 if ((q - proto_str) < sizeof (proto_str) - 1)
13 *q++ = *p;
14 p++;
15 }
16 /* if the protocol has length 1, we consider it is a dos drive */
17 if (*p == '\0' || (q - proto_str) <= 1) {
18 file_proto:
19 strcpy (proto_str, "file" );
20 } else {
21 *q = '\0' ;
22 }
23 up = first_protocol;
24 while (up != NULL) {
25 if (! strcmp (proto_str, up->name))
26 ################################################## ###############
27 很顯然,此時已經知道up,filename,flags
28 ################################################## ###############
29 return url_open_protocol (puc, up, filename, flags);
30 up = up->next;
31 }
32 *puc = NULL;
33 return AVERROR(ENOENT);
34 }
[libavformat/avio.c]
01 int url_open_protocol (URLContext **puc, struct URLProtocol *up,
02 const char *filename, int flags)
03 {
04 URLContext *uc;
05 int err;
06  
07 ################################################## ########################
08 【a】? 為什麼這樣分配空間
09 ################################################## ########################
10 uc = av_malloc( sizeof (URLContext) + strlen (filename) + 1);
11 if (!uc) {
12 err = AVERROR(ENOMEM);
13 goto fail;
14 }
15 #if LIBAVFORMAT_VERSION_MAJOR >= 53
16 uc->av_class = &urlcontext_class;
17 #endif
18 ################################################## ########################
19 【b】? 這樣的用意又是為什麼
20 ################################################## ########################
21 uc->filename = ( char *) &uc[1];
22 strcpy (uc->filename, filename);
23 uc->prot = up;
24 uc->flags = flags;
25 uc->is_streamed = 0; /* default = not streamed */
26 uc->max_packet_size = 0; /* default: stream file */
27 err = up->url_open(uc, filename, flags);
28 if (err < 0) {
29 av_free(uc);
30 *puc = NULL;