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

Predrag Filipovic agoracsinc at gmail.com
Thu Mar 24 20:14:56 CET 2016


PCR insertion with specified period is functional when muxrate
is also specified (contant bitrate) as required by DVB and ATSC
(and other) specifications. Feature was non-functional for
variable bitrate (muxrate not specified). Insertion timing
is based on video frame keys and frame period (dts) thus, in
this patch (work in progress), pcr period precision is limited
to +/- one video frame period

Signed-off-by: Predrag Filipovic <predrag at agora-cs.com>
---
 libavformat/mpegtsenc.c | 80
+++++++++++++++++++++++++++++++++++++------------
 1 file changed, 61 insertions(+), 19 deletions(-)

diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
index 7656720..8600ba6 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;
+        }
         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;
     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
-                ts_st->service->pcr_packet_count++;
+            if (ts->mux_rate > 1 || is_start)
+                if (ts_st->service->pcr_packet_period != INT_MAX)
ts_st->service->pcr_packet_count++;
             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;
             }
         }

+        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;
         }

+        /* 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) )
+        {
+            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) ) {
+                    ts->last_pcr_ts = pcr / 300;
+                    ts_st->service->pcr_packet_count = 0; /* keep track of
last_pcr_ts */
+                }
+            }
+            // set Random Access for key frames
             set_af_flag(buf, 0x40);
             q = get_ts_payload_start(buf);
         }
         if (write_pcr) {
             set_af_flag(buf, 0x10);
             q = get_ts_payload_start(buf);
-            // add 11, pcr references the last byte of program clock
reference base
-            if (ts->mux_rate > 1)
-                pcr = get_pcr(ts, s->pb);
-            else
-                pcr = (dts - delay) * 300;
             if (dts != AV_NOPTS_VALUE && dts < pcr / 300)
                 av_log(s, AV_LOG_WARNING, "dts < pcr, TS is invalid\n");
             extend_af(buf, write_pcr_bits(q, pcr));
@@ -1833,7 +1875,7 @@ static const AVOption options[] = {
       { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
     { "pcr_period", "PCR retransmission time",
       offsetof(MpegTSWrite, pcr_period), AV_OPT_TYPE_INT,
-      { .i64 = PCR_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM
},
+      { .i64 = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },
     { "pat_period", "PAT/PMT retransmission time limit in seconds",
       offsetof(MpegTSWrite, pat_period), AV_OPT_TYPE_DOUBLE,
       { .dbl = INT_MAX }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },


More information about the ffmpeg-devel mailing list