[FFmpeg-devel] libavformat/mpegtsenc: fix incorrect PCR with multiple programs [v3]

Marton Balint cus at passwd.hu
Fri Aug 2 11:09:08 EEST 2019



On Fri, 2 Aug 2019, Andreas HÃ¥kon wrote:

> Hi Marton,
>
>
>> > > > +
>> > > > +          /* program service checks */
>> > > > +          program = av_find_program_from_stream(s, NULL, i);
>> > > > +          do {
>> > > > +              for (j = 0; j < ts->nb_services; j++) {
>> > > > +                  if (program) {
>> > > > +                      /* search for the services corresponding to this program */
>> > > > +                      if (ts->services[j]->program == program)
>> > > > +                          service = ts->services[j];
>> > > > +                      else
>> > > > +                          continue;
>> > > > +                  }
>> > > > +
>> > > > +                  ts_st->service = service;
>> > > > +
>> > > > +                  /* check for PMT pid conflicts */
>> > > > +                  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;
>> > > > +                  }
>> > > > +
>> > > > +                  /* 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;
>> > > > +                      service->pcr_st  = st;
>> > > > +                  }
>> > > > +
>> > > > +                  /* store this stream as a candidate PCR if the service doesn't have any */
>> > > > +                  if (service->pcr_pid == 0x1fff &&
>> > > > +                      !service->pcr_st) {
>> > > > +                      service->pcr_st  = st;
>> > > > +                  }
>> > > > +
>> > > > +                  /* exit when just one single service */
>> > > > +                  if (!program)
>> > > > +                      break;
>> > > > +              }
>> > > > +              program = program ? av_find_program_from_stream(s, program, i) : NULL;
>> > > > +          } while (program);
>> > > >
>> > > >
>> > >
>> > > Maybe I miss something but as far as I see only this block needs to be in
>> > > the loop, the rest is not. So you can actually write this:
>> >
>> > False! All checks require to be completed inside the loop.
>> > Please, note that one stream (aka pid) can be assigned to multiple programs.
>> > So we need to iterate over all programs and services.
>>
>> My problem is that the body of the inner loop, after the if (program)
>> condition is executed exactly once. Therefore it does not belong to a
>> loop.
>>
>
> Why you say that the condition (after the "if (program)") is executed exactly once?
> That statement is false! The inner **loop** (yes it's a loop) is executed:
>
> A) Just one time, only if no program is defined (that's the reason of the last
>   "if (!program) break)" plus the "program = program ? ... : NULL" and the
>   "while(program)" ).

Agreed.

> B) Just one time, if only one service is found for this stream.
> C) Multiple times, if the stream is inside multiple services.

I agree that if the outer (program) loop fires more than once if there are 
multiple programs, I meant that for each outer loop iteration the body of 
the inner loop is only executed once.

>
> So, because of (B) and (C) it's required to execute a loop. In fact, we iterate
> over all programs, except in one edge case: when no programs are defined. And only
> in this case the inner loop is executed once deterministically.
>
>
>> > > /* program service checks */
>> > > program = av_find_program_from_stream(s, NULL, i);
>> > > do {
>> > > 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;
>> > > /* check for PMT pid conflicts */
>> > > ...
>> > > This way you also ensure that ts_st->service is always set for every
>> > > stream which is kind of how the old code worked, right?
>> >
>> > Not really. The "service" can be assigned on two ways: or using the iteration
>> > over all programs, or in the creation of a new service with the call
>> > to the "mpegts_add_service()" function when no programs are defined.
>>
>> To be frank, the whole ts_st->service is bogus, because a stream can
>> belong to multiple services, so on what grounds do we select one?
>
> To be frank too, I'm not responsible for the previous code.
> I think the original author created the code with the assumption that there was
> only one service. And after that, the support for multiple services was added.
> And now I have discovered this error, and I have fixed it with minimal modifications.
> Where's the problem, honestly?

It is natural that existing code needs to be cleaned up before adding a 
new feature.

>
>> I'd suggest to remove service from MpegTSWriteStream and move
>> pcr_packet_count and pcr_packet_period from MpegTSService to
>> MpegTSWriteStream. Of course this should be yet another, separate patch, a
>> prerequisite to this one.
>
> Sorry, too much work for me!

I will post a patch which does this, please test it and rebase your work 
upon it.

Thanks,
Marton


More information about the ffmpeg-devel mailing list