如果RTSP传输的是RTP/MPEG2 TS数据,那么ffmpeg的数据流过程如下:
rtsp_read_packet----->rtp_parse_packet--->rtp_valid_packet_in_sequence-------------------------------------------->
-------------------------------->如果是MPEG2 TS------>mpegts_parse_packet
--------------------------------->else s->parse_packet
---------------------------------->else copy buf to packet and return;
static void handle_packet(MpegTSContext *ts, const uint8_t *packet)
该函数解析MPEG2 TS 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;
- //根据iso13818-1种ts packet的格式描述,可以从ts packet中获取PID
- pid = AV_RB16(packet + 1) & 0x1fff;
- if(pid && discard_pid(ts, pid))
- return;
- //如果start_indicator为1表示该ts packet中是PES packet的起始数据
- is_start = packet[1] & 0x40;
- tss = ts->pids[pid];
- //tss为null,表示pid对应的program的filter还没有创建,所以创建一个pes stream以及pes filter
- //一般会创建两种ts filer:PES filter,用来过滤一个节目
- //--------------------:section filer,用来过滤PSI信息
- if (ts->auto_guess && tss == NULL && is_start) {
- //参考add_pes_stream, add_pes_stream函数返回的PESContext变量没有在这里保存起来,但是在add_pes_stream函数中将其作为PESFilter的opaque变量传给了PESFilter,而PESFilter又可以通过ts->pids[pid]得到
- add_pes_stream(ts, pid, -1, 0);
- tss = ts->pids[pid];
- }
- if (!tss)
- return;
- /* continuity check (currently not used) */
- cc = (packet[3] & 0xf);
- //在add_pes_stream中将tss的last_cc设为-1
- cc_ok = (tss->last_cc < 0) || ((((tss->last_cc + 1) & 0x0f) == cc));
- tss->last_cc = cc;
- /* 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;---------------〉获取TS PACKET中的data byte
- }
- /* 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;
- //SECTION FILTER
- if (tss->type == MPEGTS_SECTION) {
- if (is_start) {
- /* pointer field present */
- len = *p++;
- if (p + len > p_end)
- return;
- if (len && cc_ok) {
- /* 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) {
- write_section_data(s, tss,
- p, p_end - p, 1);
- }
- } else {
- if (cc_ok) {
- write_section_data(s, tss,
- p, p_end - p, 0);
- }
- }
- } else {
- //PES filter, pes_cb为mpegts_push_data
- tss->u.pes_filter.pes_cb(tss,
- p, p_end - p, is_start);
- }
- }
mpegts_push_data在构造一个PES packet:
- /* return non zero if a packet could be constructed */
- static void mpegts_push_data(MpegTSFilter *filter,
- const uint8_t *buf, int buf_size, int is_start)
- {
- PESContext *pes = filter->u.pes_filter.opaque;
- MpegTSContext *ts = pes->ts;
- const uint8_t *p;
- int len, code;
- if(!ts->pkt)
- return;
- //开始一个PES packet
- if (is_start) {
- //pes->data_index表示pes packet的数据的位置
- pes->state = MPEGTS_HEADER;
- pes->data_index = 0;
- }
- p = buf;
- while (buf_size > 0) {
- switch(pes->state) {
- case MPEGTS_HEADER:
- //PES_START_SIZE=9,表示PES PACKET HEADER的长度,参考iso13818-1
- len = PES_START_SIZE - pes->data_index;
- if (len > buf_size)
- len = buf_size;
- //将PES packet header部分复制到pes->header中,pes->header根据不同的pes类型,或有不同的结构
- memcpy(pes->header + pes->data_index, p, len);
- pes->data_index += len;
- p += len;
- buf_size -= len;
- if (pes->data_index == PES_START_SIZE) {
- //获取到PES PACKET的长度
- /* we got all the PES or section header. We can now
- decide */
- #if 0
- av_hex_dump_log(pes->stream, AV_LOG_DEBUG, pes->header, pes->data_index);
- #endif
- //packet_start_code_prefix为0x000001
- if (pes->header[0] == 0x00 && pes->header[1] == 0x00 &&
- pes->header[2] == 0x01) {
- /* it must be an mpeg2 PES stream */
- //pes->header[3]为stream id
- code = pes->header[3] | 0x100;
- //0xc0---0xdf-------->audio stream
- //0xe0---0xef-------->video stream
- //0xbd----->private stream1
- //0xfd----->???
- if (!((code >= 0x1c0 && code <= 0x1df) ||
- (code >= 0x1e0 && code <= 0x1ef) ||
- (code == 0x1bd) || (code == 0x1fd)))
- goto skip;
- if (!pes->st) {
- /* allocate stream */
- //为该PES分配AVStream
- new_pes_av_stream(pes, code);
- }
- //下面进行pes packet分析
- pes->state = MPEGTS_PESHEADER_FILL;
- //对于video/audio stream等,
- //total_size指的是下面的packet length
- pes->total_size = AV_RB16(pes->header + 4);
- /* NOTE: a zero total size means the PES size is
- unbounded */
- if (pes->total_size)
- pes->total_size += 6;
- //pes_header_size
- pes->pes_header_size = pes->header[8] + 9;
- } else {
- /* otherwise, it should be a table */
- /* skip packet */
- skip:
- pes->state = MPEGTS_SKIP;
- continue;
- }
- }
- break;
- /**********************************************/
- /* PES packing parsing */
- case MPEGTS_PESHEADER_FILL:
- //pes_header_size指的是pes packet header中optional fields的长度, optional fields由header_length前面的一个byte指定
- len = pes->pes_header_size - pes->data_index;
- if (len > buf_size)
- len = buf_size;
- memcpy(pes->header + pes->data_index, p, len);
- pes->data_index += len;
- p += len;
- buf_size -= len;
- if (pes->data_index == pes->pes_header_size) {
- const uint8_t *r;
- unsigned int flags;
- flags = pes->header[7];
- r = pes->header + 9;
- pes->pts = AV_NOPTS_VALUE;
- pes->dts = AV_NOPTS_VALUE;
- //PTS_DTS_FLAG=10, ONLY PTS
- if ((flags & 0xc0) == 0x80) {
- pes->pts = get_pts(r);
- r += 5;
- //PTS_DTS_FLAG=11, PTS & DTS
- } else if ((flags & 0xc0) == 0xc0) {
- pes->pts = get_pts(r);
- r += 5;
- pes->dts = get_pts(r);
- r += 5;
- }
- /* we got the full header. We parse it and get the payload */
- pes->state = MPEGTS_PAYLOAD;
- }
- break;
- case MPEGTS_PAYLOAD:
- //将PES packet的data 部分copy到 AVPacket中
- if (pes->total_size) {
- len = pes->total_size - pes->data_index;
- if (len > buf_size)
- len = buf_size;
- } else {
- len = buf_size;
- }
- if (len > 0) {
- AVPacket *pkt = ts->pkt;
- if (pes->st && av_new_packet(pkt, len) == 0) {
- memcpy(pkt->data, p, len);
- pkt->stream_index = pes->st->index;
- pkt->pts = pes->pts;
- pkt->dts = pes->dts;
- /* reset pts values */
- pes->pts = AV_NOPTS_VALUE;
- pes->dts = AV_NOPTS_VALUE;
- //stop_parse指示mpegts_parse_packet可以停止parse packet
- ts->stop_parse = 1;
- return;
- }
- }
- buf_size = 0;
- break;
- case MPEGTS_SKIP:
- buf_size = 0;
- break;
- }
- }
- }
由于mpeg2ts里面指定了stream id来去分video/audio/psi,对于video/audio的具体的格式还要进一步的分析。
本文介绍RTSP传输MPEG2TS数据时,ffmpeg如何解析数据流。从TS packet的PID识别到PES packet的构建,再到AVPacket的填充过程,详细解析了MPEG2TS数据包的处理流程。
2379

被折叠的 条评论
为什么被折叠?



