diff mbox

[FFmpeg-devel] avformat: close parser if codec changed

Message ID 37e48e5d-d5bc-e321-ed93-b37759c472e8@googlemail.com
State Accepted
Headers show

Commit Message

Andreas Cadhalpun Oct. 21, 2016, 11:16 p.m. UTC
On 22.10.2016 00:18, Michael Niedermayer wrote:
> On Mon, Oct 17, 2016 at 08:49:23PM +0200, Andreas Cadhalpun wrote:
>> The parser depends on the codec and thus must not be used with a different one.
>> If it is, the 'avctx->codec_id == s->parser->codec_ids[0] ...' assert in
>> av_parser_parse2 gets triggered.
>>
>> Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
>> ---
>>  libavformat/utils.c | 12 ++++++++++++
>>  1 file changed, 12 insertions(+)
> 
> This changes the audio output from
> http://samples.ffmpeg.org/MPEG2/vid_0x80.ts
> 
> is that intended ?

Nice catch: it shouldn't change, of course.

While processing that sample only the codec_tag gets changed, which is no reason to
close the parser. That is only necessary, when the codec_id changes.

Fixed patch is attached.

Best regards,
Andreas

Comments

Andreas Cadhalpun Oct. 26, 2016, 7:59 p.m. UTC | #1
On 22.10.2016 01:16, Andreas Cadhalpun wrote:
> From 9de87a4fb2c6c6311a11a2da5de8554a71adfa66 Mon Sep 17 00:00:00 2001
> From: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
> Date: Mon, 17 Oct 2016 20:26:51 +0200
> Subject: [PATCH] avformat: close parser if codec changed
> 
> The parser depends on the codec and thus must not be used with a different one.
> If it is, the 'avctx->codec_id == s->parser->codec_ids[0] ...' assert in
> av_parser_parse2 gets triggered.
> 
> Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
> ---
>  libavformat/utils.c | 12 ++++++++++++
>  1 file changed, 12 insertions(+)

Ping. (I'd like to have this in 3.2.)

Best regards,
Andreas
Michael Niedermayer Nov. 2, 2016, 12:07 p.m. UTC | #2
On Sat, Oct 22, 2016 at 01:16:00AM +0200, Andreas Cadhalpun wrote:
> On 22.10.2016 00:18, Michael Niedermayer wrote:
> > On Mon, Oct 17, 2016 at 08:49:23PM +0200, Andreas Cadhalpun wrote:
> >> The parser depends on the codec and thus must not be used with a different one.
> >> If it is, the 'avctx->codec_id == s->parser->codec_ids[0] ...' assert in
> >> av_parser_parse2 gets triggered.
> >>
> >> Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
> >> ---
> >>  libavformat/utils.c | 12 ++++++++++++
> >>  1 file changed, 12 insertions(+)
> > 
> > This changes the audio output from
> > http://samples.ffmpeg.org/MPEG2/vid_0x80.ts
> > 
> > is that intended ?
> 
> Nice catch: it shouldn't change, of course.
> 
> While processing that sample only the codec_tag gets changed, which is no reason to
> close the parser. That is only necessary, when the codec_id changes.
> 
> Fixed patch is attached.
> 
> Best regards,
> Andreas
> 

>  utils.c |   12 ++++++++++++
>  1 file changed, 12 insertions(+)
> ffefc22756b774cb7652587207ae66cfbf681be3  0001-avformat-close-parser-if-codec-changed.patch
> From 9de87a4fb2c6c6311a11a2da5de8554a71adfa66 Mon Sep 17 00:00:00 2001
> From: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
> Date: Mon, 17 Oct 2016 20:26:51 +0200
> Subject: [PATCH] avformat: close parser if codec changed
> 
> The parser depends on the codec and thus must not be used with a different one.
> If it is, the 'avctx->codec_id == s->parser->codec_ids[0] ...' assert in
> av_parser_parse2 gets triggered.
> 
> Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
> ---
>  libavformat/utils.c | 12 ++++++++++++
>  1 file changed, 12 insertions(+)
> 
> diff --git a/libavformat/utils.c b/libavformat/utils.c
> index 70dbfa1..a8a78ed 100644
> --- a/libavformat/utils.c
> +++ b/libavformat/utils.c
> @@ -480,6 +480,12 @@ static int update_stream_avctx(AVFormatContext *s)
>          if (!st->internal->need_context_update)
>              continue;
>  
> +        /* close parser, because it depends on the codec */
> +        if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
> +            av_parser_close(st->parser);
> +            st->parser = NULL;
> +        }
> +
>          /* update internal codec context, for the parser */
>          ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
>          if (ret < 0)
> @@ -1515,6 +1521,12 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
>                  st->info->found_decoder = 0;
>              }
>  
> +            /* close parser, because it depends on the codec */
> +            if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
> +                av_parser_close(st->parser);
> +                st->parser = NULL;
> +            }
> +

what if the codec id differs but both are supported by the parser ?
AVCodecParser has a list of 5 codec ids ?

I didnt find a testcase where this makes a difference, just wondering

the patch seems fine otherwise

[...]
Andreas Cadhalpun Nov. 2, 2016, 9:30 p.m. UTC | #3
On 02.11.2016 13:07, Michael Niedermayer wrote:
> On Sat, Oct 22, 2016 at 01:16:00AM +0200, Andreas Cadhalpun wrote:
>>  utils.c |   12 ++++++++++++
>>  1 file changed, 12 insertions(+)
>> ffefc22756b774cb7652587207ae66cfbf681be3  0001-avformat-close-parser-if-codec-changed.patch
>> From 9de87a4fb2c6c6311a11a2da5de8554a71adfa66 Mon Sep 17 00:00:00 2001
>> From: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
>> Date: Mon, 17 Oct 2016 20:26:51 +0200
>> Subject: [PATCH] avformat: close parser if codec changed
>>
>> The parser depends on the codec and thus must not be used with a different one.
>> If it is, the 'avctx->codec_id == s->parser->codec_ids[0] ...' assert in
>> av_parser_parse2 gets triggered.
>>
>> Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
>> ---
>>  libavformat/utils.c | 12 ++++++++++++
>>  1 file changed, 12 insertions(+)
>>
>> diff --git a/libavformat/utils.c b/libavformat/utils.c
>> index 70dbfa1..a8a78ed 100644
>> --- a/libavformat/utils.c
>> +++ b/libavformat/utils.c
>> @@ -480,6 +480,12 @@ static int update_stream_avctx(AVFormatContext *s)
>>          if (!st->internal->need_context_update)
>>              continue;
>>  
>> +        /* close parser, because it depends on the codec */
>> +        if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
>> +            av_parser_close(st->parser);
>> +            st->parser = NULL;
>> +        }
>> +
>>          /* update internal codec context, for the parser */
>>          ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
>>          if (ret < 0)
>> @@ -1515,6 +1521,12 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
>>                  st->info->found_decoder = 0;
>>              }
>>  
>> +            /* close parser, because it depends on the codec */
>> +            if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
>> +                av_parser_close(st->parser);
>> +                st->parser = NULL;
>> +            }
>> +
> 
> what if the codec id differs but both are supported by the parser ?
> AVCodecParser has a list of 5 codec ids ?
> 
> I didnt find a testcase where this makes a difference, just wondering

I think the parser should be re-initialized in that case, too.
For example the ac3 parser stores the codec_id in the parser context with:
    if(hdr.bitstream_id>10)
        hdr_info->codec_id = AV_CODEC_ID_EAC3;
    else if (hdr_info->codec_id == AV_CODEC_ID_NONE)
        hdr_info->codec_id = AV_CODEC_ID_AC3;

So if it is first AV_CODEC_ID_EAC3 and then AV_CODEC_ID_AC3, this wouldn't
be updated and the parser would change the avctx codec_id back, which
seems wrong.

> the patch seems fine otherwise

OK, pushed.

Best regards,
Andreas
Michael Niedermayer Nov. 2, 2016, 11:42 p.m. UTC | #4
On Wed, Nov 02, 2016 at 10:30:30PM +0100, Andreas Cadhalpun wrote:
> On 02.11.2016 13:07, Michael Niedermayer wrote:
> > On Sat, Oct 22, 2016 at 01:16:00AM +0200, Andreas Cadhalpun wrote:
> >>  utils.c |   12 ++++++++++++
> >>  1 file changed, 12 insertions(+)
> >> ffefc22756b774cb7652587207ae66cfbf681be3  0001-avformat-close-parser-if-codec-changed.patch
> >> From 9de87a4fb2c6c6311a11a2da5de8554a71adfa66 Mon Sep 17 00:00:00 2001
> >> From: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
> >> Date: Mon, 17 Oct 2016 20:26:51 +0200
> >> Subject: [PATCH] avformat: close parser if codec changed
> >>
> >> The parser depends on the codec and thus must not be used with a different one.
> >> If it is, the 'avctx->codec_id == s->parser->codec_ids[0] ...' assert in
> >> av_parser_parse2 gets triggered.
> >>
> >> Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
> >> ---
> >>  libavformat/utils.c | 12 ++++++++++++
> >>  1 file changed, 12 insertions(+)
> >>
> >> diff --git a/libavformat/utils.c b/libavformat/utils.c
> >> index 70dbfa1..a8a78ed 100644
> >> --- a/libavformat/utils.c
> >> +++ b/libavformat/utils.c
> >> @@ -480,6 +480,12 @@ static int update_stream_avctx(AVFormatContext *s)
> >>          if (!st->internal->need_context_update)
> >>              continue;
> >>  
> >> +        /* close parser, because it depends on the codec */
> >> +        if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
> >> +            av_parser_close(st->parser);
> >> +            st->parser = NULL;
> >> +        }
> >> +
> >>          /* update internal codec context, for the parser */
> >>          ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
> >>          if (ret < 0)
> >> @@ -1515,6 +1521,12 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
> >>                  st->info->found_decoder = 0;
> >>              }
> >>  
> >> +            /* close parser, because it depends on the codec */
> >> +            if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
> >> +                av_parser_close(st->parser);
> >> +                st->parser = NULL;
> >> +            }
> >> +
> > 
> > what if the codec id differs but both are supported by the parser ?
> > AVCodecParser has a list of 5 codec ids ?
> > 
> > I didnt find a testcase where this makes a difference, just wondering
> 
> I think the parser should be re-initialized in that case, too.

doesnt this lead to data loss (parser internal buffers being lost at
a transition between 2 types)?
both types might be handled by the same decoder so nothing special
might be needed "downstream"


[...]
Andreas Cadhalpun Nov. 3, 2016, 12:04 a.m. UTC | #5
On 03.11.2016 00:42, Michael Niedermayer wrote:
> On Wed, Nov 02, 2016 at 10:30:30PM +0100, Andreas Cadhalpun wrote:
>> On 02.11.2016 13:07, Michael Niedermayer wrote:
>>> On Sat, Oct 22, 2016 at 01:16:00AM +0200, Andreas Cadhalpun wrote:
>>>>  utils.c |   12 ++++++++++++
>>>>  1 file changed, 12 insertions(+)
>>>> ffefc22756b774cb7652587207ae66cfbf681be3  0001-avformat-close-parser-if-codec-changed.patch
>>>> From 9de87a4fb2c6c6311a11a2da5de8554a71adfa66 Mon Sep 17 00:00:00 2001
>>>> From: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
>>>> Date: Mon, 17 Oct 2016 20:26:51 +0200
>>>> Subject: [PATCH] avformat: close parser if codec changed
>>>>
>>>> The parser depends on the codec and thus must not be used with a different one.
>>>> If it is, the 'avctx->codec_id == s->parser->codec_ids[0] ...' assert in
>>>> av_parser_parse2 gets triggered.
>>>>
>>>> Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
>>>> ---
>>>>  libavformat/utils.c | 12 ++++++++++++
>>>>  1 file changed, 12 insertions(+)
>>>>
>>>> diff --git a/libavformat/utils.c b/libavformat/utils.c
>>>> index 70dbfa1..a8a78ed 100644
>>>> --- a/libavformat/utils.c
>>>> +++ b/libavformat/utils.c
>>>> @@ -480,6 +480,12 @@ static int update_stream_avctx(AVFormatContext *s)
>>>>          if (!st->internal->need_context_update)
>>>>              continue;
>>>>  
>>>> +        /* close parser, because it depends on the codec */
>>>> +        if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
>>>> +            av_parser_close(st->parser);
>>>> +            st->parser = NULL;
>>>> +        }
>>>> +
>>>>          /* update internal codec context, for the parser */
>>>>          ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
>>>>          if (ret < 0)
>>>> @@ -1515,6 +1521,12 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
>>>>                  st->info->found_decoder = 0;
>>>>              }
>>>>  
>>>> +            /* close parser, because it depends on the codec */
>>>> +            if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
>>>> +                av_parser_close(st->parser);
>>>> +                st->parser = NULL;
>>>> +            }
>>>> +
>>>
>>> what if the codec id differs but both are supported by the parser ?
>>> AVCodecParser has a list of 5 codec ids ?
>>>
>>> I didnt find a testcase where this makes a difference, just wondering
>>
>> I think the parser should be re-initialized in that case, too.
> 
> doesnt this lead to data loss (parser internal buffers being lost at
> a transition between 2 types)?

Yes, but it's not clear that the parser internal state is still correct
after a change of the codec id.

> both types might be handled by the same decoder so nothing special
> might be needed "downstream"

It might work for some decoders and might not work for others.
It's hard to tell without samples.

Best regards,
Andreas
Michael Niedermayer Nov. 3, 2016, 10:07 a.m. UTC | #6
On Thu, Nov 03, 2016 at 01:04:21AM +0100, Andreas Cadhalpun wrote:
> On 03.11.2016 00:42, Michael Niedermayer wrote:
> > On Wed, Nov 02, 2016 at 10:30:30PM +0100, Andreas Cadhalpun wrote:
> >> On 02.11.2016 13:07, Michael Niedermayer wrote:
> >>> On Sat, Oct 22, 2016 at 01:16:00AM +0200, Andreas Cadhalpun wrote:
> >>>>  utils.c |   12 ++++++++++++
> >>>>  1 file changed, 12 insertions(+)
> >>>> ffefc22756b774cb7652587207ae66cfbf681be3  0001-avformat-close-parser-if-codec-changed.patch
> >>>> From 9de87a4fb2c6c6311a11a2da5de8554a71adfa66 Mon Sep 17 00:00:00 2001
> >>>> From: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
> >>>> Date: Mon, 17 Oct 2016 20:26:51 +0200
> >>>> Subject: [PATCH] avformat: close parser if codec changed
> >>>>
> >>>> The parser depends on the codec and thus must not be used with a different one.
> >>>> If it is, the 'avctx->codec_id == s->parser->codec_ids[0] ...' assert in
> >>>> av_parser_parse2 gets triggered.
> >>>>
> >>>> Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
> >>>> ---
> >>>>  libavformat/utils.c | 12 ++++++++++++
> >>>>  1 file changed, 12 insertions(+)
> >>>>
> >>>> diff --git a/libavformat/utils.c b/libavformat/utils.c
> >>>> index 70dbfa1..a8a78ed 100644
> >>>> --- a/libavformat/utils.c
> >>>> +++ b/libavformat/utils.c
> >>>> @@ -480,6 +480,12 @@ static int update_stream_avctx(AVFormatContext *s)
> >>>>          if (!st->internal->need_context_update)
> >>>>              continue;
> >>>>  
> >>>> +        /* close parser, because it depends on the codec */
> >>>> +        if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
> >>>> +            av_parser_close(st->parser);
> >>>> +            st->parser = NULL;
> >>>> +        }
> >>>> +
> >>>>          /* update internal codec context, for the parser */
> >>>>          ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
> >>>>          if (ret < 0)
> >>>> @@ -1515,6 +1521,12 @@ static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
> >>>>                  st->info->found_decoder = 0;
> >>>>              }
> >>>>  
> >>>> +            /* close parser, because it depends on the codec */
> >>>> +            if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
> >>>> +                av_parser_close(st->parser);
> >>>> +                st->parser = NULL;
> >>>> +            }
> >>>> +
> >>>
> >>> what if the codec id differs but both are supported by the parser ?
> >>> AVCodecParser has a list of 5 codec ids ?
> >>>
> >>> I didnt find a testcase where this makes a difference, just wondering
> >>
> >> I think the parser should be re-initialized in that case, too.
> > 
> > doesnt this lead to data loss (parser internal buffers being lost at
> > a transition between 2 types)?
> 
> Yes, but it's not clear that the parser internal state is still correct
> after a change of the codec id.

what exact case are we talking about ?

A. The parser changeing the codec_id ?
B. The caller changing the codec_id ?

B looks like a violation of the API, the caller cant change things
without reopening the parser

for A id consider the parser completely broken if it changes the
codec id and falls in a inconsistent state without the user
detecting that and closing it.
Such requirement for user apps also is not documented

Parsers dont handle unrelated formats, they handle things that are
similar or identical like  AV_CODEC_ID_MJPEG, AV_CODEC_ID_JPEGLS

some parsers dont set the codec_id, like *jpeg doesnt
mpeg audio contains checks to explicitly check for changing codec id
only ac3 and mpeg2 seem to set the codec id at all

which parser becomes inconsistent with itself when changing codec id ?


> 
> > both types might be handled by the same decoder so nothing special
> > might be needed "downstream"
> 
> It might work for some decoders and might not work for others.
> It's hard to tell without samples.

making samples should be as easy as concatenating 2 streams if
someone wants to check what happens exactly

[...]
Andreas Cadhalpun Nov. 3, 2016, 8:03 p.m. UTC | #7
On 03.11.2016 11:07, Michael Niedermayer wrote:
> On Thu, Nov 03, 2016 at 01:04:21AM +0100, Andreas Cadhalpun wrote:
>> Yes, but it's not clear that the parser internal state is still correct
>> after a change of the codec id.
> 
> what exact case are we talking about ?
> 
> A. The parser changeing the codec_id ?
> B. The caller changing the codec_id ?

I meant:
C. The demuxer changing the codec_id.

> B looks like a violation of the API, the caller cant change things
> without reopening the parser

Agreed.

> for A id consider the parser completely broken if it changes the
> codec id and falls in a inconsistent state without the user
> detecting that and closing it.
> Such requirement for user apps also is not documented

Also agreed.

> Parsers dont handle unrelated formats, they handle things that are
> similar or identical like  AV_CODEC_ID_MJPEG, AV_CODEC_ID_JPEGLS
> 
> some parsers dont set the codec_id, like *jpeg doesnt
> mpeg audio contains checks to explicitly check for changing codec id
> only ac3 and mpeg2 seem to set the codec id at all
> 
> which parser becomes inconsistent with itself when changing codec id ?

I guess the gsm parser, as the blocksize is not updated once it is set,
but different between gsm and gsm_ms.

>> It's hard to tell without samples.
> making samples should be as easy as concatenating 2 streams if
> someone wants to check what happens exactly

I tried a few possibly interesting cases, but in no case could I trigger
the added calls to av_parser_close for codec_ids of the same parser.
Nonetheless, some like mp2/mp3 worked just fine, while e.g. gsm/gsm_ms
broke badly.

Best regards,
Andreas
diff mbox

Patch

From 9de87a4fb2c6c6311a11a2da5de8554a71adfa66 Mon Sep 17 00:00:00 2001
From: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
Date: Mon, 17 Oct 2016 20:26:51 +0200
Subject: [PATCH] avformat: close parser if codec changed

The parser depends on the codec and thus must not be used with a different one.
If it is, the 'avctx->codec_id == s->parser->codec_ids[0] ...' assert in
av_parser_parse2 gets triggered.

Signed-off-by: Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
---
 libavformat/utils.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/libavformat/utils.c b/libavformat/utils.c
index 70dbfa1..a8a78ed 100644
--- a/libavformat/utils.c
+++ b/libavformat/utils.c
@@ -480,6 +480,12 @@  static int update_stream_avctx(AVFormatContext *s)
         if (!st->internal->need_context_update)
             continue;
 
+        /* close parser, because it depends on the codec */
+        if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
+            av_parser_close(st->parser);
+            st->parser = NULL;
+        }
+
         /* update internal codec context, for the parser */
         ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
         if (ret < 0)
@@ -1515,6 +1521,12 @@  static int read_frame_internal(AVFormatContext *s, AVPacket *pkt)
                 st->info->found_decoder = 0;
             }
 
+            /* close parser, because it depends on the codec */
+            if (st->parser && st->internal->avctx->codec_id != st->codecpar->codec_id) {
+                av_parser_close(st->parser);
+                st->parser = NULL;
+            }
+
             ret = avcodec_parameters_to_context(st->internal->avctx, st->codecpar);
             if (ret < 0)
                 return ret;
-- 
2.9.3