diff mbox series

[FFmpeg-devel] avcodec/wmaprodec: skip foreign XMA packets

Message ID 20210831204547.25082-1-jl@conductive.de
State New
Headers show
Series [FFmpeg-devel] avcodec/wmaprodec: skip foreign XMA packets | expand

Checks

Context Check Description
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished
andriy/make_ppc success Make finished
andriy/make_fate_ppc success Make fate finished

Commit Message

Joel Linn Aug. 31, 2021, 8:45 p.m. UTC
Support decoding only a selection of the encoded XMA streams.
Previously, the decoder assumed it was decoding all available streams.
---
 libavcodec/wmaprodec.c | 64 ++++++++++++++++++++++++------------------
 1 file changed, 37 insertions(+), 27 deletions(-)

Comments

Paul B Mahol Aug. 31, 2021, 8:53 p.m. UTC | #1
What samples this fixes?
Joel Linn Aug. 31, 2021, 9:10 p.m. UTC | #2
Hello,

On 2021-08-31 22:53, Paul B Mahol wrote:
> What samples this fixes?

No specific sample, it depends on the way the decoder is invoked.

It fixes using the xma1/2 decoder directly on a memory buffer and not 
wanting to decode all channels in the stream.
Say the stream has 6 channels and one wants to only decode 2.
Previously this was not possible due to the skip logic of the channel 
interleaving.

This should also make the decoder more robust because in theory it is 
possible to have an xma stream which has chunks of 2kB junk data 
(packets) which are expected to be skipped due to the skip_packets 
numbers.
I haven't seen such streams though.
Paul B Mahol Aug. 31, 2021, 9:13 p.m. UTC | #3
On Tue, Aug 31, 2021 at 11:10 PM Joel Linn <jl@conductive.de> wrote:

> Hello,
>
> On 2021-08-31 22:53, Paul B Mahol wrote:
> > What samples this fixes?
>
> No specific sample, it depends on the way the decoder is invoked.
>
> It fixes using the xma1/2 decoder directly on a memory buffer and not
> wanting to decode all channels in the stream.
> Say the stream has 6 channels and one wants to only decode 2.
> Previously this was not possible due to the skip logic of the channel
> interleaving.
>
> This should also make the decoder more robust because in theory it is
> possible to have an xma stream which has chunks of 2kB junk data
> (packets) which are expected to be skipped due to the skip_packets
> numbers.
> I haven't seen such streams though.
>

Am I correct you already use XMA parser?
Joel Linn Aug. 31, 2021, 9:19 p.m. UTC | #4
On 2021-08-31 23:13, Paul B Mahol wrote:
> On Tue, Aug 31, 2021 at 11:10 PM Joel Linn <jl@conductive.de> wrote:
> 
>> Hello,
>> 
>> On 2021-08-31 22:53, Paul B Mahol wrote:
>> > What samples this fixes?
>> 
>> No specific sample, it depends on the way the decoder is invoked.
>> 
>> It fixes using the xma1/2 decoder directly on a memory buffer and not
>> wanting to decode all channels in the stream.
>> Say the stream has 6 channels and one wants to only decode 2.
>> Previously this was not possible due to the skip logic of the channel
>> interleaving.
>> 
>> This should also make the decoder more robust because in theory it is
>> possible to have an xma stream which has chunks of 2kB junk data
>> (packets) which are expected to be skipped due to the skip_packets
>> numbers.
>> I haven't seen such streams though.
>> 
> 
> Am I correct you already use XMA parser?

In my case I was working with raw stream buffers, the XMA format (RIFF) 
headers are invisible to me.
The format is parsed and processed by guest code (inside xenia 
emulator).
ffmpeg replaces a hardware decoder dsp in this case.
Paul B Mahol Sept. 1, 2021, 6:26 a.m. UTC | #5
On Tue, Aug 31, 2021 at 11:19 PM Joel Linn <jl@conductive.de> wrote:

> On 2021-08-31 23:13, Paul B Mahol wrote:
> > On Tue, Aug 31, 2021 at 11:10 PM Joel Linn <jl@conductive.de> wrote:
> >
> >> Hello,
> >>
> >> On 2021-08-31 22:53, Paul B Mahol wrote:
> >> > What samples this fixes?
> >>
> >> No specific sample, it depends on the way the decoder is invoked.
> >>
> >> It fixes using the xma1/2 decoder directly on a memory buffer and not
> >> wanting to decode all channels in the stream.
> >> Say the stream has 6 channels and one wants to only decode 2.
> >> Previously this was not possible due to the skip logic of the channel
> >> interleaving.
> >>
> >> This should also make the decoder more robust because in theory it is
> >> possible to have an xma stream which has chunks of 2kB junk data
> >> (packets) which are expected to be skipped due to the skip_packets
> >> numbers.
> >> I haven't seen such streams though.
> >>
> >
> > Am I correct you already use XMA parser?
>
> In my case I was working with raw stream buffers, the XMA format (RIFF)
> headers are invisible to me.
> The format is parsed and processed by guest code (inside xenia
> emulator).
> ffmpeg replaces a hardware decoder dsp in this case.
>

You should really use XMA parser, it have nothing to do with RIFF.


> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>
Joel Linn Sept. 1, 2021, 8:38 a.m. UTC | #6
On 2021-09-01 08:26, Paul B Mahol wrote:
> On Tue, Aug 31, 2021 at 11:19 PM Joel Linn <jl@conductive.de> wrote:
> 
>> On 2021-08-31 23:13, Paul B Mahol wrote:
>> > On Tue, Aug 31, 2021 at 11:10 PM Joel Linn <jl@conductive.de> wrote:
>> >
>> >> Hello,
>> >>
>> >> On 2021-08-31 22:53, Paul B Mahol wrote:
>> >> > What samples this fixes?
>> >>
>> >> No specific sample, it depends on the way the decoder is invoked.
>> >>
>> >> It fixes using the xma1/2 decoder directly on a memory buffer and not
>> >> wanting to decode all channels in the stream.
>> >> Say the stream has 6 channels and one wants to only decode 2.
>> >> Previously this was not possible due to the skip logic of the channel
>> >> interleaving.
>> >>
>> >> This should also make the decoder more robust because in theory it is
>> >> possible to have an xma stream which has chunks of 2kB junk data
>> >> (packets) which are expected to be skipped due to the skip_packets
>> >> numbers.
>> >> I haven't seen such streams though.
>> >>
>> >
>> > Am I correct you already use XMA parser?
>> 
>> In my case I was working with raw stream buffers, the XMA format 
>> (RIFF)
>> headers are invisible to me.
>> The format is parsed and processed by guest code (inside xenia
>> emulator).
>> ffmpeg replaces a hardware decoder dsp in this case.
>> 
> 
> You should really use XMA parser, it have nothing to do with RIFF.

Sorry misunderstood you.
While xma_parser indeed skips packets correctly, I do not see how that 
affects buffer parsing in `xma_decode_packet`.


I introduced a small bug (variable declaration) in the patch during 
submission cleanup, will need to submit v2.
diff mbox series

Patch

diff --git a/libavcodec/wmaprodec.c b/libavcodec/wmaprodec.c
index 66271c4037..95e039c14e 100644
--- a/libavcodec/wmaprodec.c
+++ b/libavcodec/wmaprodec.c
@@ -1814,7 +1814,43 @@  static int xma_decode_packet(AVCodecContext *avctx, void *data,
     XMADecodeCtx *s = avctx->priv_data;
     int got_stream_frame_ptr = 0;
     AVFrame *frame = data;
-    int i, ret, offset = INT_MAX;
+    int i, ret, offset, skip_current_packet = INT_MAX;
+
+    /* find the owner stream of the new XMA packet that belongs to on of our streams
+     * XMA streams find their packets following packet_skips
+     * there may be other packets in between if we are not responsible for all streams
+     * (at start there is one packet per stream, then interleave non-linearly). */
+    if (s->xma[s->current_stream].packet_done ||
+        s->xma[s->current_stream].packet_loss) {
+        /* select stream with lowest skip_packets (= uses next packet) */
+        if (s->xma[s->current_stream].skip_packets != 0) {
+            int min[2];
+
+            min[0] = s->xma[0].skip_packets;
+            min[1] = i = 0;
+
+            for (i = 1; i < s->num_streams; i++) {
+                if (s->xma[i].skip_packets < min[0]) {
+                    min[0] = s->xma[i].skip_packets;
+                    min[1] = i;
+                }
+            }
+
+            s->current_stream = min[1];
+        }
+
+        skip_current_packet = !!s->xma[s->current_stream].skip_packets;
+
+        /* advance all stream packet skip counts */
+        for (i = 0; i < s->num_streams; i++) {
+            s->xma[i].skip_packets = FFMAX(0, s->xma[i].skip_packets - 1);
+        }
+
+        /* if we are not responsible for every stream, make sure we ignore
+         * XMA packets not belonging to one of our streams */
+        if (skip_current_packet)
+            return avctx->block_align;
+    }
 
     if (!s->frames[s->current_stream]->data[0]) {
         s->frames[s->current_stream]->nb_samples = 512;
@@ -1846,34 +1882,8 @@  static int xma_decode_packet(AVCodecContext *avctx, void *data,
         return ret;
     }
 
-    /* find next XMA packet's owner stream, and update.
-     * XMA streams find their packets following packet_skips
-     * (at start there is one packet per stream, then interleave non-linearly). */
     if (s->xma[s->current_stream].packet_done ||
         s->xma[s->current_stream].packet_loss) {
-
-        /* select stream with 0 skip_packets (= uses next packet) */
-        if (s->xma[s->current_stream].skip_packets != 0) {
-            int min[2];
-
-            min[0] = s->xma[0].skip_packets;
-            min[1] = i = 0;
-
-            for (i = 1; i < s->num_streams; i++) {
-                if (s->xma[i].skip_packets < min[0]) {
-                    min[0] = s->xma[i].skip_packets;
-                    min[1] = i;
-                }
-            }
-
-            s->current_stream = min[1];
-        }
-
-        /* all other streams skip next packet */
-        for (i = 0; i < s->num_streams; i++) {
-            s->xma[i].skip_packets = FFMAX(0, s->xma[i].skip_packets - 1);
-        }
-
         /* copy samples from buffer to output if possible */
         for (i = 0; i < s->num_streams; i++) {
             offset = FFMIN(offset, s->offset[i]);