[FFmpeg-devel] [PATCH] mpegts: pcr period option for variable bitrate multiplexing

Michael Niedermayer michael at niedermayer.cc
Sun Mar 27 14:09:48 CEST 2016


On Fri, Mar 25, 2016 at 12:50:29PM -0400, Predrag Filipovic wrote:
> Enable proper PCR insertion for VBR multiplexing (muxrate not specified).
> Insertion timing is based on video frame keys and frame period, consequently
> pcr period precision is limited to +/- one video frame period.
> 
> Signed-off-by: Predrag Filipovic <agoracsinc at gmail.com>
> ---
>  libavformat/mpegtsenc.c | 80 +++++++++++++++++++++++++++++++++++++------------
>  1 file changed, 61 insertions(+), 19 deletions(-)
> 
> diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
> index 7656720..7ed9076 100644
> --- a/libavformat/mpegtsenc.c
> +++ b/libavformat/mpegtsenc.c
> @@ -105,6 +105,7 @@ typedef struct MpegTSWrite {
>      int tables_version;
>      double pat_period;
>      double sdt_period;
> +    int64_t last_pcr_ts;
>      int64_t last_pat_ts;
>      int64_t last_sdt_ts;
>  
> @@ -903,6 +904,9 @@ static int mpegts_init(AVFormatContext *s)
>          ts_st = pcr_st->priv_data;
>  
>      if (ts->mux_rate > 1) {
> +        if (ts->pcr_period >= INT_MAX/2) {
> +            ts->pcr_period = PCR_RETRANS_TIME;
> +        }

Currently pcr_period defaults are handled differently from
pat_period and sdt_period
after this patch its still different, why ?

ts->sdt_packet_period and ts->pat_packet_period are initiaized to
defaults, and disabled later in case of user provided parameters

for pcr_period the user provided value is overridden (this is a bit
ugly IMHO) and pcr_packet_period set to the user provided value if any
and later only conditionally overriden in the VBR case

why do you not change pcr_packet_period / pcr_period to work like
pat_period & sdt_period ?


>          service->pcr_packet_period = (int64_t)ts->mux_rate * ts->pcr_period /
>                                       (TS_PACKET_SIZE * 8 * 1000);
>          ts->sdt_packet_period      = (int64_t)ts->mux_rate * SDT_RETRANS_TIME /
> @@ -931,10 +935,19 @@ static int mpegts_init(AVFormatContext *s)
>              service->pcr_packet_period =
>                  ts_st->user_tb.den / (10 * ts_st->user_tb.num);
>          }
> -        if (!service->pcr_packet_period)
> +        /* if pcr_period specified, mark pcr_packet_period as NA (=INT_MAX) */
> +        if (ts->pcr_period < INT_MAX/2) {
> +            service->pcr_packet_period = INT_MAX;
> +        } else {
> +        if (!service->pcr_packet_period) {
>              service->pcr_packet_period = 1;
> +        } else if (service->pcr_packet_period == INT_MAX) {
> +            service->pcr_packet_period--;
> +        }
> +        }
>      }
>  

> +    ts->last_pcr_ts = AV_NOPTS_VALUE;

this looks wrong, i suspect this should be a loop over all services
and service->last_pcr_ts = AV_NOPTS_VALUE;

its ok to change this in a seperate patch of corse if thats cleaner


>      ts->last_pat_ts = AV_NOPTS_VALUE;
>      ts->last_sdt_ts = AV_NOPTS_VALUE;
>      // The user specified a period, use only it
> @@ -1032,10 +1045,9 @@ static void mpegts_insert_null_packet(AVFormatContext *s)
>      avio_write(s->pb, buf, TS_PACKET_SIZE);
>  }
>  
> -/* Write a single transport stream packet with a PCR and no payload */
> -static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st)
> +/* Write a single transport stream packet with a PCR (value in arg) and no payload */
> +static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st, int64_t pcr)
>  {
> -    MpegTSWrite *ts = s->priv_data;
>      MpegTSWriteStream *ts_st = st->priv_data;
>      uint8_t *q;
>      uint8_t buf[TS_PACKET_SIZE];
> @@ -1050,7 +1062,7 @@ static void mpegts_insert_pcr_only(AVFormatContext *s, AVStream *st)
>      *q++ = 0x10;               /* Adaptation flags: PCR present */
>  
>      /* PCR coded into 6 bytes */
> -    q += write_pcr_bits(q, get_pcr(ts, s->pb));
> +    q += write_pcr_bits(q, pcr);
>  
>      /* stuffing bytes */
>      memset(q, 0xFF, TS_PACKET_SIZE - (q - buf));
> @@ -1109,6 +1121,9 @@ static uint8_t *get_ts_payload_start(uint8_t *pkt)
>   * number of TS packets. The final TS packet is padded using an oversized
>   * adaptation header to exactly fill the last TS packet.
>   * NOTE: 'payload' contains a complete PES payload. */
> +/* PCR insertion for VBR TS is based on video frames time and key frames
> + * which leaves non-video TS with PCR insertion at key frames only.
> + * NOTE: PCR period "precision" for VBR TS is +/- one video frame period. */
>  static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
>                               const uint8_t *payload, int payload_size,
>                               int64_t pts, int64_t dts, int key)

> @@ -1135,26 +1150,53 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
>  
>          write_pcr = 0;
>          if (ts_st->pid == ts_st->service->pcr_pid) {
> -            if (ts->mux_rate > 1 || is_start) // VBR pcr period is based on frames
> +            if (ts->mux_rate > 1 || is_start)

> -                ts_st->service->pcr_packet_count++;
> +                if (ts_st->service->pcr_packet_period != INT_MAX) ts_st->service->pcr_packet_count++;

looks unneeded


>              if (ts_st->service->pcr_packet_count >=
> -                ts_st->service->pcr_packet_period) {
> +                ts_st->service->pcr_packet_period) { /* case is NA for VBR TS with specified pcr period*/
>                  ts_st->service->pcr_packet_count = 0;
> -                write_pcr = 1;
> +                if (ts_st->service->pcr_packet_period != INT_MAX) write_pcr = 1;
>              }

looks unneeded as well

these cases wont trigger
nor would it matter if they do trigger, like one PCR more
every 2000 gb

they complicate the logic thugh as INT_MAX becomes a special case


>          }
>  
> +        if (ts->mux_rate > 1) {
> +            pcr = get_pcr(ts, s->pb);
> +        } else {
> +            pcr = (dts - delay) * 300;
> +        }
> +        if (pcr < 0) {
> +            av_log(s, AV_LOG_WARNING, "calculated pcr < 0, TS is invalid\n");
> +            pcr = 0;
> +        }
> +
>          if (ts->mux_rate > 1 && dts != AV_NOPTS_VALUE &&
> -            (dts - get_pcr(ts, s->pb) / 300) > delay) {
> +            (dts - pcr / 300) > delay) {
>              /* pcr insert gets priority over null packet insert */
>              if (write_pcr)
> -                mpegts_insert_pcr_only(s, st);
> +                mpegts_insert_pcr_only(s, st, pcr);
>              else
>                  mpegts_insert_null_packet(s);
>              /* recalculate write_pcr and possibly retransmit si_info */
>              continue;
>          }

can changes to the pcr value be split into a seperate patch from
changes to the periods when pcrs are inserted ?


>
> +        /* Insert PCR for VBR TS with specified pcr_period based on video frame time */
> +        if ( (ts->mux_rate <= 1) && (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
> +            && (ts_st->service->pcr_packet_period == INT_MAX) )

why is this VBR specific ?

if pcr_period is setup like sdt_period/pat_period and the VBR check
is removed, wouldnt that "just work" and be consistent with sdt/pat ?


> +        {
> +            if ( (dts != AV_NOPTS_VALUE && ts->last_pcr_ts == AV_NOPTS_VALUE) ||
> +                 (dts != AV_NOPTS_VALUE && (dts - delay - ts->last_pcr_ts) >= ts->pcr_period*90) )
> +            {
> +                ts->last_pcr_ts = pcr / 300;
> +                ts_st->service->pcr_packet_count = 0;
> +                if (ts_st->pid != ts_st->service->pcr_pid) {
> +                    mpegts_insert_pcr_only(s, st, pcr);
> +                    continue;
> +                }
> +                write_pcr = 1;
> +            }
> +        }
> +
>          /* prepare packet header */
>          q    = buf;
>          *q++ = 0x47;
> @@ -1166,20 +1208,20 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
>          ts_st->cc = ts_st->cc + 1 & 0xf;
>          *q++      = 0x10 | ts_st->cc; // payload indicator + CC
>          if (key && is_start && pts != AV_NOPTS_VALUE) {
> -            // set Random Access for key frames
> -            if (ts_st->pid == ts_st->service->pcr_pid)
> +            if (ts_st->pid == ts_st->service->pcr_pid) {
>                  write_pcr = 1;

> +                if ( (ts->mux_rate <= 1) && (ts_st->service->pcr_packet_period == INT_MAX) ) {

why would this just be done for VBR ?

of course if you like to do a patch that does all changes just for VBR
and then seperately enable it all for CBR in a 2nd patch thats perfectly
ok but i think the code shouldnt be full of VBR special cases

[...]
-- 
Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

The bravest are surely those who have the clearest vision
of what is before them, glory and danger alike, and yet
notwithstanding go out to meet it. -- Thucydides
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: Digital signature
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20160327/5de4b77c/attachment.sig>


More information about the ffmpeg-devel mailing list