FFMpeg对MPEG2 TS流解码的流程分析
2013-06-29 20:10
831 查看
FFMpeg对MPEG2 TS流解码的流程分析[2]
落鹤生 发布于 2010-04-20 23:18 点击:次 | 来自:百度博客 |
TAG: TS流 IPTV FFMPEG MPEG2 MPEG2TS 视频解码
5.渐入佳境 恩,前面的基础因该已近够了,有点像手剥洋葱头的感觉,我们来看看针对MPEG TS的相应解析过程 我们后面的代码,主要集中在[libavformat/mpegts.c]里面,毛爷爷说:集中优势兵力打围歼,恩,开始吧,蚂蚁啃骨头。 static int mpegts_read_header(AVFormatContext *s, AVFormatParameters *ap) { MpegTSContext *ts = s->priv_data; ByteIOContext *pb = s->pb; uint8_t buf[1024]; int len; int64_t pos; ...... /* read the first 1024 bytes to get packet size */ ##################################################################### 【1】有了前面分析缓冲IO的经历,下面的代码就不是什么问题了:) ##################################################################### pos = url_ftell(pb); len = get_buffer(pb, buf, sizeof(buf)); if (len != sizeof(buf)) goto fail; ##################################################################### 【2】前面侦测文件格式时候其实已经知道TS包的大小了,这里又侦测一次,其实 有些多余,估计是因为解码框架的原因,已近侦测的包大小没能从前面被带过来, 可见框架虽好,却也会带来或多或少的一些不利影响 ##################################################################### ts->raw_packet_size = get_packet_size(buf, sizeof(buf)); if (ts->raw_packet_size <= 0) goto fail; ts->stream = s; ts->auto_guess = 0; if (s->iformat == &mpegts_demuxer) { /* normal demux */ /* first do a scaning to get all the services */ url_fseek(pb, pos, SEEK_SET); ################################################################## 【3】 ################################################################## mpegts_scan_sdt(ts); ################################################################## 【4】 ################################################################## mpegts_set_service(ts); ################################################################## 【5】 ################################################################## handle_packets(ts, s->probesize); /* if could not find service, enable auto_guess */ ts->auto_guess = 1; #ifdef DEBUG_SI av_log(ts->stream, AV_LOG_DEBUG, "tuning done\n"); #endif s->ctx_flags |= AVFMTCTX_NOHEADER; } else { ...... } url_fseek(pb, pos, SEEK_SET); return 0; fail: return -1; } 这里简单说一下MpegTSContext *ts,从上面可以看到,其实这是为了解码不同容器格式所使用的私有数据,只有在相应的诸如mpegts.c 文件才可以使用的,这样,增加了这个库的模块化,而模块化的最大好处,则在于把问题集中到了一个很小的有限区域里面,如果你自己构造程序时候,不妨多参考其基本思想--这样的化,你之后的代码,还有你之后的生活,都将轻松许多。 【3】【4】其实调用的是同一个函数:mpegts_open_section_filter() 我们来看看意欲何为。 static MpegTSFilter *mpegts_open_section_filter(MpegTSContext *ts, unsigned int pid, SectionCallback *section_cb, void *opaque, int check_crc) { MpegTSFilter *filter; MpegTSSectionFilter *sec; #ifdef DEBUG_SI av_log(ts->stream, AV_LOG_DEBUG, "Filter: pid=0x%x\n", pid); #endif 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; } 要完全明白这部分代码,其实需要分析作者对数据结构的定义: 依次为: struct MpegTSContext; | V struct MpegTSFilter; | V +---------------+---------------+ | | V V MpegTSPESFilter MpegTSSectionFilter 其实很简单,就是struct MpegTSContext;中有NB_PID_MAX(8192)个TS的Filter,而每个struct MpegTSFilter可能是PES的Filter或者Section的Filter。 我们先说为什么是8192,在前面的分析中: 给出过TS的语法结构: Syntax No. of bits Mnemonic transport_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)的最大数目,而为什么会有PES和Section的区分,请参考ISO/IEC-13818-1,我实在不太喜欢重复已有的东西. 可见【3】【4】,就是挂载了两个Section类型的过滤器,其实在TS的两种负载中,section是PES 的元数据,只有先解析了section,才能进一步解析PES数据,因此先挂上section的过滤器。 挂载上了两种 section过滤器,如下: ========================================================================= PID |Section Name |Callback ========================================================================= SDT_PID(0x0011) |ServiceDescriptionTable|sdt_cb | | PAT_PID(0x0000) |ProgramAssociationTable|pat_cb 既然自是挂上Callback,自然是在后面的地方使用,因此,我们还是继续 【5】处的代码看看是最重要的地方了,简单看来: handle_packets() | +->read_packet() | +->handle_packet() | +->write_section_data() read_packet()很简单,就是去找sync_byte(0x47),而看来handle_packet()才会是我们真正因该关注的地方了:) 这个函数很重要,我们贴出代码,以备分析: /* handle one TS packet */ static void handle_packet(MpegTSContext *ts, const uint8_t *packet) { AVFormatContext *s = ts->stream; MpegTSFilter *tss; int len, pid, cc, cc_ok, afc, is_start; const uint8_t *p, *p_end; ########################################################## 获取该包的PID ########################################################## pid = AV_RB16(packet + 1) & 0x1fff; if(pid && discard_pid(ts, pid)) return; ########################################################## 是否是PES或者Section的开头(payload_unit_start_indicator) ########################################################## is_start = packet[1] & 0x40; tss = ts->pids[pid]; ########################################################## ts->auto_guess此时为0,因此不考虑下面的代码 ########################################################## if (ts->auto_guess && tss == NULL && is_start) { add_pes_stream(ts, pid, -1, 0); tss = ts->pids[pid]; } if (!tss) return; ########################################################## 代码说的很清楚,虽然检查,但不利用检查的结果 ########################################################## /* continuity check (currently not used) */ cc = (packet[3] & 0xf); cc_ok = (tss->last_cc < 0) || ((((tss->last_cc + 1) & 0x0f) == cc)); tss->last_cc = cc; ########################################################## 跳到adaptation_field_control ########################################################## /* skip adaptation field */ afc = (packet[3] >> 4) & 3; p = packet + 4; if (afc == 0) /* reserved value */ return; if (afc == 2) /* adaptation field only */ return; if (afc == 3) { /* skip adapation field */ p += p[0] + 1; } ########################################################## p已近到达TS包中的有效负载的地方 ########################################################## /* if past the end of packet, ignore */ p_end = packet + TS_PACKET_SIZE; if (p >= p_end) return; ts->pos47= url_ftell(ts->stream->pb) % ts->raw_packet_size; if (tss->type == MPEGTS_SECTION) { if (is_start) { ############################################################# 针对Section,符合部分第一个字节为pointer field,该字段如果为0, 则表示后面紧跟着的是Section的开头,否则是某Section的End部分和 另一Section的开头,因此,这里的流程实际上由两个值is_start (payload_unit_start_indicator)和len(pointer field)一起来决定 ############################################################# /* pointer field present */ len = *p++; if (p + len > p_end) return; if (len && cc_ok) { ######################################################## 1).is_start == 1 len > 0 负载部分由A Section的End部分和B Section的Start组成,把A的 End部分写入 ######################################################## /* write remaining section bytes */ write_section_data(s, tss, p, len, 0); /* check whether filter has been closed */ if (!ts->pids[pid]) return; } p += len; if (p < p_end) { ######################################################## 2).is_start == 1 len > 0 负载部分由A Section的End部分和B Section的Start组成,把B的Start部分写入 或者: 3). is_start == 1 len == 0 负载部分仅是一个Section的Start部分,将其写入 ######################################################## write_section_data(s, tss, p, p_end - p, 1); } } else { if (cc_ok) { ######################################################## 4).is_start == 0 负载部分仅是一个Section的中间部分部分,将其写入 ######################################################## write_section_data(s, tss, p, p_end - p, 0); } } } else { ########################################################## 如果是PES类型,直接调用其Callback,但显然,只有Section部分解析完成后才可能解析PES ########################################################## tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start); } } write_section_data()函数则反复收集buffer中的数据,指导完成相关Section的重组过程,然后调用之前注册的两个section_cb: 后面我们将分析之前挂在的两个section_cb,待续...... (bohryan) |
相关文章推荐
- FFMpeg对MPEG2 TS流解码的流程分析
- FFMpeg对MPEG2 TS流解码的流程分析[2]
- FFMpeg对MPEG2 TS流解码的流程分析
- FFMpeg对MPEG2 TS流解码的流程分析[2]
- FFMpeg对MPEG2 TS流解码的流程分析
- FFMpeg对MPEG2 TS流解码的流程分析--一
- FFMpeg对MPEG2 TS流解码的流程分析[2]
- FFMpeg对MPEG2 TS流解码的流程分析
- FFMpeg对MPEG2 TS流解码的流程分析
- FFMpeg对MPEG2 TS流解码的流程分析
- FFMpeg对MPEG2 TS流解码的流程分析
- FFMpeg对MPEG2 TS流解码的流程分析[2]
- FFMpeg对MPEG2 TS流解码的流程分析[2]
- ffmpeg中MPEG2 TS 流解码的流程分析
- ffmpeg中MPEG2 TS 流解码的流程分析
- ffmpeg中MPEG2 TS 流解码的流程分析
- ffmpeg中MPEG2 TS 流解码的流程分析
- ffmpeg 解码流程分析
- FFmpeg解码H264流程分析
- FFMpeg的output_example.c例子分析 (解码流程)