diff mbox

[FFmpeg-devel] libavformat: add multiple PCR streams in MPEGTS muxer [v2]

Message ID nv9oSLiZL5ysahQfKZVJ0OG2ZEhZ8LnHhFw4hkK7tMnaRo8HWrH0HHrWMMUx5ODo1ujnAnJv91k1YNgd7KiGO52klKqJj_6mjELXorRyqVI=@protonmail.com
State New
Headers show

Commit Message

msanders Nov. 20, 2018, 3:57 p.m. UTC
This new patch adds three new options to the MPEG-TS muxer.
You can use them to mark PCRs in additional VIDEO/AUDIO/DATA streams.
It's useful when you will remux the output with pid filtering tools.
For example, if you put PCR in all audio streams, then you can filter
each audio stream to create a radio service.
PCR marks do not disturb outgoing stream, but it is not recommended
to do so as a regular rule. Anyway the resulting stream is conformant with specs.

Supersedes: [FFmpeg-devel] libavformat: add multiple PCR streams in MPEGTS muxer

M. Sanders.
From 198f1cfd65d2735fd86c5fc32c7b2a9f41cff79d Mon Sep 17 00:00:00 2001
From: M. Sanders <hidden at gmail.com>
Date: Tue, 20 Nov 2018 15:40:57 +0000
Subject: [PATCH] libavformat: add multiple PCR streams in MPEGTS muxer [v2]

---
This new patch adds three new options to the MPEG-TS muxer.
You can use them to mark PCRs in additional VIDEO/AUDIO/DATA streams.
It's useful when you will remux the output with pid filtering tools.
For example, if you put PCR in all audio streams, then you can filter 
each audio stream to create a radio service.
PCR marks do not disturb outgoing stream, but it is not recommended 
to do so as a regular rule. Anyway the resulting stream is conformant with specs.

Supersedes: [FFmpeg-devel] libavformat: add multiple PCR streams in MPEGTS muxer

 doc/muxers.texi         |    9 +++++++++
 libavformat/mpegtsenc.c |   32 +++++++++++++++++++++++++++++++-
 2 files changed, 40 insertions(+), 1 deletion(-)

Comments

msanders Nov. 20, 2018, 4:39 p.m. UTC | #1
Hi,

Regarding Andrey Turkin's comments on my first version of the patch,

here are some comments:

- Multiple streams with PCR marks in the same program are **allowed**.

  ISO/IEC 13818-1: "Information technology — Generic coding

                    of moving pictures and associated audio

                    information: Systems"

  Section: Intro.1 Transport Stream, paragraph 2:

     "[...] The Transport Stream rate is defined by the values and

     locations of Program Clock Reference (PCR) fields, which in general

     are separate PCR fields for each program."

  Section: Intro. 8.2 Synchronization, paragraph 3:

     "A program shall have one and only one PCR time base associated with it."

  Conclusion: In the PMT table only one (sub)stream can transport the *master*

              PCR clock. However, any other (sub)streams may be allowed to

              provide **unused** PCR marks.

  Note: This patch doesn't change the output if the new parameters aren't used.

        In addition, when enabled, the master (sub)stream receives PCR marks

        identical to the previous ones.

- Correction of the PCR repetition rate:

  The new version of the patch fixes this problem. Now, each (sub)stream has

  its own individual repetition rate.

  Thanks Andrey for noticing it!

- User case:

  I'm going to do a deeper explanation of how to use this patch.

  Imagine generating a program with 1 video stream and 2 audio streams.

  When you add "-pcr_all_audio 1", the two audio streams will include PCR marks.

  This doesn't change anything and the program can be used as before.

  But now you can also generate additional radio services just adding a PMT for

  each audio stream (this part isn't the purpose of this patch).

  For example, you can do it with an external tool without any remux.

  The only requirement is that each radio service points to the correct pid

  of the audio stream. Futhermore, you can also separate these new radio

  services from the original TS with just a pid filtering, as the PCR marks already

  exists.

I hope the patch is accepted. I'm using it without problems.

Regards,

M. Sanders.

> This looks like a (strange) hack to me.

> First, you cannot just throw some pids out - that will make a non-standard

> compliant stream. PMT will signal missing streams; PCR pid will be missing

> also. So let's assume that your tool can also rewrite PMT.

> Secondly, there is supposed to be one and only one PCR stream per program;

> there is no way to signal multiple PCRs. So let's assume you have some

> out-of-band list of PCRs for this particular case (or maybe you can just

> assume there are PCR marks in each stream so you can configure your remuxer

> accordingly).

> Thirdly and most importantly, I might be wrong here but it looks like your

> patch just "spreads" same PCRs which would go into single stream over all

> program's streams, i.e. overall PCR count doesn't change. Which means that

> remuxed stream might have arbitrarily large inter-PCR gaps which is pretty

> bad (assuming you care about PCRs at all, which presumably you do).

>

> Also - do you really have multiple audio streams in single program, which

> are supposed to be remuxed as separate radio channels? Or same for multiple

> video streams? Is this for ABR purposes? Can you describe your use case in

> some more details?

‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
On Tuesday, 20 de November de 2018 16:57, msanders <marksanders@protonmail.com> wrote:

> This new patch adds three new options to the MPEG-TS muxer.
> You can use them to mark PCRs in additional VIDEO/AUDIO/DATA streams.
> It's useful when you will remux the output with pid filtering tools.
> For example, if you put PCR in all audio streams, then you can filter
> each audio stream to create a radio service.
> PCR marks do not disturb outgoing stream, but it is not recommended
> to do so as a regular rule. Anyway the resulting stream is conformant with specs.
>
> Supersedes: [FFmpeg-devel] libavformat: add multiple PCR streams in MPEGTS muxer
>
> M. Sanders.
diff mbox

Patch

diff --git a/doc/muxers.texi b/doc/muxers.texi
index 62f4091..8f0c5a1 100644
--- a/doc/muxers.texi
+++ b/doc/muxers.texi
@@ -1528,6 +1528,15 @@  is @code{-1}, which results in shifting timestamps so that they start from 0.
 @item omit_video_pes_length @var{boolean}
 Omit the PES packet length for video packets. Default is @code{1} (true).
 
+@item pcr_all_video @var{boolean}
+Include PCR values in all video streams. Default is @code{0} (false).
+
+@item pcr_all_audio @var{boolean}
+Include PCR values in all audio streams. Default is @code{0} (false).
+
+@item pcr_all_data @var{boolean}
+Include PCR values in all data streams. Default is @code{0} (false).
+
 @item pcr_period @var{integer}
 Override the default PCR retransmission time in milliseconds. Ignored if
 variable muxrate is selected. Default is @code{20}.
diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
index 3339e26..fb7f11a 100644
--- a/libavformat/mpegtsenc.c
+++ b/libavformat/mpegtsenc.c
@@ -113,6 +113,9 @@  typedef struct MpegTSWrite {
     double sdt_period;
     int64_t last_pat_ts;
     int64_t last_sdt_ts;
+    int pcr_all_video;
+    int pcr_all_audio;
+    int pcr_all_data;
 
     int omit_video_pes_length;
 } MpegTSWrite;
@@ -237,6 +240,7 @@  typedef struct MpegTSWriteStream {
     int prev_payload_key;
     int64_t payload_pts;
     int64_t payload_dts;
+    int local_pcr_packet_count;
     int payload_flags;
     uint8_t *payload;
     AVFormatContext *amux;
@@ -902,6 +906,7 @@  static int mpegts_init(AVFormatContext *s)
         pids[i]                = ts_st->pid;
         ts_st->payload_pts     = AV_NOPTS_VALUE;
         ts_st->payload_dts     = AV_NOPTS_VALUE;
+        ts_st->local_pcr_packet_count = -1;
         ts_st->first_pts_check = 1;
         ts_st->cc              = 15;
         ts_st->discontinuity   = ts->flags & MPEGTS_FLAG_DISCONT;
@@ -1178,11 +1183,17 @@  static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
     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;
+    int force_pcr = 0;
 
     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;
     }
+    if ((ts->pcr_all_video && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) ||
+        (ts->pcr_all_audio && st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) ||
+        (ts->pcr_all_data && st->codecpar->codec_type  == AVMEDIA_TYPE_DATA)) {
+            force_pcr = 1;
+    }
 
     is_start = 1;
     while (payload_size > 0) {
@@ -1198,6 +1209,16 @@  static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
                 ts_st->service->pcr_packet_count = 0;
                 write_pcr = 1;
             }
+        } else if (force_pcr) { // for streams other than the primary PCR stream
+            if (ts_st->local_pcr_packet_count < 0) // copy from primary
+                ts_st->local_pcr_packet_count = ts_st->service->pcr_packet_count;
+            if (ts->mux_rate > 1 || is_start) // VBR pcr period is based on frames
+                ts_st->local_pcr_packet_count++;
+            if (ts_st->local_pcr_packet_count >=
+                ts_st->service->pcr_packet_period) {
+                ts_st->local_pcr_packet_count = 0;
+                write_pcr = 1;
+                }
         }
 
         if (ts->mux_rate > 1 && dts != AV_NOPTS_VALUE &&
@@ -1228,7 +1249,7 @@  static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
         }
         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 || force_pcr)
                 write_pcr = 1;
             set_af_flag(buf, 0x40);
             q = get_ts_payload_start(buf);
@@ -1951,6 +1972,15 @@  static const AVOption options[] = {
     { "omit_video_pes_length", "Omit the PES packet length for video packets",
       offsetof(MpegTSWrite, omit_video_pes_length), AV_OPT_TYPE_BOOL,
       { .i64 = 1 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
+    { "pcr_all_video", "Include PCR values in all video streams",
+      offsetof(MpegTSWrite, pcr_all_video), AV_OPT_TYPE_BOOL,
+      { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
+    { "pcr_all_audio", "Include PCR values in all audio streams",
+      offsetof(MpegTSWrite, pcr_all_audio), AV_OPT_TYPE_BOOL,
+      { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
+    { "pcr_all_data", "Include PCR values in all data streams",
+      offsetof(MpegTSWrite, pcr_all_data), AV_OPT_TYPE_BOOL,
+      { .i64 = 0 }, 0, 1, AV_OPT_FLAG_ENCODING_PARAM },
     { "pcr_period", "PCR retransmission time in milliseconds",
       offsetof(MpegTSWrite, pcr_period), AV_OPT_TYPE_INT,
       { .i64 = PCR_RETRANS_TIME }, 0, INT_MAX, AV_OPT_FLAG_ENCODING_PARAM },