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

Predrag Filipovic agoracsinc at gmail.com
Fri Mar 25 17:50:29 CET 2016


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



More information about the ffmpeg-devel mailing list