生活随笔
收集整理的這篇文章主要介紹了
ffmpeg解析TS流
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
介紹:? MPEG的系統(tǒng)層編碼為不同的應(yīng)用場(chǎng)景設(shè)計(jì)了兩種格式:?
TS(Transport Stream)?和
PS(Program Stream), 它們兩者之間不具有層級(jí)關(guān)系, 在邏輯上,它們兩者都是由PES(Packetized Elementary Stream)包組成的, 所以可以很方便地實(shí)現(xiàn)相互轉(zhuǎn)換. TS(Transport Stream):? 是將具有一個(gè)或多個(gè)獨(dú)立時(shí)間基的一個(gè)或多個(gè)節(jié)目(包括音頻和視頻)組成一個(gè)流, 組成同一個(gè)節(jié)目的基本流(如一個(gè)視頻流,多個(gè)音頻流)的PES包有一個(gè)共用的時(shí)間基。 TS的包長(zhǎng)標(biāo)準(zhǔn)為
188bytes. 從上面的定義可以分成
三層來看TS/PS。 ES層 ? : 由單獨(dú)的音頻(如mp3),視頻流(如h.264)組成基本的ES(Elementary Stream)。 PES層 ?: 將基本的ES按一定的規(guī)則(如H.264以AU)進(jìn)行封裝,并打上時(shí)間戳,組成PES。 TS/PS層: 將PES包進(jìn)行切分后再封裝成188bytes大小的TS包, 同時(shí)還將一些節(jié)目信息也封裝成TS包(稱為section), 兩者共同組成TS層。 從上面的總結(jié),TS/PS總體上來說,是一種封裝格式,用來承載數(shù)據(jù)。 所以FFmpeg 將TS/PS的解析文件定義在
libavformat/mpegts.c文件中 將音頻,視頻的解碼定義在
libavcodec/mpeg12.c文件中 下面來看FFmpeg是如何進(jìn)行TS的demuxer的。
1. MPEG2-TS的demuxer函數(shù) AVInputFormat ff_mpegts_demuxer?=?{?????"mpegts",?????NULL_IF_CONFIG_SMALL("MPEG-2 transport stream format"),????sizeof(MpegTSContext),????mpegts_probe,????mpegts_read_header,????mpegts_read_packet,?????mpegts_read_close,?????read_seek,????mpegts_get_pcr,????.flags?=?AVFMT_SHOW_IDS|AVFMT_TS_DISCONT,?#ifdef USE_SYNCPOINT_SEARCH?????.read_seek2?=?read_seek2,?#endif?}; 2. 解析流中的TS格式 ?
/*
?* 出現(xiàn)3種格式,主要原因是:
?* TS標(biāo)準(zhǔn)是 188Bytes;
?* 日本標(biāo)準(zhǔn)是192Bytes的DVH-S格式;
?* 第三種的 204Bytes則是在188Bytes的基礎(chǔ)上,加上16Bytes的FEC(前向糾錯(cuò)).
?*/#define TS_PACKET_SIZE 188#define TS_DVHS_PACKET_SIZE 192#define TS_FEC_PACKET_SIZE 204 #define TS_MAX_PACKET_SIZE 204 //<?maximum score,?half of that?is?used?for?file-extension-based detection#define AVPROBE_SCORE_MAX 100 /*
?* 函數(shù)功能:
?* 分析流中是三種TS格式的哪一種
?*/static?int?mpegts_probe(AVProbeData?*p){#define CHECK_COUNT 10 ??const?int?size=?p->buf_size;??int?score,?fec_score,?dvhs_score;??int?check_count=?size?/?TS_FEC_PACKET_SIZE; ??if?(check_count?<?CHECK_COUNT)??????return?-1; ??score ? ??=?analyze(p->buf,?TS_PACKET_SIZE?*check_count,?TS_PACKET_SIZE?,?NULL)?? ? ? ? ? ? ? *?CHECK_COUNT?/?check_count;??dvhs_score=?analyze(p->buf,?TS_DVHS_PACKET_SIZE*check_count,?TS_DVHS_PACKET_SIZE,?NULL)? ? ? ? ? ? ? *?CHECK_COUNT?/?check_count;??fec_score?=?analyze(p->buf,?TS_FEC_PACKET_SIZE?*check_count,?TS_FEC_PACKET_SIZE?,?NULL)? ? ? ? ? ? ? *?CHECK_COUNT?/?check_count; ??/*????*?we need a clear definition?for?the returned score?,???*?otherwise things will become messy sooner?or?later???*/??if?(score?>?fec_score?&&?score?>?dvhs_score?&&?score?>?6)?????return AVPROBE_SCORE_MAX?+?score?-?CHECK_COUNT;??else?if(dvhs_score?>?score?&&?dvhs_score?>?fec_score?&&?dvhs_score?>?6)?????return AVPROBE_SCORE_MAX?+?dvhs_score?-?CHECK_COUNT;??else?if(fec_score?>?6)?????return AVPROBE_SCORE_MAX?+?fec_score?-?CHECK_COUNT;??else?????return?-1;} /*
?* 函數(shù)功能:
?* 在size大小的buf中,尋找滿足特定格式,長(zhǎng)度為packet_size的
?* packet的個(gè)數(shù);
?* 顯然,返回的值越大越可能是相應(yīng)的格式(188/192/204)
?*/static?int?analyze(const?uint8_t?*buf,?int?size,?int?packet_size,?int?*index){??int?stat[TS_MAX_PACKET_SIZE];??int?i;??int?x=0;??int?best_score=0; ??memset(stat,?0,?packet_size*sizeof(int));??????for?(x=i=0;?i?<?size-3;?i++)??{????if?((buf[i]?==?0x47)?&&?!(buf[i+1]?&?0x80)?&&?(buf[i+3]?&?0x30))????{??????stat[x]++;??????????????????if?(stat[x]?>?best_score)??????{????????best_score=?stat[x];????????if?(index)???????????*index=?x;??????}????} ????x++;????if?(x?==?packet_size)???????x=?0;???}??????return best_score;} buf[i] == 0x47 ? 其中的sync_byte固定為0x47,即上面的.?
!(buf[i+1] & 0x80) ?? 由于transport_error_indicator為1的TS Packet實(shí)際有錯(cuò)誤, 表示攜帶的數(shù)據(jù)無意義, 這樣的Packet顯然沒什么意義.
buf[i+3] & 0x30? 對(duì)于adaptation_field_control, 如果取值為0x00,則表示為未來保留,現(xiàn)在不用. 這就是MPEG TS的偵測(cè)過程.
3. MPEG2-TS頭解析 #define NB_PID_MAX 8192#define MAX_SECTION_SIZE 4096????????/*?pids?*/#define PAT_PID 0x0000#define SDT_PID 0x0011????????/*?table ids?*/#define PAT_TID 0x00#define PMT_TID 0x02#define SDT_TID 0x42 /*?*?函數(shù)功能:?*??*/int?mpegts_read_header(AVFormatContext?*s,?AVFormatParameters?*ap){??/*
???* MpegTSContext , 是為了解碼不同容器格式所使用的私有數(shù)據(jù),
???* 只有在相應(yīng)的諸如mpegts.c文件才可以使用的.
???* 這樣,增加了這個(gè)庫(kù)的模塊化.
???*/??MpegTSContext?*ts?=?s->priv_data;??AVIOContext?*pb?=?s->pb;??uint8_t buf[8*1024];??int?len;??int64_t pos; ??/*?read the first 8*1024 bytes?to?get?packet size?*/??pos?=?avio_tell(pb);? ? ? ? ? ? ? ? ? ?// 獲取buf的當(dāng)前位置??len?=?avio_read(pb,?buf,?sizeof(buf));?// 從pb->opaque中讀取sizeof(buf)個(gè)字節(jié)到buf??if?(len?!=?sizeof(buf))????goto fail; ??/*?
???* 獲得TS包的實(shí)際長(zhǎng)度
???*/??ts->raw_packet_size?=?get_packet_size(buf,?sizeof(buf));??if?(ts->raw_packet_size?<=?0)???{????av_log(s,?AV_LOG_WARNING,?"Could not detect TS packet size, defaulting to non-FEC/DVHS\n");????ts->raw_packet_size?=?TS_PACKET_SIZE;??} ??ts->stream?=?s;???ts->auto_guess?=?0;????if?(s->iformat?==?&ff_mpegts_demuxer)???{????/*?normal demux?*/????/*?first?do?a scaning?to?get?all the services?*/????if?(avio_seek(pb,?pos,?SEEK_SET)?<?0)????{??????av_log(s,?AV_LOG_ERROR,?"Unable to seek back to the start\n");????} ????/*
?????* 掛載了兩個(gè)Section類型的過濾器,
?????* 其實(shí)在TS的兩種負(fù)載中,section是PES的元數(shù)據(jù),
?????* 只有先解析了section,才能進(jìn)一步解析PES數(shù)據(jù),因此先掛上section的過濾器。
?????*/????mpegts_open_section_filter(ts,?SDT_PID,?sdt_cb,?ts,?1);????mpegts_open_section_filter(ts,?PAT_PID,?pat_cb,?ts,?1); ????/*?????*/?????handle_packets(ts,?s->probesize?/?ts->raw_packet_size); ????/*?if?could?not?find service,?enable auto_guess?*/????ts->auto_guess?=?1;????av_dlog(ts->stream,?"tuning done\n");????s->ctx_flags?|=?AVFMTCTX_NOHEADER;??}???else???{????...??} ??avio_seek(pb,?pos,?SEEK_SET);???return 0; fail:??return?-1;} MpegTSFilter?*mpegts_open_section_filter(MpegTSContext*?ts,??????????????????????????????????????????unsigned?int?pid,?????????????????????????????????????????SectionCallback*?section_cb,??????????????????????????????????????????void*?opaque,?????????????????????????????????????????int?check_crc){???MpegTSFilter?*filter;??MpegTSSectionFilter?*sec;? ??av_dlog(ts->stream,?"Filter: pid=0x%x\n",?pid); ??if?(pid?>=?NB_PID_MAX?||?ts->pids[pid])????return?NULL; ??filter?=?av_mallocz(sizeof(MpegTSFilter));??if?(!filter)????return?NULL; ??ts->pids[pid]?=?filter;??filter->type?=?MPEGTS_SECTION;??filter->pid?=?pid;???filter->last_cc?=?-1;??sec?=?&filter->u.section_filter;??sec->section_cb?=?section_cb;??sec->opaque?=?opaque;??sec->section_buf=?av_malloc(MAX_SECTION_SIZE);??sec->check_crc?=?check_crc; ??if?(!sec->section_buf)???{????av_free(filter);????return?NULL;??} ??return?filter;} 對(duì)于這部分代碼,需要分析數(shù)據(jù)結(jié)構(gòu)的定義: 依次為: struct MpegTSContext; | V struct MpegTSFilter; | V +--------------+---------------+ | ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| V ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?V MpegTSPESFilter ? ? ? ?MpegTSSectionFilter 就是struct MpegTSContext;中有NB_PID_MAX(8192)個(gè)TS的Filter, 而每個(gè)struct MpegTSFilter 可能是 PES ? ?的Filter 或者是 Section的Filter。 為什么NB_PID_MAX 是 8192, 需要看TS的語(yǔ)法結(jié)構(gòu)(ISO/IEC 138138-1 page 19):
?
Syntax ? ? ? ? ? ? ? ? ? ? ? ? ?No.?of bits ? ? ? ? Mnemonictransport_packet(){???sync_byte ? ? ? ? ? ? ? ? ? ? ? ?8 ? ? ? ? ? ? ? ? bslbf??transport_error_indicator ? ? ? ?1 ? ? ? ? ? ? ? ? bslbf??payload_unit_start_indicator ? ? 1 ? ? ? ? ? ? ? ? bslbf??transport_priority ? ? ? ? ? ? ? 1 ? ? ? ? ? ? ? ? bslbf??PID ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?13 ? ? ? ? ? ? ? ?uimsbf??transport_scrambling_control ? ? 2 ? ? ? ? ? ? ? ? bslbf??adaptation_field_control ? ? ? ? 2 ? ? ? ? ? ? ? ? bslbf??continuity_counter ? ? ? ? ? ? ? 4 ? ? ? ? ? ? ? ? uimsbf??if?(adaptation_field_control=='10'?||???????adaptation_field_control=='11'?)??{?????????adaptation_field()???}??????????if?(adaptation_field_control=='01'?||???????adaptation_field_control=='11'?)???{?????for?(i=0;i<N;i++)????{???????data_byte ? ? ? ? ? ? ? ? ? ? 8 ? ? ? ? ? ? ? ?bslbf????}???}?} 而8192,是2^13=8192(PID)的最大數(shù)目, 為什么會(huì)有PES和Section的區(qū)分,更詳細(xì)的可以參考ISO/IEC-13818-1. 掛載上了兩種section過濾器,如下: ========================================================================= PID ? ? ? ? ? ? ? ?|Section Name ? ? ? ? ? |Callback ========================================================================= SDT_PID(0x0011) ? ?|ServiceDescriptionTable|sdt_cb | ? ? ? ? ? ? ? ? ? ? ? | PAT_PID(0x0000) ? ?|ProgramAssociationTable|pat_cb ========================================================================= 設(shè)計(jì)成回調(diào)函數(shù),是為了在后面使用。
4. MPEG2-TS的包處理 int?handle_packets(MpegTSContext?*ts,?int?nb_packets){??AVFormatContext?*s?=?ts->stream;??uint8_t packet[TS_PACKET_SIZE];??int?packet_num,?ret;???????ts->stop_parse?=?0;??packet_num?=?0; ??for?(?;?;?)???{????packet_num++;????????if?(nb_packets?!=?0?&&?packet_num?>=?nb_packets?||????????ts->stop_parse?>?1)?????{??????ret?=?AVERROR(EAGAIN);??????break;????} ????if?(ts->stop_parse?>?0)??????break;????????????ret?=?read_packet(s,?packet,?ts->raw_packet_size);????if?(ret?!=?0)??????return ret; ????ret?=?handle_packet(ts,?packet);????if?(ret?!=?0)??????return ret;??}?????return 0;?} 它的代碼結(jié)構(gòu)很簡(jiǎn)單: handle_packets() | +->read_packet() | +->handle_packet() | +->write_section_data() read_packet(), ?很簡(jiǎn)單, 就是去找sync_byte(0x47), handle_packet(),是真正處理數(shù)據(jù)的地方.它的代碼如下:
/*?
?* 功能: handle one TS packet?
?*/int?handle_packet(MpegTSContext?*ts,?const?uint8_t?*packet){??AVFormatContext?*s?=?ts->stream;??MpegTSFilter?*tss;??int?len,?pid,?cc,?expected_cc,?cc_ok,?afc,?is_start;??const?uint8_t?*p,?*p_end;??int64_t pos; ??/* 獲取該包的PID */??pid?=?AV_RB16(packet?+?1)?&?0x1fff;??if?(pid?&&?discard_pid(ts,?pid))?????return 0; ??/*?
???* 是否是PES或者Section的開頭
???* 即syntax element: payload_unit_start_indicator?
???*/??is_start?=?packet[1]?&?0x40;??tss?=?ts->pids[pid]; ??/*?
???* ts->auto_guess此時(shí)為0,因此不考慮下面的代碼
???*/??if?(ts->auto_guess?&&?tss?==?NULL?&&?is_start)???{????add_pes_stream(ts,?pid,?-1);????tss?=?ts->pids[pid];??}??if?(!tss)????return 0; ??/*?
???* continuity check (currently not used)?
???* 雖然檢查,但不利用檢查的結(jié)果
???*/??cc?=?(packet[3]?&?0xf);??expected_cc?=?(packet[3]?&?0x10)???(tss->last_cc?+?1)?&?0x0f?:?tss->last_cc;??cc_ok?=?(tss->last_cc?<?0)?||?(expected_cc?==?cc);??tss->last_cc?=?cc; ??/*?
???* 解析 adaptation_field_control 語(yǔ)法元素
???* =======================================================
???* 00 | Reserved for future use by ISO/IEC
???* 01 | No adaptation_field, payload only
???* 10 | Adaptation_field only, no payload
???* 11 | Adaptation_field follwed by payload
???* =======================================================
???*/???afc?=?(packet[3]?>>?4)?&?3;??p?=?packet?+?4;??if?(afc?==?0)?/*?reserved value?*/????return 0;???if?(afc?==?2)?/*?adaptation field only?*/?????return 0;??if?(afc?==?3)???{????/*?
?????* 跳過 adapation field?
?????* p[0]對(duì)應(yīng)的語(yǔ)法元素為: adaptation_field_length
?????*/????p?+=?p[0]?+?1;??} ??/*?
???* if past the end of packet, ignore?
???* p已近到達(dá)TS包中的有效負(fù)載的地方
???*/??p_end?=?packet?+?TS_PACKET_SIZE;??if?(p?>=?p_end)????return 0; ??pos?=?avio_tell(ts->stream->pb);??ts->pos47=?pos?%?ts->raw_packet_size; ??if?(tss->type?==?MPEGTS_SECTION)???{????/*
?????* 針對(duì)Section, 第一個(gè)字節(jié)對(duì)應(yīng)的語(yǔ)法元素為:pointer_field(見2.4.4.1),
?????* 它表示在當(dāng)前TS包中,從pointer_field開始到第一個(gè)section的第一個(gè)字節(jié)間的字節(jié)數(shù)。
?????* 當(dāng)TS包中有至少一個(gè)section的起始時(shí),
?????*? ? payload_unit_start_indicator = 1 且 TS負(fù)載的第一個(gè)字節(jié)為pointer_field;
?????*? ? pointer_field = 0x00時(shí),表示section的起始就在這個(gè)字節(jié)之后;
?????* 當(dāng)TS包中沒有section的起始時(shí),?
?????*? ? payload_unit_start_indicator = 0 且 TS負(fù)載中沒有pointer_field;
?????*/????if?(is_start)?????{??????/*?pointer field present?*/??????len?=?*p++;??????if?(p?+?len?>?p_end)????????return 0; ??????if?(len?&&?cc_ok)???????{????????/*?
?????????* write remaining section bytes?
?????????* TS包的負(fù)載部分由Section A的End部分和Section B的Start組成,
?????????* 先把Section A的End部分寫入
?????????*/????????write_section_data(s,?tss,?p,?len,?0); ????????/*?check whether?filter?has been closed?*/????????if?(!ts->pids[pid])??????????return 0;??????}??????p?+=?len; ??????if?(p?<?p_end)???????{?????????/*
?????????* 再將Section B的Start部分寫入
?????????*/????????write_section_data(s,?tss,?p,?p_end?-?p,?1);??????}????}?????else?????{??????/* TS包負(fù)載僅是一個(gè)Section的中間部分部分,將其寫入*/??????if?(cc_ok)???????{????????write_section_data(s,?tss,?p,?p_end?-?p,?0);??????}????}??}???else???{????int?ret; ????/*?
?????* 如果是PES類型,直接調(diào)用其Callback,
?????* 但顯然,只有Section部分解析完成后才可能解析PES
?????*/????//?Note:?The position here points actually behind the current packet.????if?((ret?=?tss->u.pes_filter.pes_cb(tss,?p,?p_end?-?p,?is_start,????????pos?-?ts->raw_packet_size))?<?0)??????return ret;??} ??return 0;} write_section_data()函數(shù): 反復(fù)收集buffer中的數(shù)據(jù),指導(dǎo)完成相關(guān)Section的重組過程, 然后調(diào)用之前注冊(cè)的兩個(gè)section_cb.
5. 節(jié)目指定信息的解析 /*
?* PAT(Program Association Table) 節(jié)目相關(guān)表
?* 提供了節(jié)目號(hào)與PID值的對(duì)應(yīng)關(guān)系
?* 見ISO/IEC 13818-1 2.4.4.3 Table 2-30
?*/void pat_cb(MpegTSFilter?*filter,?const?uint8_t?*section,?int?section_len); /*
?* PMT(Program Map Table) 節(jié)目映射表
?* 提供了節(jié)目號(hào)與組成節(jié)目的元素之間的映射關(guān)系--或者稱為"節(jié)目定義"
?* 見ISO/IEC 13818-1 2.4.4.8 Table 2-33
?*/void pmt_cb(MpegTSFilter?*filter,?const?uint8_t?*section,?int?section_len); /*
?* SDT(Transport Stream Description Table) TS描述表
?* 用于定義TS描述子的表
?* 見ISO/IEC 13818-1 2.4.4.12 Table 2-36
?*/void sdt_cb(MpegTSFilter?*filter,?const?uint8_t?*section,?int?section_len) 6. 解析PES包 /*?
?* 見ISO/IEC 13818-1 2.4.3.6 Table 2-21
?*/int?mpegts_push_data(MpegTSFilter*?filter,?????????????????????const?uint8_t*?buf,??????????????????????int?buf_size,??????????????????????int?is_start,?????????????????????int64_t pos); 至此,整個(gè)TS層的解析基本完成。
轉(zhuǎn)載于:https://www.cnblogs.com/yulang314/p/3737854.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)
總結(jié)
以上是生活随笔為你收集整理的ffmpeg解析TS流的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。