Message ID | 20200418002020.13390-3-zane@zanevaniperen.com |
---|---|
State | Accepted |
Headers | show |
Series | Pro Pinball Series Soundbank demuxer + decoder. | expand |
Context | Check | Description |
---|---|---|
andriy/default | pending | |
andriy/make | success | Make finished |
andriy/make_fate | success | Make fate finished |
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 [...]
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
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 [...]
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
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
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 --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, \
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