• 2. 修复FFMPEG 复用 PAT、PMT发送间隔小于25ms的错误


    分析ffmpeg源码

    分析问题

    mpegtsenc.c

    找到发送PAT、PMT的函数

    /* send SDT, PAT and PMT tables regularly */
    static void retransmit_si_info(AVFormatContext *s, int force_pat, int64_t dts)
    {
        MpegTSWrite *ts = s->priv_data;
        int i;
    
        if (++ts->sdt_packet_count == ts->sdt_packet_period ||
            (dts != AV_NOPTS_VALUE && ts->last_sdt_ts == AV_NOPTS_VALUE) ||
            (dts != AV_NOPTS_VALUE && dts - ts->last_sdt_ts >= ts->sdt_period*90000.0)
        ) {
            ts->sdt_packet_count = 0;
            if (dts != AV_NOPTS_VALUE)
                ts->last_sdt_ts = FFMAX(dts, ts->last_sdt_ts);
            mpegts_write_sdt(s);
        }
        if (++ts->pat_packet_count == ts->pat_packet_period ||
            (dts != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE) ||
            (dts != AV_NOPTS_VALUE && dts - ts->last_pat_ts >= ts->pat_period*90000.0) ||
            force_pat) {
            ts->pat_packet_count = 0;
            if (dts != AV_NOPTS_VALUE)
                ts->last_pat_ts = FFMAX(dts, ts->last_pat_ts);
            mpegts_write_pat(s);
            for (i = 0; i < ts->nb_services; i++)
                mpegts_write_pmt(s, ts->services[i]);
        }
    }
    

    从源码分析,可以知道PAT、PMT的发送条件

    if (++ts->pat_packet_count == ts->pat_packet_period ||
            (dts != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE) ||
            (dts != AV_NOPTS_VALUE && dts - ts->last_pat_ts >= ts->pat_period*90000.0) ||
            force_pat)
    

    dts != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE:第一次发送PAT、PMT用到的是这个条件。

    ++ts->pat_packet_count == ts->pat_packet_period:这个条件是定时100ms发送PAT、PMT;ts->pat_packet_period 这个字段在函数mpegts_init里面有定义。

    ts->last_pat_ts >= ts->pat_period*90000.0: 这个条件用户自定义发表间隔时会生效,ts->pat_period 这个字段默认值是INT_MAX。

    ts->pat_period:这个字段可以通过如下接口进行用户自定义设置:

    av_opt_set(ofmt_ctx->priv_data, "pat_period",   "0.01",      0);
    

    :ffmpeg 巧妙的地方时,你用户自定义后,定时100ms发送的条件就会失效。

    force_pat: 强制发送PAT、PMT,这个参数是函数入参,故需要查看函数retransmit_si_info被谁调用。

    搜索代码可知,只有mpegts_write_pes调用此函数。

    static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
                                 const uint8_t *payload, int payload_size,
                                 int64_t pts, int64_t dts, int key, int stream_id)
    {
        MpegTSWriteStream *ts_st = st->priv_data;
        MpegTSWrite *ts = s->priv_data;
        uint8_t buf[TS_PACKET_SIZE];
        uint8_t *q;
        int val, is_start, len, header_len, write_pcr, is_dvb_subtitle, is_dvb_teletext, flags;
        int afc_len, stuffing_len;
        int64_t pcr = -1; /* avoid warning */
        int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE);
        int force_pat = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key;
    
        av_assert0(ts_st->payload != buf || st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO);
        if (ts->flags & MPEGTS_FLAG_PAT_PMT_AT_FRAMES && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            force_pat = 1;
        }
    
        is_start = 1;
        while (payload_size > 0) {
            retransmit_si_info(s, force_pat, dts);
            force_pat = 0;
    
            write_pcr = 0;
    

    分析源码可知,force_pat遇到视频关键帧时,会被赋值为1。

    由此分析可知,当定时发送完PAT、PMT后,25ms内来了一帧关键帧,也会发送PAT、PMT,导致出现PAT、PMT间隔小于25ms这个错误。

    修改源码解决问题

    修改函数retransmit_si_info:将发送PAT、PMT条件中的force_pat去掉即可

    /* send SDT, PAT and PMT tables regularly */
    static void retransmit_si_info(AVFormatContext *s, int force_pat, int64_t dts)
    {
        MpegTSWrite *ts = s->priv_data;
        int i;
    
        if (++ts->sdt_packet_count == ts->sdt_packet_period ||
            (dts != AV_NOPTS_VALUE && ts->last_sdt_ts == AV_NOPTS_VALUE) ||
            (dts != AV_NOPTS_VALUE && dts - ts->last_sdt_ts >= ts->sdt_period*90000.0)
        ) {
            ts->sdt_packet_count = 0;
            if (dts != AV_NOPTS_VALUE)
                ts->last_sdt_ts = FFMAX(dts, ts->last_sdt_ts);
            mpegts_write_sdt(s);
        }
    
    	#if 0
        if (++ts->pat_packet_count == ts->pat_packet_period ||
            (dts != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE) ||
            (dts != AV_NOPTS_VALUE && dts - ts->last_pat_ts >= ts->pat_period*90000.0) ||
            force_pat)
    	#endif
    	if (++ts->pat_packet_count == ts->pat_packet_period ||
            (dts != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE) ||
            (dts != AV_NOPTS_VALUE && dts - ts->last_pat_ts >= ts->pat_period*90000.0))
    		{
            ts->pat_packet_count = 0;
            if (dts != AV_NOPTS_VALUE)
                ts->last_pat_ts = FFMAX(dts, ts->last_pat_ts);
            mpegts_write_pat(s);
            for (i = 0; i < ts->nb_services; i++)
                mpegts_write_pmt(s, ts->services[i]);
        }
    }
    
  • 相关阅读:
    MPLS 知识要点1
    ISIS的SSN和SRM标志
    对比ISIS和OSPF
    ISIS帧中继实验
    ISIS 认证实验
    ISIS数据库同步
    ISIS Lab 路由泄露
    ISIS Lab 重分布直连
    32、端口有效范围是多少到多少?
    33、为何需要把 TCP/IP 协议栈分成 5 层(或7层)?开放式回答。
  • 原文地址:https://www.cnblogs.com/standardzero/p/10687943.html
Copyright © 2020-2023  润新知