diff mbox

[FFmpeg-devel] avformat/mpegts: add merge_pmt_versions option

Message ID 20180514225401.40824-1-ffmpeg@tmm1.net
State Superseded
Headers show

Commit Message

Aman Karmani May 14, 2018, 10:54 p.m. UTC
From: Aman Gupta <aman@tmm1.net>

This new optional flag makes it easier to deal with mpegts
samples where the PMT is updated and elementary streams move
to different PIDs in the middle of playback.

Previously, new AVStreams were created per PID, and it was up
to the user to figure out which streams had migrated to a new PID
(by iterating over the list of AVProgram and making guesses), and
switch seamlessly to the new AVStream during playback.

Transcoding or remuxing these streams with ffmpeg on the CLI was
also quite painful, and the user would need to extract each set of
PIDs into a separate file and then stitch them back together.

With this new option, the mpegts demuxer will automatically detect
PMT changes and feed data from the new PID to the original AVStream
that was created for the orignal PID. For mpegts samples with
stream_identifier_descriptor available, the unique ID is used to merge
PIDs together. If the stream id is not available, the demuxer attempts
to map PIDs based on their order and relation to the PCR pid.

With this change, I am able to playback and transcode/remux these
two samples which previously caused issues:

    https://tmm1.s3.amazonaws.com/pmt-version-change.ts
    https://kuroko.fushizen.eu/videos/pid_switch_sample.ts

I also have another longer sample which contains multiple PMT
changes, some of which change the ES pids and others which do not:

    https://tmm1.s3.amazonaws.com/multiple-pmt-change.ts

Demuxing this sample with the new option shows several new log
messages as the PMT changes are handled:

    [mpegts @ 0x7ffe18801200] detected PMT change (version=3/4, pcr_pid=0xf98/0xf9b)
    [mpegts @ 0x7ffe18801200] re-using existing video stream 0 (pid=0xf98) for new pid=0xf9b
    [mpegts @ 0x7ffe18801200] re-using existing audio stream 1 (pid=0xf99) for new pid=0xf9c
    [mpegts @ 0x7ffe18801200] re-using existing audio stream 2 (pid=0xf9a) for new pid=0xf9d
    [mpegts @ 0x7ffe18801200] detected PMT change (version=4/5, pcr_pid=0xf9b/0xfa9)
    [mpegts @ 0x7ffe18801200] re-using existing video stream 0 (pid=0xf98) for new pid=0xfa9
    [mpegts @ 0x7ffe18801200] re-using existing audio stream 1 (pid=0xf99) for new pid=0xfaa
    [mpegts @ 0x7ffe18801200] re-using existing audio stream 2 (pid=0xf9a) for new pid=0xfab

Signed-off-by: Aman Gupta <aman@tmm1.net>
---
 doc/demuxers.texi    |   4 ++
 libavformat/mpegts.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 105 insertions(+), 4 deletions(-)

Comments

Michael Niedermayer May 15, 2018, 12:12 p.m. UTC | #1
On Mon, May 14, 2018 at 03:54:01PM -0700, Aman Gupta wrote:
> From: Aman Gupta <aman@tmm1.net>
> 
> This new optional flag makes it easier to deal with mpegts
> samples where the PMT is updated and elementary streams move
> to different PIDs in the middle of playback.
> 
> Previously, new AVStreams were created per PID, and it was up
> to the user to figure out which streams had migrated to a new PID
> (by iterating over the list of AVProgram and making guesses), and
> switch seamlessly to the new AVStream during playback.
> 
> Transcoding or remuxing these streams with ffmpeg on the CLI was
> also quite painful, and the user would need to extract each set of
> PIDs into a separate file and then stitch them back together.
> 
> With this new option, the mpegts demuxer will automatically detect
> PMT changes and feed data from the new PID to the original AVStream
> that was created for the orignal PID. For mpegts samples with
> stream_identifier_descriptor available, the unique ID is used to merge
> PIDs together. If the stream id is not available, the demuxer attempts
> to map PIDs based on their order and relation to the PCR pid.
> 
> With this change, I am able to playback and transcode/remux these
> two samples which previously caused issues:
> 
>     https://tmm1.s3.amazonaws.com/pmt-version-change.ts
>     https://kuroko.fushizen.eu/videos/pid_switch_sample.ts
> 
> I also have another longer sample which contains multiple PMT
> changes, some of which change the ES pids and others which do not:
> 
>     https://tmm1.s3.amazonaws.com/multiple-pmt-change.ts
> 
> Demuxing this sample with the new option shows several new log
> messages as the PMT changes are handled:
> 
>     [mpegts @ 0x7ffe18801200] detected PMT change (version=3/4, pcr_pid=0xf98/0xf9b)
>     [mpegts @ 0x7ffe18801200] re-using existing video stream 0 (pid=0xf98) for new pid=0xf9b
>     [mpegts @ 0x7ffe18801200] re-using existing audio stream 1 (pid=0xf99) for new pid=0xf9c
>     [mpegts @ 0x7ffe18801200] re-using existing audio stream 2 (pid=0xf9a) for new pid=0xf9d
>     [mpegts @ 0x7ffe18801200] detected PMT change (version=4/5, pcr_pid=0xf9b/0xfa9)
>     [mpegts @ 0x7ffe18801200] re-using existing video stream 0 (pid=0xf98) for new pid=0xfa9
>     [mpegts @ 0x7ffe18801200] re-using existing audio stream 1 (pid=0xf99) for new pid=0xfaa
>     [mpegts @ 0x7ffe18801200] re-using existing audio stream 2 (pid=0xf9a) for new pid=0xfab

This sounds like an interresting feature
this should also get a fate test


> 
> Signed-off-by: Aman Gupta <aman@tmm1.net>
> ---
>  doc/demuxers.texi    |   4 ++
>  libavformat/mpegts.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++--
>  2 files changed, 105 insertions(+), 4 deletions(-)
> 
> diff --git a/doc/demuxers.texi b/doc/demuxers.texi
> index e7c2abce57..2f7d7e0f3a 100644
> --- a/doc/demuxers.texi
> +++ b/doc/demuxers.texi
> @@ -552,6 +552,10 @@ Show the detected raw packet size, cannot be set by the user.
>  Scan and combine all PMTs. The value is an integer with value from -1
>  to 1 (-1 means automatic setting, 1 means enabled, 0 means
>  disabled). Default value is -1.
> +
> +@item merge_pmt_versions
> +Re-use existing streams when a PMT's version is updated and elementary
> +streams move to different PIDs. Default value is 0.
>  @end table
>  
>  @section mpjpeg
> diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
> index 27b1c30a44..c42e564a7e 100644
> --- a/libavformat/mpegts.c
> +++ b/libavformat/mpegts.c
> @@ -84,6 +84,8 @@ typedef struct MpegTSSectionFilter {
>      unsigned int end_of_section_reached : 1;
>      SectionCallback *section_cb;
>      void *opaque;
> +    int orig_pcr_pid; /* pmt specific */
> +    int last_pcr_pid;
>  } MpegTSSectionFilter;
>  
>  struct MpegTSFilter {
> @@ -147,6 +149,7 @@ struct MpegTSContext {
>      int scan_all_pmts;
>  
>      int resync_size;
> +    int merge_pmt_versions;
>  
>      /******************************************/
>      /* private mpegts data */
> @@ -172,6 +175,8 @@ static const AVOption options[] = {
>       {.i64 = 0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY },
>      {"scan_all_pmts", "scan and combine all PMTs", offsetof(MpegTSContext, scan_all_pmts), AV_OPT_TYPE_BOOL,
>       {.i64 = -1}, -1, 1, AV_OPT_FLAG_DECODING_PARAM },
> +    {"merge_pmt_versions", "re-use streams when PMT's version/pids change", offsetof(MpegTSContext, merge_pmt_versions), AV_OPT_TYPE_BOOL,
> +     {.i64 = 0}, 0, 1,  AV_OPT_FLAG_DECODING_PARAM },
>      {"skip_changes", "skip changing / adding streams / programs", offsetof(MpegTSContext, skip_changes), AV_OPT_TYPE_BOOL,
>       {.i64 = 0}, 0, 1, 0 },
>      {"skip_clear", "skip clearing programs", offsetof(MpegTSContext, skip_clear), AV_OPT_TYPE_BOOL,
> @@ -1073,7 +1078,8 @@ static int mpegts_push_data(MpegTSFilter *filter,
>                          if (ts->skip_changes)
>                              goto skip;
>  
> -                        pes->st = avformat_new_stream(ts->stream, NULL);
> +                        if (!pes->st)
> +                            pes->st = avformat_new_stream(ts->stream, NULL);
>                          if (!pes->st)
>                              return AVERROR(ENOMEM);
>                          pes->st->id = pes->pid;
> @@ -1982,6 +1988,68 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
>      *pp = desc_end;
>      return 0;
>  }
> +static AVStream *find_matching_stream(MpegTSContext *ts, int pid,
> +                                      int stream_id,
> +                                      int pcr_pid, int orig_pcr_pid)
> +{
> +    AVFormatContext *s = ts->stream;
> +    int i, orig_pid = orig_pcr_pid + (pid - pcr_pid);
> +    AVStream *found = NULL;
> +
> +    for (i = 0; i < s->nb_streams; i++) {
> +        AVStream *st = s->streams[i];
> +        if (stream_id != -1) {
> +            if (st->stream_identifier == stream_id+1) {
> +                found = st;
> +                break;
> +            }
> +        } else if (pcr_pid != orig_pcr_pid && st->id == orig_pid) {
> +            found = st;
> +            break;
> +        }
> +    }
> +
> +    if (found) {
> +        av_log(ts->stream, AV_LOG_DEBUG, "re-using existing %s stream %d (pid=0x%x) for new pid=0x%x\n",
> +               av_get_media_type_string(found->codecpar->codec_type), i, found->id, pid);
> +    }
> +
> +    return found;
> +}
> +

> +static int parse_stream_identifier_desc(uint8_t *p, uint8_t *p_end)
> +{
> +    const uint8_t **pp = &p;

libavformat/mpegts.c:2022:26: warning: initialization from incompatible pointer type [enabled by default]


> +    const uint8_t *desc_list_end;
> +    const uint8_t *desc_end;
> +    int desc_list_len;
> +    int desc_len, desc_tag;
> +

> +    desc_list_len = get16(&p, p_end);

type mismatch:
libavformat/mpegts.c:2028:5: warning: passing argument 1 of ‘get16’ from incompatible pointer type [enabled by default]


[...]
Jan Ekström May 15, 2018, 3:29 p.m. UTC | #2
On Tue, May 15, 2018, 15:12 Michael Niedermayer <michael@niedermayer.cc>
wrote:

> On Mon, May 14, 2018 at 03:54:01PM -0700, Aman Gupta wrote:
> > From: Aman Gupta <aman@tmm1.net>
> >
> > This new optional flag makes it easier to deal with mpegts
> > samples where the PMT is updated and elementary streams move
> > to different PIDs in the middle of playback.
> >
> > Previously, new AVStreams were created per PID, and it was up
> > to the user to figure out which streams had migrated to a new PID
> > (by iterating over the list of AVProgram and making guesses), and
> > switch seamlessly to the new AVStream during playback.
> >
> > Transcoding or remuxing these streams with ffmpeg on the CLI was
> > also quite painful, and the user would need to extract each set of
> > PIDs into a separate file and then stitch them back together.
> >
> > With this new option, the mpegts demuxer will automatically detect
> > PMT changes and feed data from the new PID to the original AVStream
> > that was created for the orignal PID. For mpegts samples with
> > stream_identifier_descriptor available, the unique ID is used to merge
> > PIDs together. If the stream id is not available, the demuxer attempts
> > to map PIDs based on their order and relation to the PCR pid.
> >
> > With this change, I am able to playback and transcode/remux these
> > two samples which previously caused issues:
> >
> >     https://tmm1.s3.amazonaws.com/pmt-version-change.ts
> >     https://kuroko.fushizen.eu/videos/pid_switch_sample.ts
> >
> > I also have another longer sample which contains multiple PMT
> > changes, some of which change the ES pids and others which do not:
> >
> >     https://tmm1.s3.amazonaws.com/multiple-pmt-change.ts
> >
> > Demuxing this sample with the new option shows several new log
> > messages as the PMT changes are handled:
> >
> >     [mpegts @ 0x7ffe18801200] detected PMT change (version=3/4,
> pcr_pid=0xf98/0xf9b)
> >     [mpegts @ 0x7ffe18801200] re-using existing video stream 0
> (pid=0xf98) for new pid=0xf9b
> >     [mpegts @ 0x7ffe18801200] re-using existing audio stream 1
> (pid=0xf99) for new pid=0xf9c
> >     [mpegts @ 0x7ffe18801200] re-using existing audio stream 2
> (pid=0xf9a) for new pid=0xf9d
> >     [mpegts @ 0x7ffe18801200] detected PMT change (version=4/5,
> pcr_pid=0xf9b/0xfa9)
> >     [mpegts @ 0x7ffe18801200] re-using existing video stream 0
> (pid=0xf98) for new pid=0xfa9
> >     [mpegts @ 0x7ffe18801200] re-using existing audio stream 1
> (pid=0xf99) for new pid=0xfaa
> >     [mpegts @ 0x7ffe18801200] re-using existing audio stream 2
> (pid=0xf9a) for new pid=0xfab
>
> This sounds like an interresting feature
> this should also get a fate test
>

It is not the "real" solution, as we still need a flag for PMT updates in
AVProgram, or a callback for updated programs - so that API clients can
handle all this properly dynamically.

But, I do find this an interesting way of handling some cases of PID
switch. And under an AVOption it will not break any expectations by default.


>
> >
> > Signed-off-by: Aman Gupta <aman@tmm1.net>
> > ---
> >  doc/demuxers.texi    |   4 ++
> >  libavformat/mpegts.c | 105
> +++++++++++++++++++++++++++++++++++++++++++++++++--
> >  2 files changed, 105 insertions(+), 4 deletions(-)
> >
> > diff --git a/doc/demuxers.texi b/doc/demuxers.texi
> > index e7c2abce57..2f7d7e0f3a 100644
> > --- a/doc/demuxers.texi
> > +++ b/doc/demuxers.texi
> > @@ -552,6 +552,10 @@ Show the detected raw packet size, cannot be set by
> the user.
> >  Scan and combine all PMTs. The value is an integer with value from -1
> >  to 1 (-1 means automatic setting, 1 means enabled, 0 means
> >  disabled). Default value is -1.
> > +
> > +@item merge_pmt_versions
> > +Re-use existing streams when a PMT's version is updated and elementary
> > +streams move to different PIDs. Default value is 0.
> >  @end table
> >
> >  @section mpjpeg
> > diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
> > index 27b1c30a44..c42e564a7e 100644
> > --- a/libavformat/mpegts.c
> > +++ b/libavformat/mpegts.c
> > @@ -84,6 +84,8 @@ typedef struct MpegTSSectionFilter {
> >      unsigned int end_of_section_reached : 1;
> >      SectionCallback *section_cb;
> >      void *opaque;
> > +    int orig_pcr_pid; /* pmt specific */
> > +    int last_pcr_pid;
> >  } MpegTSSectionFilter;
> >
> >  struct MpegTSFilter {
> > @@ -147,6 +149,7 @@ struct MpegTSContext {
> >      int scan_all_pmts;
> >
> >      int resync_size;
> > +    int merge_pmt_versions;
> >
> >      /******************************************/
> >      /* private mpegts data */
> > @@ -172,6 +175,8 @@ static const AVOption options[] = {
> >       {.i64 = 0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_EXPORT
> | AV_OPT_FLAG_READONLY },
> >      {"scan_all_pmts", "scan and combine all PMTs",
> offsetof(MpegTSContext, scan_all_pmts), AV_OPT_TYPE_BOOL,
> >       {.i64 = -1}, -1, 1, AV_OPT_FLAG_DECODING_PARAM },
> > +    {"merge_pmt_versions", "re-use streams when PMT's version/pids
> change", offsetof(MpegTSContext, merge_pmt_versions), AV_OPT_TYPE_BOOL,
> > +     {.i64 = 0}, 0, 1,  AV_OPT_FLAG_DECODING_PARAM },
> >      {"skip_changes", "skip changing / adding streams / programs",
> offsetof(MpegTSContext, skip_changes), AV_OPT_TYPE_BOOL,
> >       {.i64 = 0}, 0, 1, 0 },
> >      {"skip_clear", "skip clearing programs", offsetof(MpegTSContext,
> skip_clear), AV_OPT_TYPE_BOOL,
> > @@ -1073,7 +1078,8 @@ static int mpegts_push_data(MpegTSFilter *filter,
> >                          if (ts->skip_changes)
> >                              goto skip;
> >
> > -                        pes->st = avformat_new_stream(ts->stream, NULL);
> > +                        if (!pes->st)
> > +                            pes->st = avformat_new_stream(ts->stream,
> NULL);
> >                          if (!pes->st)
> >                              return AVERROR(ENOMEM);
> >                          pes->st->id = pes->pid;
> > @@ -1982,6 +1988,68 @@ int ff_parse_mpeg2_descriptor(AVFormatContext
> *fc, AVStream *st, int stream_type
> >      *pp = desc_end;
> >      return 0;
> >  }
> > +static AVStream *find_matching_stream(MpegTSContext *ts, int pid,
> > +                                      int stream_id,
> > +                                      int pcr_pid, int orig_pcr_pid)
> > +{
> > +    AVFormatContext *s = ts->stream;
> > +    int i, orig_pid = orig_pcr_pid + (pid - pcr_pid);
> > +    AVStream *found = NULL;
> > +
> > +    for (i = 0; i < s->nb_streams; i++) {
> > +        AVStream *st = s->streams[i];
> > +        if (stream_id != -1) {
> > +            if (st->stream_identifier == stream_id+1) {
> > +                found = st;
> > +                break;
> > +            }
> > +        } else if (pcr_pid != orig_pcr_pid && st->id == orig_pid) {
> > +            found = st;
> > +            break;
> > +        }
> > +    }
> > +
> > +    if (found) {
> > +        av_log(ts->stream, AV_LOG_DEBUG, "re-using existing %s stream
> %d (pid=0x%x) for new pid=0x%x\n",
> > +               av_get_media_type_string(found->codecpar->codec_type),
> i, found->id, pid);
> > +    }
> > +
> > +    return found;
> > +}
> > +
>
> > +static int parse_stream_identifier_desc(uint8_t *p, uint8_t *p_end)
> > +{
> > +    const uint8_t **pp = &p;
>
> libavformat/mpegts.c:2022:26: warning: initialization from incompatible
> pointer type [enabled by default]
>

Basically, the pointer parameters should be "const". That shuts the
warnings at least on clang.



>
> > +    const uint8_t *desc_list_end;
> > +    const uint8_t *desc_end;
> > +    int desc_list_len;
> > +    int desc_len, desc_tag;
> > +
>
> > +    desc_list_len = get16(&p, p_end);
>
> type mismatch:
> libavformat/mpegts.c:2028:5: warning: passing argument 1 of ‘get16’ from
> incompatible pointer type [enabled by default]
>
>
> [...]
> --
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> Opposition brings concord. Out of discord comes the fairest harmony.
> -- Heraclitus
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
Aman Karmani May 15, 2018, 7:23 p.m. UTC | #3
On Tue, May 15, 2018 at 5:12 AM, Michael Niedermayer <michael@niedermayer.cc
> wrote:

> On Mon, May 14, 2018 at 03:54:01PM -0700, Aman Gupta wrote:
> > From: Aman Gupta <aman@tmm1.net>
> >
> > This new optional flag makes it easier to deal with mpegts
> > samples where the PMT is updated and elementary streams move
> > to different PIDs in the middle of playback.
> >
> > Previously, new AVStreams were created per PID, and it was up
> > to the user to figure out which streams had migrated to a new PID
> > (by iterating over the list of AVProgram and making guesses), and
> > switch seamlessly to the new AVStream during playback.
> >
> > Transcoding or remuxing these streams with ffmpeg on the CLI was
> > also quite painful, and the user would need to extract each set of
> > PIDs into a separate file and then stitch them back together.
> >
> > With this new option, the mpegts demuxer will automatically detect
> > PMT changes and feed data from the new PID to the original AVStream
> > that was created for the orignal PID. For mpegts samples with
> > stream_identifier_descriptor available, the unique ID is used to merge
> > PIDs together. If the stream id is not available, the demuxer attempts
> > to map PIDs based on their order and relation to the PCR pid.
> >
> > With this change, I am able to playback and transcode/remux these
> > two samples which previously caused issues:
> >
> >     https://tmm1.s3.amazonaws.com/pmt-version-change.ts
> >     https://kuroko.fushizen.eu/videos/pid_switch_sample.ts
> >
> > I also have another longer sample which contains multiple PMT
> > changes, some of which change the ES pids and others which do not:
> >
> >     https://tmm1.s3.amazonaws.com/multiple-pmt-change.ts
> >
> > Demuxing this sample with the new option shows several new log
> > messages as the PMT changes are handled:
> >
> >     [mpegts @ 0x7ffe18801200] detected PMT change (version=3/4,
> pcr_pid=0xf98/0xf9b)
> >     [mpegts @ 0x7ffe18801200] re-using existing video stream 0
> (pid=0xf98) for new pid=0xf9b
> >     [mpegts @ 0x7ffe18801200] re-using existing audio stream 1
> (pid=0xf99) for new pid=0xf9c
> >     [mpegts @ 0x7ffe18801200] re-using existing audio stream 2
> (pid=0xf9a) for new pid=0xf9d
> >     [mpegts @ 0x7ffe18801200] detected PMT change (version=4/5,
> pcr_pid=0xf9b/0xfa9)
> >     [mpegts @ 0x7ffe18801200] re-using existing video stream 0
> (pid=0xf98) for new pid=0xfa9
> >     [mpegts @ 0x7ffe18801200] re-using existing audio stream 1
> (pid=0xf99) for new pid=0xfaa
> >     [mpegts @ 0x7ffe18801200] re-using existing audio stream 2
> (pid=0xf9a) for new pid=0xfab
>
> This sounds like an interresting feature
> this should also get a fate test
>

Sure. Can you add a sample to fate-suite:

curl -o fate-suite/mpegts/pmtchange.ts http://0x0.st/secA.ts


>
>
> >
> > Signed-off-by: Aman Gupta <aman@tmm1.net>
> > ---
> >  doc/demuxers.texi    |   4 ++
> >  libavformat/mpegts.c | 105 ++++++++++++++++++++++++++++++
> +++++++++++++++++++--
> >  2 files changed, 105 insertions(+), 4 deletions(-)
> >
> > diff --git a/doc/demuxers.texi b/doc/demuxers.texi
> > index e7c2abce57..2f7d7e0f3a 100644
> > --- a/doc/demuxers.texi
> > +++ b/doc/demuxers.texi
> > @@ -552,6 +552,10 @@ Show the detected raw packet size, cannot be set by
> the user.
> >  Scan and combine all PMTs. The value is an integer with value from -1
> >  to 1 (-1 means automatic setting, 1 means enabled, 0 means
> >  disabled). Default value is -1.
> > +
> > +@item merge_pmt_versions
> > +Re-use existing streams when a PMT's version is updated and elementary
> > +streams move to different PIDs. Default value is 0.
> >  @end table
> >
> >  @section mpjpeg
> > diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
> > index 27b1c30a44..c42e564a7e 100644
> > --- a/libavformat/mpegts.c
> > +++ b/libavformat/mpegts.c
> > @@ -84,6 +84,8 @@ typedef struct MpegTSSectionFilter {
> >      unsigned int end_of_section_reached : 1;
> >      SectionCallback *section_cb;
> >      void *opaque;
> > +    int orig_pcr_pid; /* pmt specific */
> > +    int last_pcr_pid;
> >  } MpegTSSectionFilter;
> >
> >  struct MpegTSFilter {
> > @@ -147,6 +149,7 @@ struct MpegTSContext {
> >      int scan_all_pmts;
> >
> >      int resync_size;
> > +    int merge_pmt_versions;
> >
> >      /******************************************/
> >      /* private mpegts data */
> > @@ -172,6 +175,8 @@ static const AVOption options[] = {
> >       {.i64 = 0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_EXPORT
> | AV_OPT_FLAG_READONLY },
> >      {"scan_all_pmts", "scan and combine all PMTs",
> offsetof(MpegTSContext, scan_all_pmts), AV_OPT_TYPE_BOOL,
> >       {.i64 = -1}, -1, 1, AV_OPT_FLAG_DECODING_PARAM },
> > +    {"merge_pmt_versions", "re-use streams when PMT's version/pids
> change", offsetof(MpegTSContext, merge_pmt_versions), AV_OPT_TYPE_BOOL,
> > +     {.i64 = 0}, 0, 1,  AV_OPT_FLAG_DECODING_PARAM },
> >      {"skip_changes", "skip changing / adding streams / programs",
> offsetof(MpegTSContext, skip_changes), AV_OPT_TYPE_BOOL,
> >       {.i64 = 0}, 0, 1, 0 },
> >      {"skip_clear", "skip clearing programs", offsetof(MpegTSContext,
> skip_clear), AV_OPT_TYPE_BOOL,
> > @@ -1073,7 +1078,8 @@ static int mpegts_push_data(MpegTSFilter *filter,
> >                          if (ts->skip_changes)
> >                              goto skip;
> >
> > -                        pes->st = avformat_new_stream(ts->stream,
> NULL);
> > +                        if (!pes->st)
> > +                            pes->st = avformat_new_stream(ts->stream,
> NULL);
> >                          if (!pes->st)
> >                              return AVERROR(ENOMEM);
> >                          pes->st->id = pes->pid;
> > @@ -1982,6 +1988,68 @@ int ff_parse_mpeg2_descriptor(AVFormatContext
> *fc, AVStream *st, int stream_type
> >      *pp = desc_end;
> >      return 0;
> >  }
> > +static AVStream *find_matching_stream(MpegTSContext *ts, int pid,
> > +                                      int stream_id,
> > +                                      int pcr_pid, int orig_pcr_pid)
> > +{
> > +    AVFormatContext *s = ts->stream;
> > +    int i, orig_pid = orig_pcr_pid + (pid - pcr_pid);
> > +    AVStream *found = NULL;
> > +
> > +    for (i = 0; i < s->nb_streams; i++) {
> > +        AVStream *st = s->streams[i];
> > +        if (stream_id != -1) {
> > +            if (st->stream_identifier == stream_id+1) {
> > +                found = st;
> > +                break;
> > +            }
> > +        } else if (pcr_pid != orig_pcr_pid && st->id == orig_pid) {
> > +            found = st;
> > +            break;
> > +        }
> > +    }
> > +
> > +    if (found) {
> > +        av_log(ts->stream, AV_LOG_DEBUG, "re-using existing %s stream
> %d (pid=0x%x) for new pid=0x%x\n",
> > +               av_get_media_type_string(found->codecpar->codec_type),
> i, found->id, pid);
> > +    }
> > +
> > +    return found;
> > +}
> > +
>
> > +static int parse_stream_identifier_desc(uint8_t *p, uint8_t *p_end)
> > +{
> > +    const uint8_t **pp = &p;
>
> libavformat/mpegts.c:2022:26: warning: initialization from incompatible
> pointer type [enabled by default]
>
>
> > +    const uint8_t *desc_list_end;
> > +    const uint8_t *desc_end;
> > +    int desc_list_len;
> > +    int desc_len, desc_tag;
> > +
>
> > +    desc_list_len = get16(&p, p_end);
>
> type mismatch:
> libavformat/mpegts.c:2028:5: warning: passing argument 1 of ‘get16’ from
> incompatible pointer type [enabled by default]
>

Oops, sloppy copy/paste from ff_parse_mpeg2_descriptor on my part. Will fix
for v2.

Thanks for reviewing,
Aman


>
>
> [...]
> --
> Michael     GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
>
> Opposition brings concord. Out of discord comes the fairest harmony.
> -- Heraclitus
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
>
diff mbox

Patch

diff --git a/doc/demuxers.texi b/doc/demuxers.texi
index e7c2abce57..2f7d7e0f3a 100644
--- a/doc/demuxers.texi
+++ b/doc/demuxers.texi
@@ -552,6 +552,10 @@  Show the detected raw packet size, cannot be set by the user.
 Scan and combine all PMTs. The value is an integer with value from -1
 to 1 (-1 means automatic setting, 1 means enabled, 0 means
 disabled). Default value is -1.
+
+@item merge_pmt_versions
+Re-use existing streams when a PMT's version is updated and elementary
+streams move to different PIDs. Default value is 0.
 @end table
 
 @section mpjpeg
diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c
index 27b1c30a44..c42e564a7e 100644
--- a/libavformat/mpegts.c
+++ b/libavformat/mpegts.c
@@ -84,6 +84,8 @@  typedef struct MpegTSSectionFilter {
     unsigned int end_of_section_reached : 1;
     SectionCallback *section_cb;
     void *opaque;
+    int orig_pcr_pid; /* pmt specific */
+    int last_pcr_pid;
 } MpegTSSectionFilter;
 
 struct MpegTSFilter {
@@ -147,6 +149,7 @@  struct MpegTSContext {
     int scan_all_pmts;
 
     int resync_size;
+    int merge_pmt_versions;
 
     /******************************************/
     /* private mpegts data */
@@ -172,6 +175,8 @@  static const AVOption options[] = {
      {.i64 = 0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY },
     {"scan_all_pmts", "scan and combine all PMTs", offsetof(MpegTSContext, scan_all_pmts), AV_OPT_TYPE_BOOL,
      {.i64 = -1}, -1, 1, AV_OPT_FLAG_DECODING_PARAM },
+    {"merge_pmt_versions", "re-use streams when PMT's version/pids change", offsetof(MpegTSContext, merge_pmt_versions), AV_OPT_TYPE_BOOL,
+     {.i64 = 0}, 0, 1,  AV_OPT_FLAG_DECODING_PARAM },
     {"skip_changes", "skip changing / adding streams / programs", offsetof(MpegTSContext, skip_changes), AV_OPT_TYPE_BOOL,
      {.i64 = 0}, 0, 1, 0 },
     {"skip_clear", "skip clearing programs", offsetof(MpegTSContext, skip_clear), AV_OPT_TYPE_BOOL,
@@ -1073,7 +1078,8 @@  static int mpegts_push_data(MpegTSFilter *filter,
                         if (ts->skip_changes)
                             goto skip;
 
-                        pes->st = avformat_new_stream(ts->stream, NULL);
+                        if (!pes->st)
+                            pes->st = avformat_new_stream(ts->stream, NULL);
                         if (!pes->st)
                             return AVERROR(ENOMEM);
                         pes->st->id = pes->pid;
@@ -1982,6 +1988,68 @@  int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type
     *pp = desc_end;
     return 0;
 }
+static AVStream *find_matching_stream(MpegTSContext *ts, int pid,
+                                      int stream_id,
+                                      int pcr_pid, int orig_pcr_pid)
+{
+    AVFormatContext *s = ts->stream;
+    int i, orig_pid = orig_pcr_pid + (pid - pcr_pid);
+    AVStream *found = NULL;
+
+    for (i = 0; i < s->nb_streams; i++) {
+        AVStream *st = s->streams[i];
+        if (stream_id != -1) {
+            if (st->stream_identifier == stream_id+1) {
+                found = st;
+                break;
+            }
+        } else if (pcr_pid != orig_pcr_pid && st->id == orig_pid) {
+            found = st;
+            break;
+        }
+    }
+
+    if (found) {
+        av_log(ts->stream, AV_LOG_DEBUG, "re-using existing %s stream %d (pid=0x%x) for new pid=0x%x\n",
+               av_get_media_type_string(found->codecpar->codec_type), i, found->id, pid);
+    }
+
+    return found;
+}
+
+static int parse_stream_identifier_desc(uint8_t *p, uint8_t *p_end)
+{
+    const uint8_t **pp = &p;
+    const uint8_t *desc_list_end;
+    const uint8_t *desc_end;
+    int desc_list_len;
+    int desc_len, desc_tag;
+
+    desc_list_len = get16(&p, p_end);
+    if (desc_list_len < 0)
+        return -1;
+    desc_list_len &= 0xfff;
+    desc_list_end  = p + desc_list_len;
+    if (desc_list_end > p_end)
+        return -1;
+
+    while (1) {
+        desc_tag = get8(pp, desc_list_end);
+        if (desc_tag < 0)
+            return -1;
+        desc_len = get8(pp, desc_list_end);
+        if (desc_len < 0)
+            return -1;
+        desc_end = *pp + desc_len;
+        if (desc_end > desc_list_end)
+            return -1;
+
+        if (desc_tag == 0x52) {
+            return get8(pp, desc_end);
+        }
+        *pp = desc_end;
+    }
+}
 
 static int is_pes_stream(int stream_type, uint32_t prog_reg_desc)
 {
@@ -2000,6 +2068,8 @@  static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
     int program_info_length, pcr_pid, pid, stream_type;
     int desc_list_len;
     uint32_t prog_reg_desc = 0; /* registration descriptor */
+    int last_ver = tssf->last_ver;
+    int stream_id = -1;
 
     int mp4_descr_count = 0;
     Mp4Descr mp4_descr[MAX_MP4_DESCR_COUNT] = { { 0 } };
@@ -2032,6 +2102,14 @@  static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
     pcr_pid &= 0x1fff;
     add_pid_to_pmt(ts, h->id, pcr_pid);
     set_pcr_pid(ts->stream, h->id, pcr_pid);
+    if (!tssf->orig_pcr_pid)
+        tssf->orig_pcr_pid = pcr_pid;
+
+    if (last_ver != -1 && h->version != last_ver) {
+        av_log(ts->stream, AV_LOG_DEBUG, "detected PMT change (version=%d/%d, pcr_pid=0x%x/0x%x)\n",
+               last_ver, h->version, tssf->last_pcr_pid, pcr_pid);
+    }
+    tssf->last_pcr_pid = pcr_pid;
 
     av_log(ts->stream, AV_LOG_TRACE, "pcr_pid=0x%x\n", pcr_pid);
 
@@ -2083,14 +2161,22 @@  static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
         if (pid < 0)
             goto out;
         pid &= 0x1fff;
+        stream_id = parse_stream_identifier_desc(p, p_end);
         if (pid == ts->current_pid)
             goto out;
 
         /* now create stream */
         if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) {
             pes = ts->pids[pid]->u.pes_filter.opaque;
+            if (ts->merge_pmt_versions && !pes->st) {
+                st = find_matching_stream(ts, pid, stream_id, pcr_pid, tssf->orig_pcr_pid);
+                if (st) {
+                    pes->st = st;
+                    pes->stream_type = stream_type;
+                }
+            }
             if (!pes->st) {
-                pes->st     = avformat_new_stream(pes->stream, NULL);
+                pes->st = avformat_new_stream(pes->stream, NULL);
                 if (!pes->st)
                     goto out;
                 pes->st->id = pes->pid;
@@ -2100,7 +2186,14 @@  static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
             if (ts->pids[pid])
                 mpegts_close_filter(ts, ts->pids[pid]); // wrongly added sdt filter probably
             pes = add_pes_stream(ts, pid, pcr_pid);
-            if (pes) {
+            if (ts->merge_pmt_versions && pes && !pes->st) {
+                st = find_matching_stream(ts, pid, stream_id, pcr_pid, tssf->orig_pcr_pid);
+                if (st) {
+                    pes->st = st;
+                    pes->stream_type = stream_type;
+                }
+            }
+            if (pes && !pes->st) {
                 st = avformat_new_stream(pes->stream, NULL);
                 if (!st)
                     goto out;
@@ -2110,7 +2203,11 @@  static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len
             int idx = ff_find_stream_index(ts->stream, pid);
             if (idx >= 0) {
                 st = ts->stream->streams[idx];
-            } else {
+            }
+            if (ts->merge_pmt_versions && !st) {
+                st = find_matching_stream(ts, pid, stream_id, pcr_pid, tssf->orig_pcr_pid);
+            }
+            if (!st) {
                 st = avformat_new_stream(ts->stream, NULL);
                 if (!st)
                     goto out;