diff mbox

[FFmpeg-devel,PATCHv2,1/4] avformat/mpegtsenc: fix incorrect PCR selection with multiple programs

Message ID 20190803081936.14688-1-cus@passwd.hu
State New
Headers show

Commit Message

Marton Balint Aug. 3, 2019, 8:19 a.m. UTC
The MPEG-TS muxer had a serious bug related to the use of multiple programs:
in that case, the PCR pid selection was incomplete for all services except one.
This patch solves this problem and selects a stream to become PCR for each
service, preferably the video stream.

This patch also moves pcr calculation attributes to MpegTSWriteStream from
MpegTSService. PCR is a per-stream and not per-service thing, so it was
misleading to refer to it as something that is per-service.

Also remove *service from MpegTSWriteStream because a stream can belong to
multiple services so it was misleading to select one for each stream.

You can check the result with this example command:

./ffmpeg -loglevel verbose -y -f lavfi -i \
  "testsrc=s=64x64:d=10,split=2[out0][tmp1];[tmp1]vflip[out1];sine=d=10,asetnsamples=1152[out2]" \
  -flags +bitexact -fflags +bitexact -sws_flags +accurate_rnd+bitexact  \
  -codec:v libx264 -codec:a mp2 -pix_fmt yuv420p \
  -map '0:v:0' \
  -map '0:v:1' \
  -map '0:a:0'  \
  -program st=0:st=2 -program st=1:st=2 -program st=2 -program st=0 -f mpegts out.ts

You should now see this:

[mpegts @ 0x37505c0] service 1 using PCR in pid=256
[mpegts @ 0x37505c0] service 2 using PCR in pid=257
[mpegts @ 0x37505c0] service 3 using PCR in pid=258
[mpegts @ 0x37505c0] service 4 using PCR in pid=256

Fixes ticket #8039.

v2: a video is stream is preferred if there are no programs, just like before
the patch.

Signed-off-by: Marton Balint <cus@passwd.hu>
---
 libavformat/mpegtsenc.c | 142 +++++++++++++++++++++++++++---------------------
 1 file changed, 79 insertions(+), 63 deletions(-)

Comments

Andriy Gelman Aug. 3, 2019, 6:32 p.m. UTC | #1
On Sat, 03. Aug 10:19, Marton Balint wrote:
> The MPEG-TS muxer had a serious bug related to the use of multiple programs:
> in that case, the PCR pid selection was incomplete for all services except one.
> This patch solves this problem and selects a stream to become PCR for each
> service, preferably the video stream.
> 
> This patch also moves pcr calculation attributes to MpegTSWriteStream from
> MpegTSService. PCR is a per-stream and not per-service thing, so it was
> misleading to refer to it as something that is per-service.
> 
> Also remove *service from MpegTSWriteStream because a stream can belong to
> multiple services so it was misleading to select one for each stream.
> 
> You can check the result with this example command:
> 
> ./ffmpeg -loglevel verbose -y -f lavfi -i \
>   "testsrc=s=64x64:d=10,split=2[out0][tmp1];[tmp1]vflip[out1];sine=d=10,asetnsamples=1152[out2]" \
>   -flags +bitexact -fflags +bitexact -sws_flags +accurate_rnd+bitexact  \
>   -codec:v libx264 -codec:a mp2 -pix_fmt yuv420p \
>   -map '0:v:0' \
>   -map '0:v:1' \
>   -map '0:a:0'  \
>   -program st=0:st=2 -program st=1:st=2 -program st=2 -program st=0 -f mpegts out.ts
> 
> You should now see this:
> 
> [mpegts @ 0x37505c0] service 1 using PCR in pid=256
> [mpegts @ 0x37505c0] service 2 using PCR in pid=257
> [mpegts @ 0x37505c0] service 3 using PCR in pid=258
> [mpegts @ 0x37505c0] service 4 using PCR in pid=256
> 
> Fixes ticket #8039.
> 
> v2: a video is stream is preferred if there are no programs, just like before
> the patch.

Thanks. 
I looked at the other patches and they seem fine. 

Andriy
Andreas Håkon Aug. 4, 2019, 3:26 p.m. UTC | #2
Hi Marton,


‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐

> v2: a video is stream is preferred if there are no programs, just like before
> the patch.

Thank you for your effort!
However, something is wrong with this patch regarding PCR intervals.

First, try this example (using the file shared by Michael
https://trac.ffmpeg.org/raw-attachment/ticket/3714/FFmpeg%20Sample_cut.ts ):


% ffmpeg -loglevel info -y -i Sample_cut.ts \
 -map '0:v:0' -c:v:0 copy \
 -map '0:v:0' -c:v:1 copy \
 -map '0:a:0' -c:a:0 copy \
 -pat_period 1 \
 -program st=0,1,2 \
 -program st=0,2 \
 -program st=1,2 \
 -program st=3 \
 -sdt_period 1 \
 this_was_less_than_2560000-marton.ts

Then you get:
[mpegts @ 0x3046880] service 1 using PCR in pid=256
[mpegts @ 0x3046880] service 2 using PCR in pid=256
[mpegts @ 0x3046880] service 3 using PCR in pid=257
[mpegts @ 0x3046880] service 4 using PCR in pid=258

Until here all seems correct.

However, if you analyze the PCR intervals:
- 256: PCR_count: 0x3 (3) => repetition rate: 0,299 seconds
- 257: PCR_count: 0x3 (3) => repetition rate: 0,305 seconds
- 258: PCR_count: 0x4 (4) => repetition rate: 0,139 seconds

(You can check it with the DVB Inspector tool or any other)

And regular repetition rate are 0,450 seconds (with ffmpeg).

So...

> @@ -57,8 +57,6 @@ typedef struct MpegTSService {
> uint8_t name[256];
> uint8_t provider_name[256];
> int pcr_pid;
>
> -   int pcr_packet_count;
> -   int pcr_packet_period;
>     AVProgram *program;
>     } MpegTSService;

Why you remove the "pcr_packet_period" from services?
I know that the value can be the same for all services inside the
same TS, but the PCR interval is per service, and not per TS.


> @@ -1015,8 +1032,7 @@ static int mpegts_init(AVFormatContext *s)
> else
> av_log(s, AV_LOG_VERBOSE, "muxrate %d, ", ts->mux_rate);
>      av_log(s, AV_LOG_VERBOSE,
> -             "pcr every %d pkts, sdt every %d, pat/pmt every %d pkts\\n",
> -             service->pcr_packet_period,
> +             "sdt every %d, pat/pmt every %d pkts\\n",
>               ts->sdt_packet_period, ts->pat_packet_period);

The information about the PCR interval is a must have. Please, don't remove it!


> @@ -1198,12 +1214,12 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
> force_pat = 0;
> write_pcr = 0;
> -          if (ts_st->pid == ts_st->service->pcr_pid) {
> +          if (ts_st->pcr_packet_period) {
>                if (ts->mux_rate > 1 || is_start) // VBR pcr period is based on frames
> -                  ts_st->service->pcr_packet_count++;
> -              if (ts_st->service->pcr_packet_count >=
> -                  ts_st->service->pcr_packet_period) {
> -                  ts_st->service->pcr_packet_count = 0;
> +                  ts_st->pcr_packet_count++;
> +              if (ts_st->pcr_packet_count >=
> +                  ts_st->pcr_packet_period) {
> +                  ts_st->pcr_packet_count = 0;
>                }
>            }

In this way, with multiple services (the reason for this patch) you're generating a
Transport Stream with PCR streams sharing the interval period. That's a new serious BUG!

For each service it's one PCR. And the interval of each PCR for one stream is
computed from the last PCR of the same stream. It can't be calculated from the last
PCR of any other stream.

Please, fix this bug before merging this patch.


> @@ -1236,7 +1252,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->pcr_packet_period)
>                    write_pcr = 1;
>                set_af_flag(buf, 0x40);
>                q = get_ts_payload_start(buf);

Just the same problem.


Regards.
A.H.

---
Marton Balint Aug. 4, 2019, 8:25 p.m. UTC | #3
On Sun, 4 Aug 2019, Andreas Håkon wrote:

> Hi Marton,
>
>
> ‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
>
>> v2: a video is stream is preferred if there are no programs, just like before
>> the patch.
>
> Thank you for your effort!
> However, something is wrong with this patch regarding PCR intervals.
>
> First, try this example (using the file shared by Michael
> https://trac.ffmpeg.org/raw-attachment/ticket/3714/FFmpeg%20Sample_cut.ts ):
>
>
> % ffmpeg -loglevel info -y -i Sample_cut.ts \
> -map '0:v:0' -c:v:0 copy \
> -map '0:v:0' -c:v:1 copy \
> -map '0:a:0' -c:a:0 copy \
> -pat_period 1 \
> -program st=0,1,2 \
> -program st=0,2 \
> -program st=1,2 \
> -program st=3 \
> -sdt_period 1 \
> this_was_less_than_2560000-marton.ts

This command line does not seem correct, there is no stream 3, also in 
order to specify multiple streams per program you have to use this syntax: 
-program st=0:st=2.

>
> Then you get:
> [mpegts @ 0x3046880] service 1 using PCR in pid=256
> [mpegts @ 0x3046880] service 2 using PCR in pid=256
> [mpegts @ 0x3046880] service 3 using PCR in pid=257
> [mpegts @ 0x3046880] service 4 using PCR in pid=258
>
> Until here all seems correct.
>
> However, if you analyze the PCR intervals:
> - 256: PCR_count: 0x3 (3) => repetition rate: 0,299 seconds
> - 257: PCR_count: 0x3 (3) => repetition rate: 0,305 seconds
> - 258: PCR_count: 0x4 (4) => repetition rate: 0,139 seconds
>
> (You can check it with the DVB Inspector tool or any other)
>
> And regular repetition rate are 0,450 seconds (with ffmpeg).

450m PCR seems wrong. ISO/IEC13818-1 specifies 
an interval of 100 ms, while DVB recommends 40 ms PCR-s.

What was the PCR interval before the patch? 450 ms seems like the 
keyframe interval of your video, because for keyframes you always get 
a PCR with the current code.

Keep in mind that according to this
https://ffmpeg.org/pipermail/ffmpeg-devel/2016-March/192040.html
PCR in VBR streams never worked correctly.

CBR with multiple programs was also problematic, because instead of 
counting all packets the counter only counted the packets of a service.

The way I see it PCR generation needs a serious rework, I will post a 
patch for that, you will have to apply it on top of this series.

>
> So...
>
>> @@ -57,8 +57,6 @@ typedef struct MpegTSService {
>> uint8_t name[256];
>> uint8_t provider_name[256];
>> int pcr_pid;
>>
>> -   int pcr_packet_count;
>> -   int pcr_packet_period;
>>     AVProgram *program;
>>     } MpegTSService;
>
> Why you remove the "pcr_packet_period" from services?
> I know that the value can be the same for all services inside the
> same TS, but the PCR interval is per service, and not per TS.

It is per PID now instead of per service.

>
>
>> @@ -1015,8 +1032,7 @@ static int mpegts_init(AVFormatContext *s)
>> else
>> av_log(s, AV_LOG_VERBOSE, "muxrate %d, ", ts->mux_rate);
>>      av_log(s, AV_LOG_VERBOSE,
>> -             "pcr every %d pkts, sdt every %d, pat/pmt every %d pkts\\n",
>> -             service->pcr_packet_period,
>> +             "sdt every %d, pat/pmt every %d pkts\\n",
>>               ts->sdt_packet_period, ts->pat_packet_period);
>
> The information about the PCR interval is a must have. Please, don't remove it!

It is written out in select_pcr_streams.

>
>
>> @@ -1198,12 +1214,12 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
>> force_pat = 0;
>> write_pcr = 0;
>> -          if (ts_st->pid == ts_st->service->pcr_pid) {
>> +          if (ts_st->pcr_packet_period) {
>>                if (ts->mux_rate > 1 || is_start) // VBR pcr period is based on frames
>> -                  ts_st->service->pcr_packet_count++;
>> -              if (ts_st->service->pcr_packet_count >=
>> -                  ts_st->service->pcr_packet_period) {
>> -                  ts_st->service->pcr_packet_count = 0;
>> +                  ts_st->pcr_packet_count++;
>> +              if (ts_st->pcr_packet_count >=
>> +                  ts_st->pcr_packet_period) {
>> +                  ts_st->pcr_packet_count = 0;
>>                }
>>            }
>
> In this way, with multiple services (the reason for this patch) you're generating a
> Transport Stream with PCR streams sharing the interval period. That's a new serious BUG!

No, ts_st is different for each stream (each PID).

>
> For each service it's one PCR. And the interval of each PCR for one stream is
> computed from the last PCR of the same stream. It can't be calculated from the last
> PCR of any other stream.

ts_st is different for each stream, it is not common.

>
> Please, fix this bug before merging this patch.
>
>
>> @@ -1236,7 +1252,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->pcr_packet_period)
>>                    write_pcr = 1;
>>                set_af_flag(buf, 0x40);
>>                q = get_ts_payload_start(buf);
>
> Just the same problem.

Regards,
Marton
Marton Balint Aug. 4, 2019, 9:54 p.m. UTC | #4
On Sun, 4 Aug 2019, Marton Balint wrote:

>>> @@ -1015,8 +1032,7 @@ static int mpegts_init(AVFormatContext *s)
>>> else
>>> av_log(s, AV_LOG_VERBOSE, "muxrate %d, ", ts->mux_rate);
>>>      av_log(s, AV_LOG_VERBOSE,
>>> -             "pcr every %d pkts, sdt every %d, pat/pmt every %d pkts\\n",
>>> -             service->pcr_packet_period,
>>> +             "sdt every %d, pat/pmt every %d pkts\\n",
>>>               ts->sdt_packet_period, ts->pat_packet_period);
>>
>> The information about the PCR interval is a must have. Please, don't remove 
> it!
>
> It is written out in select_pcr_streams.

Sorry, I had some version locally which did this, but not the current 
version. The PCR rework replaces this anyway with a fixed time based 
approach, so it does not really matter.

Regards,
Marton
Andreas Håkon Aug. 5, 2019, 8:14 a.m. UTC | #5
Hi Marton,


‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
On Sunday, 4 de August de 2019 22:25, Marton Balint <cus@passwd.hu> wrote:

> On Sun, 4 Aug 2019, Andreas Håkon wrote:
>
> > Hi Marton,
> > ‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
> >
> > > v2: a video is stream is preferred if there are no programs, just like before
> > > the patch.
> >
> > Thank you for your effort!
> > However, something is wrong with this patch regarding PCR intervals.
> > First, try this example (using the file shared by Michael
> > https://trac.ffmpeg.org/raw-attachment/ticket/3714/FFmpeg Sample_cut.ts ):
> > % ffmpeg -loglevel info -y -i Sample_cut.ts \
> > -map '0:v:0' -c:v:0 copy \
> > -map '0:v:0' -c:v:1 copy \
> > -map '0:a:0' -c:a:0 copy \
> > -pat_period 1 \
> > -program st=0,1,2 \
> > -program st=0,2 \
> > -program st=1,2 \
> > -program st=3 \
> > -sdt_period 1 \
> > this_was_less_than_2560000-marton.ts
>
> This command line does not seem correct, there is no stream 3, also in
> order to specify multiple streams per program you have to use this syntax:
> -program st=0:st=2.

Sorry, a lot of typos... corrected example:

[...]
 -program st=0:st=1:st=2 \
 -program st=0:st=2 \
 -program st=1:st=2 \
 -program st=2 \
[...]

The idea with this example is quite simple: Share identical streams over
multiple services to check the correctness of the process.

> > Then you get:
> > [mpegts @ 0x3046880] service 1 using PCR in pid=256
> > [mpegts @ 0x3046880] service 2 using PCR in pid=256
> > [mpegts @ 0x3046880] service 3 using PCR in pid=257
> > [mpegts @ 0x3046880] service 4 using PCR in pid=258
> > Until here all seems correct.

But this is right anyway!

> > However, if you analyze the PCR intervals:
> >
> > -   256: PCR_count: 0x3 (3) => repetition rate: 0,299 seconds
> > -   257: PCR_count: 0x3 (3) => repetition rate: 0,305 seconds
> > -   258: PCR_count: 0x4 (4) => repetition rate: 0,139 seconds
> >
> > (You can check it with the DVB Inspector tool or any other)
> > And regular repetition rate are 0,450 seconds (with ffmpeg).
>
> 450m PCR seems wrong. ISO/IEC13818-1 specifies
> an interval of 100 ms, while DVB recommends 40 ms PCR-s.

Sorry, sorry! Another typo: 0,045 seconds is the regular value
from ffmpeg (without patches).

> What was the PCR interval before the patch? 450 ms seems like the
> keyframe interval of your video, because for keyframes you always get
> a PCR with the current code.

0,045 seconds

> Keep in mind that according to this
> https://ffmpeg.org/pipermail/ffmpeg-devel/2016-March/192040.html
> PCR in VBR streams never worked correctly.

OK.

> CBR with multiple programs was also problematic, because instead of
> counting all packets the counter only counted the packets of a service.

And this is wrong. For MPTS-CBR the PCR interval needs to be calculated
individually for each PCR pid and count all packets.
So, I agree with you.

> The way I see it PCR generation needs a serious rework, I will post a
> patch for that, you will have to apply it on top of this series.

OK. I'll try https://patchwork.ffmpeg.org/patch/14233/


> > Why you remove the "pcr_packet_period" from services?
> > I know that the value can be the same for all services inside the
> > same TS, but the PCR interval is per service, and not per TS.
>
> It is per PID now instead of per service.

OK.

[...]

> > In this way, with multiple services (the reason for this patch) you're generating a
> > Transport Stream with PCR streams sharing the interval period. That's a new serious BUG!
>
> No, ts_st is different for each stream (each PID).
>
> > For each service it's one PCR. And the interval of each PCR for one stream is
> > computed from the last PCR of the same stream. It can't be calculated from the last
> > PCR of any other stream.
>
> ts_st is different for each stream, it is not common.

That's right. That's right. I assumed that the wrong intervals are for this. Excuse me!


Regards.
A.H.

---
diff mbox

Patch

diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
index fc0ea225c6..3ca7599097 100644
--- a/libavformat/mpegtsenc.c
+++ b/libavformat/mpegtsenc.c
@@ -57,8 +57,6 @@  typedef struct MpegTSService {
     uint8_t name[256];
     uint8_t provider_name[256];
     int pcr_pid;
-    int pcr_packet_count;
-    int pcr_packet_period;
     AVProgram *program;
 } MpegTSService;
 
@@ -228,7 +226,6 @@  static int mpegts_write_section1(MpegTSSection *s, int tid, int id,
 #define PCR_RETRANS_TIME 20
 
 typedef struct MpegTSWriteStream {
-    struct MpegTSService *service;
     int pid; /* stream associated pid */
     int cc;
     int discontinuity;
@@ -242,6 +239,9 @@  typedef struct MpegTSWriteStream {
     AVFormatContext *amux;
     AVRational user_tb;
 
+    int pcr_packet_count;
+    int pcr_packet_period;
+
     /* For Opus */
     int opus_queued_samples;
     int opus_pending_trim_start;
@@ -769,12 +769,73 @@  static void section_write_packet(MpegTSSection *s, const uint8_t *packet)
     avio_write(ctx->pb, packet, TS_PACKET_SIZE);
 }
 
+static void enable_pcr_generation_for_stream(AVFormatContext *s, AVStream *pcr_st)
+{
+    MpegTSWrite *ts = s->priv_data;
+    MpegTSWriteStream *ts_st = pcr_st->priv_data;
+
+    if (ts->mux_rate > 1) {
+        ts_st->pcr_packet_period   = (int64_t)ts->mux_rate * ts->pcr_period /
+                                     (TS_PACKET_SIZE * 8 * 1000);
+    } else {
+        if (pcr_st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+            int frame_size = av_get_audio_frame_duration2(pcr_st->codecpar, 0);
+            if (!frame_size) {
+                av_log(s, AV_LOG_WARNING, "frame size not set\n");
+                ts_st->pcr_packet_period =
+                    pcr_st->codecpar->sample_rate / (10 * 512);
+            } else {
+                ts_st->pcr_packet_period =
+                    pcr_st->codecpar->sample_rate / (10 * frame_size);
+            }
+        } else {
+            // max delta PCR 0.1s
+            // TODO: should be avg_frame_rate
+            ts_st->pcr_packet_period =
+                ts_st->user_tb.den / (10 * ts_st->user_tb.num);
+        }
+        if (!ts_st->pcr_packet_period)
+            ts_st->pcr_packet_period = 1;
+    }
+
+    // output a PCR as soon as possible
+    ts_st->pcr_packet_count = ts_st->pcr_packet_period;
+}
+
+static void select_pcr_streams(AVFormatContext *s)
+{
+    MpegTSWrite *ts = s->priv_data;
+
+    for (int i = 0; i < ts->nb_services; i++) {
+        MpegTSService *service = ts->services[i];
+        AVStream *pcr_st = NULL;
+        AVProgram *program = service->program;
+        int nb_streams = program ? program->nb_stream_indexes : s->nb_streams;
+
+        for (int j = 0; j < nb_streams; j++) {
+            AVStream *st = s->streams[program ? program->stream_index[j] : j];
+            if (!pcr_st ||
+                pcr_st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
+            {
+                pcr_st = st;
+            }
+        }
+
+        if (pcr_st) {
+            MpegTSWriteStream *ts_st = pcr_st->priv_data;
+            service->pcr_pid = ts_st->pid;
+            enable_pcr_generation_for_stream(s, pcr_st);
+            av_log(s, AV_LOG_VERBOSE, "service %i using PCR in pid=%i\n", service->sid, service->pcr_pid);
+        }
+    }
+}
+
 static int mpegts_init(AVFormatContext *s)
 {
     MpegTSWrite *ts = s->priv_data;
     MpegTSWriteStream *ts_st;
     MpegTSService *service;
-    AVStream *st, *pcr_st = NULL;
+    AVStream *st;
     AVDictionaryEntry *title, *provider;
     int i, j;
     const char *service_name;
@@ -853,7 +914,6 @@  static int mpegts_init(AVFormatContext *s)
 
     /* assign pids to each stream */
     for (i = 0; i < s->nb_streams; i++) {
-        AVProgram *program;
         st = s->streams[i];
 
         ts_st = av_mallocz(sizeof(MpegTSWriteStream));
@@ -872,17 +932,6 @@  static int mpegts_init(AVFormatContext *s)
             goto fail;
         }
 
-        program = av_find_program_from_stream(s, NULL, i);
-        if (program) {
-            for (j = 0; j < ts->nb_services; j++) {
-                if (ts->services[j]->program == program) {
-                    service = ts->services[j];
-                    break;
-                }
-            }
-        }
-
-        ts_st->service = service;
         /* MPEG pid values < 16 are reserved. Applications which set st->id in
          * this range are assigned a calculated pid. */
         if (st->id < 16) {
@@ -895,10 +944,12 @@  static int mpegts_init(AVFormatContext *s)
             ret = AVERROR(EINVAL);
             goto fail;
         }
-        if (ts_st->pid == service->pmt.pid) {
-            av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", ts_st->pid);
-            ret = AVERROR(EINVAL);
-            goto fail;
+        for (j = 0; j < ts->nb_services; j++) {
+            if (ts_st->pid == ts->services[j]->pmt.pid) {
+                av_log(s, AV_LOG_ERROR, "Duplicate stream id %d\n", ts_st->pid);
+                ret = AVERROR(EINVAL);
+                goto fail;
+            }
         }
         for (j = 0; j < i; j++) {
             if (pids[j] == ts_st->pid) {
@@ -913,12 +964,6 @@  static int mpegts_init(AVFormatContext *s)
         ts_st->first_pts_check = 1;
         ts_st->cc              = 15;
         ts_st->discontinuity   = ts->flags & MPEGTS_FLAG_DISCONT;
-        /* update PCR pid by using the first video stream */
-        if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
-            service->pcr_pid == 0x1fff) {
-            service->pcr_pid = ts_st->pid;
-            pcr_st           = st;
-        }
         if (st->codecpar->codec_id == AV_CODEC_ID_AAC &&
             st->codecpar->extradata_size > 0) {
             AVStream *ast;
@@ -953,17 +998,9 @@  static int mpegts_init(AVFormatContext *s)
 
     av_freep(&pids);
 
-    /* if no video stream, use the first stream as PCR */
-    if (service->pcr_pid == 0x1fff && s->nb_streams > 0) {
-        pcr_st           = s->streams[0];
-        ts_st            = pcr_st->priv_data;
-        service->pcr_pid = ts_st->pid;
-    } else
-        ts_st = pcr_st->priv_data;
+    select_pcr_streams(s);
 
     if (ts->mux_rate > 1) {
-        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 /
                                      (TS_PACKET_SIZE * 8 * 1000);
         ts->pat_packet_period      = (int64_t)ts->mux_rate * PAT_RETRANS_TIME /
@@ -975,24 +1012,6 @@  static int mpegts_init(AVFormatContext *s)
         /* Arbitrary values, PAT/PMT will also be written on video key frames */
         ts->sdt_packet_period = 200;
         ts->pat_packet_period = 40;
-        if (pcr_st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
-            int frame_size = av_get_audio_frame_duration2(pcr_st->codecpar, 0);
-            if (!frame_size) {
-                av_log(s, AV_LOG_WARNING, "frame size not set\n");
-                service->pcr_packet_period =
-                    pcr_st->codecpar->sample_rate / (10 * 512);
-            } else {
-                service->pcr_packet_period =
-                    pcr_st->codecpar->sample_rate / (10 * frame_size);
-            }
-        } else {
-            // max delta PCR 0.1s
-            // TODO: should be avg_frame_rate
-            service->pcr_packet_period =
-                ts_st->user_tb.den / (10 * ts_st->user_tb.num);
-        }
-        if (!service->pcr_packet_period)
-            service->pcr_packet_period = 1;
     }
 
     ts->last_pat_ts = AV_NOPTS_VALUE;
@@ -1005,8 +1024,6 @@  static int mpegts_init(AVFormatContext *s)
         ts->sdt_packet_period = INT_MAX;
     }
 
-    // output a PCR as soon as possible
-    service->pcr_packet_count = service->pcr_packet_period;
     ts->pat_packet_count      = ts->pat_packet_period - 1;
     ts->sdt_packet_count      = ts->sdt_packet_period - 1;
 
@@ -1015,8 +1032,7 @@  static int mpegts_init(AVFormatContext *s)
     else
         av_log(s, AV_LOG_VERBOSE, "muxrate %d, ", ts->mux_rate);
     av_log(s, AV_LOG_VERBOSE,
-           "pcr every %d pkts, sdt every %d, pat/pmt every %d pkts\n",
-           service->pcr_packet_period,
+           "sdt every %d, pat/pmt every %d pkts\n",
            ts->sdt_packet_period, ts->pat_packet_period);
 
     if (ts->m2ts_mode == -1) {
@@ -1198,12 +1214,12 @@  static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
         force_pat = 0;
 
         write_pcr = 0;
-        if (ts_st->pid == ts_st->service->pcr_pid) {
+        if (ts_st->pcr_packet_period) {
             if (ts->mux_rate > 1 || is_start) // VBR pcr period is based on frames
-                ts_st->service->pcr_packet_count++;
-            if (ts_st->service->pcr_packet_count >=
-                ts_st->service->pcr_packet_period) {
-                ts_st->service->pcr_packet_count = 0;
+                ts_st->pcr_packet_count++;
+            if (ts_st->pcr_packet_count >=
+                ts_st->pcr_packet_period) {
+                ts_st->pcr_packet_count = 0;
                 write_pcr = 1;
             }
         }
@@ -1236,7 +1252,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->pcr_packet_period)
                 write_pcr = 1;
             set_af_flag(buf, 0x40);
             q = get_ts_payload_start(buf);