diff mbox series

[FFmpeg-devel,v10,2/2] avformat: add demuxer for Pro Pinball Series' Soundbanks

Message ID 20200418002020.13390-3-zane@zanevaniperen.com
State Accepted
Headers show
Series Pro Pinball Series Soundbank demuxer + decoder. | expand

Checks

Context Check Description
andriy/default pending
andriy/make success Make finished
andriy/make_fate success Make fate finished

Commit Message

Zane van Iperen April 18, 2020, 12:20 a.m. UTC
Signed-off-by: Zane van Iperen <zane@zanevaniperen.com>
---
 Changelog                |   1 +
 libavformat/Makefile     |   1 +
 libavformat/allformats.c |   1 +
 libavformat/pp_bnk.c     | 293 +++++++++++++++++++++++++++++++++++++++
 libavformat/version.h    |   2 +-
 5 files changed, 297 insertions(+), 1 deletion(-)
 create mode 100644 libavformat/pp_bnk.c

Comments

Michael Niedermayer April 25, 2020, 11:15 a.m. UTC | #1
On Sat, Apr 18, 2020 at 12:20:49AM +0000, Zane van Iperen wrote:
> Signed-off-by: Zane van Iperen <zane@zanevaniperen.com>
> ---
>  Changelog                |   1 +
>  libavformat/Makefile     |   1 +
>  libavformat/allformats.c |   1 +
>  libavformat/pp_bnk.c     | 293 +++++++++++++++++++++++++++++++++++++++
>  libavformat/version.h    |   2 +-
>  5 files changed, 297 insertions(+), 1 deletion(-)
>  create mode 100644 libavformat/pp_bnk.c
> 
> diff --git a/Changelog b/Changelog
> index 1d219a56e0..592beca626 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -60,6 +60,7 @@ version <next>:
>  - Expanded styling support for 3GPP Timed Text Subtitles (movtext)
>  - WebP parser
>  - Cunning Developments ADPCM decoder
> +- Pro Pinball Series Soundbank demuxer
>  
>  
>  version 4.2:
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index d4bed3c113..b744eb69b2 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -428,6 +428,7 @@ OBJS-$(CONFIG_PCM_VIDC_DEMUXER)          += pcmdec.o pcm.o
>  OBJS-$(CONFIG_PCM_VIDC_MUXER)            += pcmenc.o rawenc.o
>  OBJS-$(CONFIG_PJS_DEMUXER)               += pjsdec.o subtitles.o
>  OBJS-$(CONFIG_PMP_DEMUXER)               += pmpdec.o
> +OBJS-$(CONFIG_PP_BNK_DEMUXER)            += pp_bnk.o
>  OBJS-$(CONFIG_PVA_DEMUXER)               += pva.o
>  OBJS-$(CONFIG_PVF_DEMUXER)               += pvfdec.o pcm.o
>  OBJS-$(CONFIG_QCP_DEMUXER)               += qcp.o
> diff --git a/libavformat/allformats.c b/libavformat/allformats.c
> index 39d2c352f5..3919c9e4c1 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -341,6 +341,7 @@ extern AVInputFormat  ff_pcm_u8_demuxer;
>  extern AVOutputFormat ff_pcm_u8_muxer;
>  extern AVInputFormat  ff_pjs_demuxer;
>  extern AVInputFormat  ff_pmp_demuxer;
> +extern AVInputFormat  ff_pp_bnk_demuxer;
>  extern AVOutputFormat ff_psp_muxer;
>  extern AVInputFormat  ff_pva_demuxer;
>  extern AVInputFormat  ff_pvf_demuxer;
> diff --git a/libavformat/pp_bnk.c b/libavformat/pp_bnk.c
> new file mode 100644
> index 0000000000..f67f04b699
> --- /dev/null
> +++ b/libavformat/pp_bnk.c
> @@ -0,0 +1,293 @@
> +/*
> + * Pro Pinball Series Soundbank (44c, 22c, 11c, 5c) demuxer.
> + *
> + * Copyright (C) 2020 Zane van Iperen (zane@zanevaniperen.com)
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +#include "avformat.h"
> +#include "internal.h"
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/avassert.h"
> +#include "libavutil/internal.h"
> +
> +#define PP_BNK_MAX_READ_SIZE    4096
> +#define PP_BNK_FILE_HEADER_SIZE 20
> +#define PP_BNK_TRACK_SIZE       20
> +
> +typedef struct PPBnkHeader {
> +    uint32_t        bank_id;        /*< Bank ID, useless for our purposes. */
> +    uint32_t        sample_rate;    /*< Sample rate of the contained tracks. */
> +    uint32_t        always1;        /*< Unknown, always seems to be 1. */
> +    uint32_t        track_count;    /*< Number of tracks in the file. */
> +    uint32_t        flags;          /*< Flags. */
> +} PPBnkHeader;
> +
> +typedef struct PPBnkTrack {
> +    uint32_t        id;             /*< Track ID. Usually track[i].id == track[i-1].id + 1, but not always */
> +    uint32_t        size;           /*< Size of the data in bytes. */
> +    uint32_t        sample_rate;    /*< Sample rate. */
> +    uint32_t        always1_1;      /*< Unknown, always seems to be 1. */
> +    uint32_t        always1_2;      /*< Unknown, always seems to be 1. */
> +} PPBnkTrack;
> +
> +typedef struct PPBnkCtxTrack {
> +    int64_t         data_offset;
> +    uint32_t        data_size;
> +} PPBnkCtxTrack;
> +
> +typedef struct PPBnkCtx {
> +    int             track_count;
> +    PPBnkCtxTrack   *tracks;
> +    uint32_t        current_track;
> +    uint32_t        bytes_read;
> +} PPBnkCtx;
> +
> +enum {
> +    PP_BNK_FLAG_PERSIST = (1 << 0), /*< This is a large file, keep in memory. */
> +    PP_BNK_FLAG_MUSIC   = (1 << 1), /*< This is music. */
> +    PP_BNK_FLAG_MASK    = (PP_BNK_FLAG_PERSIST | PP_BNK_FLAG_MUSIC)
> +};
> +
> +static void pp_bnk_parse_header(PPBnkHeader *hdr, const uint8_t *buf)
> +{
> +    hdr->bank_id        = AV_RL32(buf +  0);
> +    hdr->sample_rate    = AV_RL32(buf +  4);
> +    hdr->always1        = AV_RL32(buf +  8);
> +    hdr->track_count    = AV_RL32(buf + 12);
> +    hdr->flags          = AV_RL32(buf + 16);
> +}
> +
> +static void pp_bnk_parse_track(PPBnkTrack *trk, const uint8_t *buf)
> +{
> +    trk->id             = AV_RL32(buf +  0);
> +    trk->size           = AV_RL32(buf +  4);
> +    trk->sample_rate    = AV_RL32(buf +  8);
> +    trk->always1_1      = AV_RL32(buf + 12);
> +    trk->always1_2      = AV_RL32(buf + 16);
> +}
> +

> +static int pp_bnk_probe(const AVProbeData *p)
> +{
> +    uint32_t sample_rate = AV_RL32(p->buf +  4);
> +    uint32_t track_count = AV_RL32(p->buf + 12);
> +    uint32_t flags       = AV_RL32(p->buf + 16);
> +
> +    if (track_count == 0 || sample_rate == 0)
> +        return 0;

the header code checks these also for INT_MAX


> +
> +    /* Sometimes we have the first track header, so check that too. */
> +    if (p->buf_size >= 32 && AV_RL32(p->buf + 28) != sample_rate)
> +        return 0;

are files with 32 or less bytes valid ?
If not its probably better to not recognize such small pieces


[...]
Zane van Iperen April 25, 2020, 1:10 p.m. UTC | #2
On Sat, 25 Apr 2020 13:15:25 +0200
"Michael Niedermayer" <michael@niedermayer.cc> wrote:

> > +static int pp_bnk_probe(const AVProbeData *p)
> > +{
> > +    uint32_t sample_rate = AV_RL32(p->buf +  4);
> > +    uint32_t track_count = AV_RL32(p->buf + 12);
> > +    uint32_t flags       = AV_RL32(p->buf + 16);
> > +
> > +    if (track_count == 0 || sample_rate == 0)
> > +        return 0;  
> 
> the header code checks these also for INT_MAX
> 
> 

See below I check both track_count and sample_rate for sane upper
limits. Is it worth adding an INT_MAX check too?


    /* These limits are based on analysing the game files. */
    if (track_count > 113)
        return 10;

    if ((sample_rate !=  5512) && (sample_rate != 11025) &&
        (sample_rate != 22050) && (sample_rate != 44100))
        return 10;



> > +
> > +    /* Sometimes we have the first track header, so check that
> > too. */
> > +    if (p->buf_size >= 32 && AV_RL32(p->buf + 28) != sample_rate)
> > +        return 0;  
> 
> are files with 32 or less bytes valid ?
> If not its probably better to not recognize such small pieces
> 

A file needs at least one track and the first track header is
immediately after the file header, so I guess the minimum valid size
would be 40 (assuming an empty track).

Would removing the "p->buf_size >= 32" check be alright?


Zane
Michael Niedermayer April 25, 2020, 10:07 p.m. UTC | #3
On Sat, Apr 25, 2020 at 01:10:53PM +0000, Zane van Iperen wrote:
> On Sat, 25 Apr 2020 13:15:25 +0200
> "Michael Niedermayer" <michael@niedermayer.cc> wrote:
> 
> > > +static int pp_bnk_probe(const AVProbeData *p)
> > > +{
> > > +    uint32_t sample_rate = AV_RL32(p->buf +  4);
> > > +    uint32_t track_count = AV_RL32(p->buf + 12);
> > > +    uint32_t flags       = AV_RL32(p->buf + 16);
> > > +
> > > +    if (track_count == 0 || sample_rate == 0)
> > > +        return 0;  
> > 
> > the header code checks these also for INT_MAX
> > 
> > 
> 
> See below I check both track_count and sample_rate for sane upper
> limits. Is it worth adding an INT_MAX check too?

why not ? its rejected by the header reading code


> 
> 
>     /* These limits are based on analysing the game files. */
>     if (track_count > 113)
>         return 10;
> 
>     if ((sample_rate !=  5512) && (sample_rate != 11025) &&
>         (sample_rate != 22050) && (sample_rate != 44100))
>         return 10;
> 
> 
> 
> > > +
> > > +    /* Sometimes we have the first track header, so check that
> > > too. */
> > > +    if (p->buf_size >= 32 && AV_RL32(p->buf + 28) != sample_rate)
> > > +        return 0;  
> > 
> > are files with 32 or less bytes valid ?
> > If not its probably better to not recognize such small pieces
> > 
> 
> A file needs at least one track and the first track header is
> immediately after the file header, so I guess the minimum valid size
> would be 40 (assuming an empty track).
> 
> Would removing the "p->buf_size >= 32" check be alright?

yes, that should be ok due to AVPROBE_PADDING_SIZE

also iam not sure it makes sense or if it goes to far but the probe
code could check all tracks which are within the buffer
all these low 10 scores are a bit ugly, some more solid "yes iam sure
this is / is not" instead of such really low scores.

low scores which get returned based on few weak checks have the problem
of more often producing errors in detection. Random non multimedia files being
detected as something they are not or if 2 demuxers have such a low score
detection then its more likely the wrong one will be choosen


thx

[...]
Zane van Iperen April 25, 2020, 11:50 p.m. UTC | #4
On Sun, 26 Apr 2020 00:07:52 +0200
"Michael Niedermayer" <michael@niedermayer.cc> wrote:

> On Sat, Apr 25, 2020 at 01:10:53PM +0000, Zane van Iperen wrote:
> > On Sat, 25 Apr 2020 13:15:25 +0200
> > "Michael Niedermayer" <michael@niedermayer.cc> wrote:
> >  
> > > > +static int pp_bnk_probe(const AVProbeData *p)
> > > > +{
> > > > +    uint32_t sample_rate = AV_RL32(p->buf +  4);
> > > > +    uint32_t track_count = AV_RL32(p->buf + 12);
> > > > +    uint32_t flags       = AV_RL32(p->buf + 16);
> > > > +
> > > > +    if (track_count == 0 || sample_rate == 0)
> > > > +        return 0;  
> > >
> > > the header code checks these also for INT_MAX
> > >
> > >  
> >
> > See below I check both track_count and sample_rate for sane upper
> > limits. Is it worth adding an INT_MAX check too?  
> 
> why not ? its rejected by the header reading code
> 

Done.
> 
> >
> >
> >     /* These limits are based on analysing the game files. */
> >     if (track_count > 113)
> >         return 10;
> >
> >     if ((sample_rate !=  5512) && (sample_rate != 11025) &&
> >         (sample_rate != 22050) && (sample_rate != 44100))
> >         return 10;
> >
> >
> >  
> > > > +
> > > > +    /* Sometimes we have the first track header, so check that
> > > > too. */
> > > > +    if (p->buf_size >= 32 && AV_RL32(p->buf + 28) !=
> > > > sample_rate)
> > > > +        return 0;  
> > >
> > > are files with 32 or less bytes valid ?
> > > If not its probably better to not recognize such small pieces
> > >  
> >
> > A file needs at least one track and the first track header is
> > immediately after the file header, so I guess the minimum valid size
> > would be 40 (assuming an empty track).
> >
> > Would removing the "p->buf_size >= 32" check be alright?  
> 
> yes, that should be ok due to AVPROBE_PADDING_SIZE
> 

Done.

> also iam not sure it makes sense or if it goes to far but the probe
> code could check all tracks which are within the buffer
> all these low 10 scores are a bit ugly, some more solid "yes iam sure
> this is / is not" instead of such really low scores.
> 
> low scores which get returned based on few weak checks have the
> problem of more often producing errors in detection. Random non
> multimedia files being detected as something they are not or if 2
> demuxers have such a low score detection then its more likely the
> wrong one will be choosen
> 

I agree it's ugly. Checking all the tracks seems rather messy imo, and
would only work for the smaller files anyway.

One thing that might be possible (but is still ugly) is to check for
file extension as well. These files are only ever be seen with
a ".{5,11,22,44}c" extension. If it matches, the score could be bumped
a bit higher.

A more-forceful option is to change the "return 10" checks to "return
0". Although technically valid files, there are none that exist in
the wild with these cases. I'd prefer this solution and can just leave a
comment if it needs to be changed in the future.

Zane
Zane van Iperen April 26, 2020, 11:49 p.m. UTC | #5
On Sun, 26 Apr 2020 00:07:52 +0200
"Michael Niedermayer" <michael@niedermayer.cc> wrote:

> 
> also iam not sure it makes sense or if it goes to far but the probe
> code could check all tracks which are within the buffer
> all these low 10 scores are a bit ugly, some more solid "yes iam sure
> this is / is not" instead of such really low scores.
> 
> low scores which get returned based on few weak checks have the
> problem of more often producing errors in detection. Random non
> multimedia files being detected as something they are not or if 2
> demuxers have such a low score detection then its more likely the
> wrong one will be choosen
> 
> 

How's this?

I've removed the "track_count > 113" check and made it
all-or-nothing.

static int pp_bnk_probe(const AVProbeData *p)
{
    uint32_t sample_rate = AV_RL32(p->buf +  4);
    uint32_t track_count = AV_RL32(p->buf + 12);
    uint32_t flags       = AV_RL32(p->buf + 16);

    if (track_count == 0 || track_count > INT_MAX)
        return 0;

    if ((sample_rate !=  5512) && (sample_rate != 11025) &&
        (sample_rate != 22050) && (sample_rate != 44100))
        return 0;

    /* Check the first track header. */
    if (AV_RL32(p->buf + 28) != sample_rate)
        return 0;

    if ((flags & ~PP_BNK_FLAG_MASK) != 0)
        return 0;

    return AVPROBE_SCORE_MAX / 4 + 1;
}


Zane
Michael Niedermayer April 27, 2020, 7:22 p.m. UTC | #6
On Sun, Apr 26, 2020 at 11:49:00PM +0000, Zane van Iperen wrote:
> On Sun, 26 Apr 2020 00:07:52 +0200
> "Michael Niedermayer" <michael@niedermayer.cc> wrote:
> 
> > 
> > also iam not sure it makes sense or if it goes to far but the probe
> > code could check all tracks which are within the buffer
> > all these low 10 scores are a bit ugly, some more solid "yes iam sure
> > this is / is not" instead of such really low scores.
> > 
> > low scores which get returned based on few weak checks have the
> > problem of more often producing errors in detection. Random non
> > multimedia files being detected as something they are not or if 2
> > demuxers have such a low score detection then its more likely the
> > wrong one will be choosen
> > 
> > 
> 
> How's this?
> 
> I've removed the "track_count > 113" check and made it
> all-or-nothing.
> 
> static int pp_bnk_probe(const AVProbeData *p)
> {
>     uint32_t sample_rate = AV_RL32(p->buf +  4);
>     uint32_t track_count = AV_RL32(p->buf + 12);
>     uint32_t flags       = AV_RL32(p->buf + 16);
> 
>     if (track_count == 0 || track_count > INT_MAX)
>         return 0;
> 
>     if ((sample_rate !=  5512) && (sample_rate != 11025) &&
>         (sample_rate != 22050) && (sample_rate != 44100))
>         return 0;
> 
>     /* Check the first track header. */
>     if (AV_RL32(p->buf + 28) != sample_rate)
>         return 0;
> 
>     if ((flags & ~PP_BNK_FLAG_MASK) != 0)
>         return 0;
> 
>     return AVPROBE_SCORE_MAX / 4 + 1;
> }

ok until someone sees a way to improve this

thx


[...]
diff mbox series

Patch

diff --git a/Changelog b/Changelog
index 1d219a56e0..592beca626 100644
--- a/Changelog
+++ b/Changelog
@@ -60,6 +60,7 @@  version <next>:
 - Expanded styling support for 3GPP Timed Text Subtitles (movtext)
 - WebP parser
 - Cunning Developments ADPCM decoder
+- Pro Pinball Series Soundbank demuxer
 
 
 version 4.2:
diff --git a/libavformat/Makefile b/libavformat/Makefile
index d4bed3c113..b744eb69b2 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -428,6 +428,7 @@  OBJS-$(CONFIG_PCM_VIDC_DEMUXER)          += pcmdec.o pcm.o
 OBJS-$(CONFIG_PCM_VIDC_MUXER)            += pcmenc.o rawenc.o
 OBJS-$(CONFIG_PJS_DEMUXER)               += pjsdec.o subtitles.o
 OBJS-$(CONFIG_PMP_DEMUXER)               += pmpdec.o
+OBJS-$(CONFIG_PP_BNK_DEMUXER)            += pp_bnk.o
 OBJS-$(CONFIG_PVA_DEMUXER)               += pva.o
 OBJS-$(CONFIG_PVF_DEMUXER)               += pvfdec.o pcm.o
 OBJS-$(CONFIG_QCP_DEMUXER)               += qcp.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index 39d2c352f5..3919c9e4c1 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -341,6 +341,7 @@  extern AVInputFormat  ff_pcm_u8_demuxer;
 extern AVOutputFormat ff_pcm_u8_muxer;
 extern AVInputFormat  ff_pjs_demuxer;
 extern AVInputFormat  ff_pmp_demuxer;
+extern AVInputFormat  ff_pp_bnk_demuxer;
 extern AVOutputFormat ff_psp_muxer;
 extern AVInputFormat  ff_pva_demuxer;
 extern AVInputFormat  ff_pvf_demuxer;
diff --git a/libavformat/pp_bnk.c b/libavformat/pp_bnk.c
new file mode 100644
index 0000000000..f67f04b699
--- /dev/null
+++ b/libavformat/pp_bnk.c
@@ -0,0 +1,293 @@ 
+/*
+ * Pro Pinball Series Soundbank (44c, 22c, 11c, 5c) demuxer.
+ *
+ * Copyright (C) 2020 Zane van Iperen (zane@zanevaniperen.com)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "avformat.h"
+#include "internal.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/avassert.h"
+#include "libavutil/internal.h"
+
+#define PP_BNK_MAX_READ_SIZE    4096
+#define PP_BNK_FILE_HEADER_SIZE 20
+#define PP_BNK_TRACK_SIZE       20
+
+typedef struct PPBnkHeader {
+    uint32_t        bank_id;        /*< Bank ID, useless for our purposes. */
+    uint32_t        sample_rate;    /*< Sample rate of the contained tracks. */
+    uint32_t        always1;        /*< Unknown, always seems to be 1. */
+    uint32_t        track_count;    /*< Number of tracks in the file. */
+    uint32_t        flags;          /*< Flags. */
+} PPBnkHeader;
+
+typedef struct PPBnkTrack {
+    uint32_t        id;             /*< Track ID. Usually track[i].id == track[i-1].id + 1, but not always */
+    uint32_t        size;           /*< Size of the data in bytes. */
+    uint32_t        sample_rate;    /*< Sample rate. */
+    uint32_t        always1_1;      /*< Unknown, always seems to be 1. */
+    uint32_t        always1_2;      /*< Unknown, always seems to be 1. */
+} PPBnkTrack;
+
+typedef struct PPBnkCtxTrack {
+    int64_t         data_offset;
+    uint32_t        data_size;
+} PPBnkCtxTrack;
+
+typedef struct PPBnkCtx {
+    int             track_count;
+    PPBnkCtxTrack   *tracks;
+    uint32_t        current_track;
+    uint32_t        bytes_read;
+} PPBnkCtx;
+
+enum {
+    PP_BNK_FLAG_PERSIST = (1 << 0), /*< This is a large file, keep in memory. */
+    PP_BNK_FLAG_MUSIC   = (1 << 1), /*< This is music. */
+    PP_BNK_FLAG_MASK    = (PP_BNK_FLAG_PERSIST | PP_BNK_FLAG_MUSIC)
+};
+
+static void pp_bnk_parse_header(PPBnkHeader *hdr, const uint8_t *buf)
+{
+    hdr->bank_id        = AV_RL32(buf +  0);
+    hdr->sample_rate    = AV_RL32(buf +  4);
+    hdr->always1        = AV_RL32(buf +  8);
+    hdr->track_count    = AV_RL32(buf + 12);
+    hdr->flags          = AV_RL32(buf + 16);
+}
+
+static void pp_bnk_parse_track(PPBnkTrack *trk, const uint8_t *buf)
+{
+    trk->id             = AV_RL32(buf +  0);
+    trk->size           = AV_RL32(buf +  4);
+    trk->sample_rate    = AV_RL32(buf +  8);
+    trk->always1_1      = AV_RL32(buf + 12);
+    trk->always1_2      = AV_RL32(buf + 16);
+}
+
+static int pp_bnk_probe(const AVProbeData *p)
+{
+    uint32_t sample_rate = AV_RL32(p->buf +  4);
+    uint32_t track_count = AV_RL32(p->buf + 12);
+    uint32_t flags       = AV_RL32(p->buf + 16);
+
+    if (track_count == 0 || sample_rate == 0)
+        return 0;
+
+    /* Sometimes we have the first track header, so check that too. */
+    if (p->buf_size >= 32 && AV_RL32(p->buf + 28) != sample_rate)
+        return 0;
+
+    /* These limits are based on analysing the game files. */
+    if (track_count > 113)
+        return 10;
+
+    if ((sample_rate !=  5512) && (sample_rate != 11025) &&
+        (sample_rate != 22050) && (sample_rate != 44100))
+        return 10;
+
+    if ((flags & ~PP_BNK_FLAG_MASK) != 0)
+        return 10;
+
+    return AVPROBE_SCORE_MAX / 4 + 1;
+}
+
+static int pp_bnk_read_header(AVFormatContext *s)
+{
+    int64_t ret;
+    AVStream *st;
+    AVCodecParameters *par;
+    PPBnkCtx *ctx = s->priv_data;
+    uint8_t buf[FFMAX(PP_BNK_FILE_HEADER_SIZE, PP_BNK_TRACK_SIZE)];
+    PPBnkHeader hdr;
+
+    if ((ret = avio_read(s->pb, buf, PP_BNK_FILE_HEADER_SIZE)) < 0)
+        return ret;
+    else if (ret != PP_BNK_FILE_HEADER_SIZE)
+        return AVERROR(EIO);
+
+    pp_bnk_parse_header(&hdr, buf);
+
+    if (hdr.track_count == 0 || hdr.track_count > INT_MAX)
+        return AVERROR_INVALIDDATA;
+
+    if (hdr.sample_rate == 0 || hdr.sample_rate > INT_MAX)
+        return AVERROR_INVALIDDATA;
+
+    if (hdr.always1 != 1) {
+        avpriv_request_sample(s, "Non-one header value");
+        return AVERROR_PATCHWELCOME;
+    }
+
+    ctx->track_count = hdr.track_count;
+
+    if (!(ctx->tracks = av_malloc_array(hdr.track_count, sizeof(PPBnkCtxTrack))))
+        return AVERROR(ENOMEM);
+
+    /* Parse and validate each track. */
+    for (int i = 0; i < hdr.track_count; i++) {
+        PPBnkTrack e;
+        PPBnkCtxTrack *trk = ctx->tracks + i;
+
+        ret = avio_read(s->pb, buf, PP_BNK_TRACK_SIZE);
+        if (ret < 0 && ret != AVERROR_EOF)
+            goto fail;
+
+        /* Short byte-count or EOF, we have a truncated file. */
+        if (ret != PP_BNK_TRACK_SIZE) {
+            av_log(s, AV_LOG_WARNING, "File truncated at %d/%u track(s)\n",
+                   i, hdr.track_count);
+            ctx->track_count = i;
+            break;
+        }
+
+        pp_bnk_parse_track(&e, buf);
+
+        /* The individual sample rates of all tracks must match that of the file header. */
+        if (e.sample_rate != hdr.sample_rate) {
+            ret = AVERROR_INVALIDDATA;
+            goto fail;
+        }
+
+        if (e.always1_1 != 1 || e.always1_2 != 1) {
+            avpriv_request_sample(s, "Non-one track header values");
+            ret = AVERROR_PATCHWELCOME;
+            goto fail;
+        }
+
+        trk->data_offset = avio_tell(s->pb);
+        trk->data_size   = e.size;
+
+        /*
+         * Skip over the data to the next stream header.
+         * Sometimes avio_skip() doesn't detect EOF. If it doesn't, either:
+         *   - the avio_read() above will, or
+         *   - pp_bnk_read_packet() will read a truncated last track.
+         */
+        if ((ret = avio_skip(s->pb, e.size)) == AVERROR_EOF) {
+            ctx->track_count = i + 1;
+            av_log(s, AV_LOG_WARNING,
+                   "Track %d has truncated data, assuming track count == %d\n",
+                   i, ctx->track_count);
+            break;
+        } else if (ret < 0) {
+            goto fail;
+        }
+    }
+
+    /* File is only a header. */
+    if (ctx->track_count == 0) {
+        ret = AVERROR_INVALIDDATA;
+        goto fail;
+    }
+
+    /* Build the streams. */
+    for (int i = 0; i < ctx->track_count; i++) {
+        if (!(st = avformat_new_stream(s, NULL))) {
+            ret = AVERROR(ENOMEM);
+            goto fail;
+        }
+
+        par                         = st->codecpar;
+        par->codec_type             = AVMEDIA_TYPE_AUDIO;
+        par->codec_id               = AV_CODEC_ID_ADPCM_IMA_CUNNING;
+        par->format                 = AV_SAMPLE_FMT_S16;
+        par->channel_layout         = AV_CH_LAYOUT_MONO;
+        par->channels               = 1;
+        par->sample_rate            = hdr.sample_rate;
+        par->bits_per_coded_sample  = 4;
+        par->bits_per_raw_sample    = 16;
+        par->block_align            = 1;
+        par->bit_rate               = par->sample_rate * par->bits_per_coded_sample;
+
+        avpriv_set_pts_info(st, 64, 1, par->sample_rate);
+        st->start_time              = 0;
+        st->duration                = ctx->tracks[i].data_size * 2;
+    }
+
+    /* Seek to the start of the first stream. */
+    if ((ret = avio_seek(s->pb, ctx->tracks[0].data_offset, SEEK_SET)) < 0) {
+        goto fail;
+    } else if (ret != ctx->tracks[0].data_offset) {
+        ret = AVERROR(EIO);
+        goto fail;
+    }
+
+    return 0;
+
+fail:
+    av_freep(&ctx->tracks);
+    return ret;
+}
+
+static int pp_bnk_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    int64_t ret;
+    int size;
+    PPBnkCtx *ctx = s->priv_data;
+    PPBnkCtxTrack *trk = ctx->tracks + ctx->current_track;
+
+    av_assert0(ctx->bytes_read <= trk->data_size);
+
+    if (ctx->bytes_read == trk->data_size) {
+        if (ctx->current_track == ctx->track_count - 1)
+            return AVERROR_EOF;
+
+        trk++;
+
+        if ((ret = avio_seek(s->pb, trk->data_offset, SEEK_SET)) < 0)
+            return ret;
+        else if (ret != trk->data_offset)
+            return AVERROR(EIO);
+
+        ctx->bytes_read = 0;
+        ctx->current_track++;
+    }
+
+    size = FFMIN(trk->data_size - ctx->bytes_read, PP_BNK_MAX_READ_SIZE);
+
+    if ((ret = av_get_packet(s->pb, pkt, size)) < 0)
+        return ret;
+
+    ctx->bytes_read    += ret;
+    pkt->flags         &= ~AV_PKT_FLAG_CORRUPT;
+    pkt->stream_index   = ctx->current_track;
+    pkt->duration       = ret * 2;
+
+    return 0;
+}
+
+static int pp_bnk_read_close(AVFormatContext *s)
+{
+    PPBnkCtx *ctx = s->priv_data;
+
+    av_freep(&ctx->tracks);
+
+    return 0;
+}
+
+AVInputFormat ff_pp_bnk_demuxer = {
+    .name           = "pp_bnk",
+    .long_name      = NULL_IF_CONFIG_SMALL("Pro Pinball Series Soundbank"),
+    .priv_data_size = sizeof(PPBnkCtx),
+    .read_probe     = pp_bnk_probe,
+    .read_header    = pp_bnk_read_header,
+    .read_packet    = pp_bnk_read_packet,
+    .read_close     = pp_bnk_read_close
+};
diff --git a/libavformat/version.h b/libavformat/version.h
index 18c2f5fec2..493a0b337f 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -32,7 +32,7 @@ 
 // Major bumping may affect Ticket5467, 5421, 5451(compatibility with Chromium)
 // Also please add any ticket numbers that you believe might be affected here
 #define LIBAVFORMAT_VERSION_MAJOR  58
-#define LIBAVFORMAT_VERSION_MINOR  42
+#define LIBAVFORMAT_VERSION_MINOR  43
 #define LIBAVFORMAT_VERSION_MICRO 100
 
 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \