diff mbox

[FFmpeg-devel] decode.c: Handle 0-size packets in compat_decode

Message ID 20170703185721.9520-1-Reimar.Doeffinger@gmx.de
State New
Headers show

Commit Message

Reimar Döffinger July 3, 2017, 6:57 p.m. UTC
The old API did that just fine, and if we provide
a compatibility layer it should at least be compatible.
For the test-case (feeding AVParser output directly to
decoder, failing to discard 0-size packets) just discarding
0-size packets at the start works, but not sure if in some
cases we might want to pass them on to e.g. allow retrieving
additional pending frames.
---
 libavcodec/decode.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

Comments

wm4 July 3, 2017, 7:07 p.m. UTC | #1
On Mon,  3 Jul 2017 20:57:21 +0200
Reimar Döffinger <Reimar.Doeffinger@gmx.de> wrote:

> The old API did that just fine, and if we provide
> a compatibility layer it should at least be compatible.
> For the test-case (feeding AVParser output directly to
> decoder, failing to discard 0-size packets) just discarding
> 0-size packets at the start works, but not sure if in some
> cases we might want to pass them on to e.g. allow retrieving
> additional pending frames.
> ---
>  libavcodec/decode.c | 6 +++++-
>  1 file changed, 5 insertions(+), 1 deletion(-)
> 
> diff --git a/libavcodec/decode.c b/libavcodec/decode.c
> index 052f93d82f..c63090f137 100644
> --- a/libavcodec/decode.c
> +++ b/libavcodec/decode.c
> @@ -842,6 +842,7 @@ static int compat_decode(AVCodecContext *avctx, AVFrame *frame,
>      avci->compat_decode = 1;
>  
>      if (avci->compat_decode_partial_size > 0 &&
> +        pkt->size &&
>          avci->compat_decode_partial_size != pkt->size) {
>          av_log(avctx, AV_LOG_ERROR,
>                 "Got unexpected packet size after a partial decode\n");
> @@ -902,7 +903,10 @@ finish:
>              ret = FFMIN(avci->compat_decode_consumed, pkt->size);
>      }
>      avci->compat_decode_consumed = 0;
> -    avci->compat_decode_partial_size = (ret >= 0) ? pkt->size - ret : 0;
> +    // for compatibility with old API behaviour handle
> +    // 0-size specially
> +    if (pkt->size)
> +        avci->compat_decode_partial_size = (ret >= 0) ? pkt->size - ret : 0;
>  
>      return ret;
>  }

I thought it was decided that 0-sized packets are always disallowed for
encoding? Also, 0-sized packets are drain packets even with the old
API, and thus could never properly work. You just relied in implicit
restart behavior (i.e. leaving the drained state by feeding a new
non-0-sized packet), which also didn't work with all decoders.
Reimar Döffinger July 3, 2017, 10:03 p.m. UTC | #2
On 03.07.2017, at 21:07, wm4 <nfxjfg@googlemail.com> wrote:

> On Mon,  3 Jul 2017 20:57:21 +0200
> Reimar Döffinger <Reimar.Doeffinger@gmx.de> wrote:
> 
>> The old API did that just fine, and if we provide
>> a compatibility layer it should at least be compatible.
>> For the test-case (feeding AVParser output directly to
>> decoder, failing to discard 0-size packets) just discarding
>> 0-size packets at the start works, but not sure if in some
>> cases we might want to pass them on to e.g. allow retrieving
>> additional pending frames.
>> ---
>> libavcodec/decode.c | 6 +++++-
>> 1 file changed, 5 insertions(+), 1 deletion(-)
>> 
>> diff --git a/libavcodec/decode.c b/libavcodec/decode.c
>> index 052f93d82f..c63090f137 100644
>> --- a/libavcodec/decode.c
>> +++ b/libavcodec/decode.c
>> @@ -842,6 +842,7 @@ static int compat_decode(AVCodecContext *avctx, AVFrame *frame,
>>     avci->compat_decode = 1;
>> 
>>     if (avci->compat_decode_partial_size > 0 &&
>> +        pkt->size &&
>>         avci->compat_decode_partial_size != pkt->size) {
>>         av_log(avctx, AV_LOG_ERROR,
>>                "Got unexpected packet size after a partial decode\n");
>> @@ -902,7 +903,10 @@ finish:
>>             ret = FFMIN(avci->compat_decode_consumed, pkt->size);
>>     }
>>     avci->compat_decode_consumed = 0;
>> -    avci->compat_decode_partial_size = (ret >= 0) ? pkt->size - ret : 0;
>> +    // for compatibility with old API behaviour handle
>> +    // 0-size specially
>> +    if (pkt->size)
>> +        avci->compat_decode_partial_size = (ret >= 0) ? pkt->size - ret : 0;
>> 
>>     return ret;
>> }
> 
> I thought it was decided that 0-sized packets are always disallowed for
> encoding?

This function is for decoding, I have not checked encoding side...

> Also, 0-sized packets are drain packets even with the old
> API, and thus could never properly work. You just relied in implicit
> restart behavior (i.e. leaving the drained state by feeding a new
> non-0-sized packet), which also didn't work with all decoders.

I don't know, and I don't really care, I just noticed that we have a huge compatibility wrapper function that isn't even compatible, which is a bit ridiculous.
So I proposed to make it at least more compatible.
I don't mind changing it to be simpler and just discard them right at the start, if you think that makes more sense. Though even if there's just one decoder where 0-sized packets had some working practical use it might be better to pass them through...
Note that I don't disagree that this was probably a bug that 0-sized packets got in (though I didn't re-check the parser API), just saying I do not see much reason to not be backwards-compatible.
I know for sure that the ac3 and mp3 audio codecs at least had no issues with 0-size packets before.
wm4 July 4, 2017, 8:42 a.m. UTC | #3
On Tue, 4 Jul 2017 00:03:19 +0200
Reimar Döffinger <Reimar.Doeffinger@gmx.de> wrote:

> On 03.07.2017, at 21:07, wm4 <nfxjfg@googlemail.com> wrote:
> 
> > On Mon,  3 Jul 2017 20:57:21 +0200
> > Reimar Döffinger <Reimar.Doeffinger@gmx.de> wrote:
> >   
> >> The old API did that just fine, and if we provide
> >> a compatibility layer it should at least be compatible.
> >> For the test-case (feeding AVParser output directly to
> >> decoder, failing to discard 0-size packets) just discarding
> >> 0-size packets at the start works, but not sure if in some
> >> cases we might want to pass them on to e.g. allow retrieving
> >> additional pending frames.
> >> ---
> >> libavcodec/decode.c | 6 +++++-
> >> 1 file changed, 5 insertions(+), 1 deletion(-)
> >> 
> >> diff --git a/libavcodec/decode.c b/libavcodec/decode.c
> >> index 052f93d82f..c63090f137 100644
> >> --- a/libavcodec/decode.c
> >> +++ b/libavcodec/decode.c
> >> @@ -842,6 +842,7 @@ static int compat_decode(AVCodecContext *avctx, AVFrame *frame,
> >>     avci->compat_decode = 1;
> >> 
> >>     if (avci->compat_decode_partial_size > 0 &&
> >> +        pkt->size &&
> >>         avci->compat_decode_partial_size != pkt->size) {
> >>         av_log(avctx, AV_LOG_ERROR,
> >>                "Got unexpected packet size after a partial decode\n");
> >> @@ -902,7 +903,10 @@ finish:
> >>             ret = FFMIN(avci->compat_decode_consumed, pkt->size);
> >>     }
> >>     avci->compat_decode_consumed = 0;
> >> -    avci->compat_decode_partial_size = (ret >= 0) ? pkt->size - ret : 0;
> >> +    // for compatibility with old API behaviour handle
> >> +    // 0-size specially
> >> +    if (pkt->size)
> >> +        avci->compat_decode_partial_size = (ret >= 0) ? pkt->size - ret : 0;
> >> 
> >>     return ret;
> >> }  
> > 
> > I thought it was decided that 0-sized packets are always disallowed for
> > encoding?  
> 
> This function is for decoding, I have not checked encoding side...

Typo. I meant decoding. I was probably thinking about how encoding
sometimes uses 0-sized packets with extradata, and then wrote
"encoding" instead of "decoding". Sorry.

> > Also, 0-sized packets are drain packets even with the old
> > API, and thus could never properly work. You just relied in implicit
> > restart behavior (i.e. leaving the drained state by feeding a new
> > non-0-sized packet), which also didn't work with all decoders.  
> 
> I don't know, and I don't really care, I just noticed that we have a huge compatibility wrapper function that isn't even compatible, which is a bit ridiculous.
> So I proposed to make it at least more compatible.
> I don't mind changing it to be simpler and just discard them right at the start, if you think that makes more sense. Though even if there's just one decoder where 0-sized packets had some working practical use it might be better to pass them through...

Well no, there are multiple situations where 0-sized packets are
treated as end-of-stream, so we decided that they are simply not
allowed.

> Note that I don't disagree that this was probably a bug that 0-sized packets got in (though I didn't re-check the parser API), just saying I do not see much reason to not be backwards-compatible.
> I know for sure that the ac3 and mp3 audio codecs at least had no issues with 0-size packets before.

Other decoders crashed or didn't recover. (I faintly remember
something about windows audio...)

Not really comfortable with the current patch. Why does it even touch
the compat_decode_partial_size handling path?

If you really want this, then I think you should go all the way and
implement the implicit drain-and-flush behavior that the old API
exposed with most decoders. (Meaning you can drain the decoder, and
then it's implicitly flushed, so that sending new packets will restart
decoding.)

Either that, or skip empty packets on the API user side. If a
libavformat demuxer or libavcodec parser is producing empty packets,
these should probably be fixed.
Reimar Döffinger July 4, 2017, 6:20 p.m. UTC | #4
On 04.07.2017, at 10:42, wm4 <nfxjfg@googlemail.com> wrote:
> Not really comfortable with the current patch. Why does it even touch
> the compat_decode_partial_size handling path?

Because that is the code that gets utterly confused by 0-size packets and breaks everything.
For ac3 what happens is that after the first 0-sized packets all following packets fail to decode.
Probably that means the whole partial_size thing is broken and not robust, but I haven't been able to really understand it.

> If you really want this, then I think you should go all the way and
> implement the implicit drain-and-flush behavior that the old API
> exposed with most decoders. (Meaning you can drain the decoder, and
> then it's implicitly flushed, so that sending new packets will restart
> decoding.)

Yeah, I just don't feel particularly confident implementing that, since this compat wrapper has all kinds of weird code and no comments...

> Either that, or skip empty packets on the API user side. If a
> libavformat demuxer or libavcodec parser is producing empty packets,
> these should probably be fixed.

Skipping it on user side is of course what I did, but it doesn't provide compatibility.
Any distribution e.g. updating FFmpeg more often than MPlayer will break.
wm4 July 5, 2017, 7:52 a.m. UTC | #5
On Tue, 4 Jul 2017 20:20:38 +0200
Reimar Döffinger <Reimar.Doeffinger@gmx.de> wrote:

> On 04.07.2017, at 10:42, wm4 <nfxjfg@googlemail.com> wrote:
> > Not really comfortable with the current patch. Why does it even touch
> > the compat_decode_partial_size handling path?  
> 
> Because that is the code that gets utterly confused by 0-size packets and breaks everything.
> For ac3 what happens is that after the first 0-sized packets all following packets fail to decode.

0-sized packets do not exist in AC3 in particular and have no meaning.

> Probably that means the whole partial_size thing is broken and not robust, but I haven't been able to really understand it.

This is for the mechanism where you passed a packet to the decode API,
and it only decoded a few bytes of it, and you had to feed the rest of
the packet to the decoder again next time to get all audio. (Plus
confusing special cases about some decoders consuming 0 bytes in the
first call, or consuming more bytes than what was provided as input...)

The new API doesn't have that, and will just require multiple receive
calls in those cases.

> > If you really want this, then I think you should go all the way and
> > implement the implicit drain-and-flush behavior that the old API
> > exposed with most decoders. (Meaning you can drain the decoder, and
> > then it's implicitly flushed, so that sending new packets will restart
> > decoding.)  
> 
> Yeah, I just don't feel particularly confident implementing that, since this compat wrapper has all kinds of weird code and no comments...

Shouldn't be too hard if you understand the old API. The new API is
well-documented.

> > Either that, or skip empty packets on the API user side. If a
> > libavformat demuxer or libavcodec parser is producing empty packets,
> > these should probably be fixed.  
> 
> Skipping it on user side is of course what I did, but it doesn't provide compatibility.
> Any distribution e.g. updating FFmpeg more often than MPlayer will break.

Well, it was broken before anyway. Decoding 0 sized packets usually
will start draining the decoder, and sending new data after that
(without flush) resulted in arbitrary behavior.
Michael Niedermayer July 5, 2017, 2:01 p.m. UTC | #6
On Wed, Jul 05, 2017 at 09:52:55AM +0200, wm4 wrote:
> On Tue, 4 Jul 2017 20:20:38 +0200
> Reimar Döffinger <Reimar.Doeffinger@gmx.de> wrote:
> 
> > On 04.07.2017, at 10:42, wm4 <nfxjfg@googlemail.com> wrote:
> > > Not really comfortable with the current patch. Why does it even touch
> > > the compat_decode_partial_size handling path?  
> > 
> > Because that is the code that gets utterly confused by 0-size packets and breaks everything.
> > For ac3 what happens is that after the first 0-sized packets all following packets fail to decode.
> 
> 0-sized packets do not exist in AC3 in particular and have no meaning.
> 
> > Probably that means the whole partial_size thing is broken and not robust, but I haven't been able to really understand it.
> 
> This is for the mechanism where you passed a packet to the decode API,
> and it only decoded a few bytes of it, and you had to feed the rest of
> the packet to the decoder again next time to get all audio. (Plus
> confusing special cases about some decoders consuming 0 bytes in the
> first call, or

> consuming more bytes than what was provided as input...)

That would be a bug and such bugs where fixed when they where reported.


> 
> The new API doesn't have that, and will just require multiple receive
> calls in those cases.


[...]
diff mbox

Patch

diff --git a/libavcodec/decode.c b/libavcodec/decode.c
index 052f93d82f..c63090f137 100644
--- a/libavcodec/decode.c
+++ b/libavcodec/decode.c
@@ -842,6 +842,7 @@  static int compat_decode(AVCodecContext *avctx, AVFrame *frame,
     avci->compat_decode = 1;
 
     if (avci->compat_decode_partial_size > 0 &&
+        pkt->size &&
         avci->compat_decode_partial_size != pkt->size) {
         av_log(avctx, AV_LOG_ERROR,
                "Got unexpected packet size after a partial decode\n");
@@ -902,7 +903,10 @@  finish:
             ret = FFMIN(avci->compat_decode_consumed, pkt->size);
     }
     avci->compat_decode_consumed = 0;
-    avci->compat_decode_partial_size = (ret >= 0) ? pkt->size - ret : 0;
+    // for compatibility with old API behaviour handle
+    // 0-size specially
+    if (pkt->size)
+        avci->compat_decode_partial_size = (ret >= 0) ? pkt->size - ret : 0;
 
     return ret;
 }