mpegts.c中handle_packet() 函数代码注释


author: hjjdebug
date : 2025年 07月 14日 星期一 17:13:07 CST
descrip: mpegts.c中handle_packet() 函数代码注释


文章目录

  • 1. handle_packet() 的功能
  • 2. 上层调用接口
  • 3. 代码注释
  • 4. 概括归纳.

1. handle_packet() 的功能

ffmpeg 中函数调用链为: handle_packets → handle_packet,
hanle_packet 每次分析188字节
解析TS包头部信息,并根据PID(包标识符)将数据分发给对应的过滤器进行处理.
简单说就是解析,组包.
handle_packets 就是循环调用handle_packet 来处理一系列小包
可见handle_packet 是数据包处理的核心函数,承上启下.
其再上一层往往是是为了读取一个pkt

2. 上层调用接口

举3个响当当的上层接口.

例如1: avformat_open_input.
0 in handle_packet of libavformat/mpegts.c:2950
1 in handle_packets of libavformat/mpegts.c:3231
2 in mpegts_read_header of libavformat/mpegts.c:3362
3 in avformat_open_input of libavformat/demux.c:316

例如2: avformat_find_stream_info
0 in handle_packet of libavformat/mpegts.c:2950
1 in handle_packets of libavformat/mpegts.c:3231
2 in mpegts_read_packet of libavformat/mpegts.c:3500
3 in ff_read_packet of libavformat/demux.c:576
4 in read_frame_internal of libavformat/demux.c:1264
5 in avformat_find_stream_info of libavformat/demux.c:2624

例如3: av_read_frame 
0 in handle_packet of libavformat/mpegts.c:2950
1 in handle_packets of libavformat/mpegts.c:3231
2 in mpegts_read_packet of libavformat/mpegts.c:3500
3 in ff_read_packet of libavformat/demux.c:576
4 in read_frame_internal of libavformat/demux.c:1264
5 in av_read_frame of libavformat/demux.c:1473

3. 代码注释

来自ffmpeg6.1.1 libavformat/mpegts.c

static int handle_packet(MpegTSContext* ts, const uint8_t* packet, int64_t pos)
{
    MpegTSFilter* tss;
    int len, pid, cc, expected_cc, cc_ok, afc, is_start, is_discontinuity,
        has_adaptation, has_payload;
    const uint8_t *p, *p_end;

    pid = AV_RB16(packet + 1) & 0x1fff; //提取pid
    is_start = packet[1] & 0x40;  //判断数据包起始位
    tss = ts->pids[pid]; //获取过滤器
    if (ts->auto_guess && !tss && is_start)
    {
        add_pes_stream(ts, pid, -1); //没有过滤器,添加pes 流
        tss = ts->pids[pid];
    }
    if (!tss)
        return 0;
    if (is_start)
        tss->discard = discard_pid(ts, pid); //判读pid 是否被抛弃
    if (tss->discard)
        return 0;
    ts->current_pid = pid;

    afc = (packet[3] >> 4) & 3;
    if (afc == 0) /* reserved value */
        return 0;
    has_adaptation = afc & 2;
    has_payload = afc & 1;
    is_discontinuity = has_adaptation && packet[4] != 0 && /* with length > 0 */
        (packet[5] & 0x80); /* and discontinuity indicated */

    /* continuity check (currently not used) */
    cc = (packet[3] & 0xf);
    expected_cc = has_payload ? (tss->last_cc + 1) & 0x0f : tss->last_cc;
    cc_ok = pid == 0x1FFF || // null packet PID
        is_discontinuity || tss->last_cc < 0 || expected_cc == cc;

    tss->last_cc = cc;
    if (!cc_ok)
    {
        av_log(ts->stream, AV_LOG_DEBUG,
            "Continuity check failed for pid %d expected %d got %d\n",
            pid, expected_cc, cc);
        if (tss->type == MPEGTS_PES)
        {
            PESContext* pc = tss->u.pes_filter.opaque;
            pc->flags |= AV_PKT_FLAG_CORRUPT;
        }
    }

    if (packet[1] & 0x80)
    { //检查错误标志
        av_log(ts->stream, AV_LOG_DEBUG, "Packet had TEI flag set; marking as corrupt\n");
        if (tss->type == MPEGTS_PES)
        {
            PESContext* pc = tss->u.pes_filter.opaque;
            pc->flags |= AV_PKT_FLAG_CORRUPT;
        }
    }

    p = packet + 4;  //跳过头部字节
    if (has_adaptation)
    {
        int64_t pcr_h;
        int pcr_l;
        if (parse_pcr(&pcr_h, &pcr_l, packet) == 0)
            tss->last_pcr = pcr_h * 300 + pcr_l;
        /* skip adaptation field */
        p += p[0] + 1; //跳过适配域字段
    }
    /* if past the end of packet, ignore */
    p_end = packet + TS_PACKET_SIZE;
    if (p >= p_end || !has_payload)
        return 0;

    if (pos >= 0)  //pos 是当前的文件位置
    {
        av_assert0(pos >= TS_PACKET_SIZE);
        ts->pos47_full = pos - TS_PACKET_SIZE;
    }

    if (tss->type == MPEGTS_SECTION)
    {  //已经跳过了adaption field
        if (is_start)
        {
            /* pointer field present */
            len = *p++;  //这个len 一般为0
            if (len > p_end - p)
                return 0;
            if (len && cc_ok)
            {
                /* write remaining section bytes */
                write_section_data(ts, tss,
                    p, len, 0);
                /* check whether filter has been closed */
                if (!ts->pids[pid])
                    return 0;
            }
            p += len;
            if (p < p_end)
            {
                write_section_data(ts, tss,
                    p, p_end - p, 1);
            }
        }
        else
        {
            if (cc_ok)
            {
                write_section_data(ts, tss,
                    p, p_end - p, 0);
            }
        }

        // stop find_stream_info from waiting for more streams
        // when all programs have received a PMT
        if (ts->stream->ctx_flags & AVFMTCTX_NOHEADER && ts->scan_all_pmts <= 0)
        {
            int i;
            for (i = 0; i < ts->nb_prg; i++)
            {
                if (!ts->prg[i].pmt_found)
                    break;
            }
            if (i == ts->nb_prg && ts->nb_prg > 0)
            {
                av_log(ts->stream, AV_LOG_DEBUG, "All programs have pmt, headers found\n");
                ts->stream->ctx_flags &= ~AVFMTCTX_NOHEADER;
            }
        }
    }
    else
    {
        int ret;
        // Note: The position here points actually behind the current packet.
        if (tss->type == MPEGTS_PES)
        {
            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;
}

4. 概括归纳.

TS包头解析
通过AV_RB16(packet + 1) & 0x1fff提取PID,
判断是否为有效数据包起始(is_start = packet[1] & 0x40),

检查连续性计数器(CRC)确保数据完整性
检查错误标志

适配域处理
根据afc = (packet[3] >> 4) & 3的值跳过适配字段(adaptation field),
仅处理有效负载数据。当afc == 3时需跳过适配域长度p[0] + 1字节812。

PID路由机制
通过ts->pids[pid]查找对应的过滤器(MpegTSFilter * tss),
根据过滤器类型(MPEGTS_PES或MPEGTS_SECTION)调用相应的处理回调函数.
例如
PSI/SI表数据会调用 write_section_data(ts,tss,p,p_end_p,1); //1代表is_start
由tss->section_cb 去回调pat_cb, pmt_cb, sdt_cb, scte_data_cb. 依据section_cb 指针的值
PES流会触发tss->u.pes_filter.pes_cb,其指针是mpegts_push_data

各回调函数就不详细解释了.
简单介绍一下组包过程. pes 保留了状态,
switch (pes->state)
{
case MPEGTS_HEADER:
case MPEGTS_PESHEADER:
case MPEGTS_PESHEADER_FILL:
case MPEGTS_PAYLOAD:
case MPEGTS_SKIP:
}
根据不同的状态对小包进行处理,最后合成一个合格的pkt,
然后通过ts->stop_parse标志控制退出循环.
不仅pes_cb 可以组包, 发现scte_data_cb 也能组包. pat_cb,pmt_cb,sdt_cb是不会组包的,
它们会影响ts状态, 例如会添加过滤器等

你可能感兴趣的:(#,ts,流,c语言,ffmpeg,ts,handle_packet)