2 * Pro Pinball Series Soundbank (44c, 22c, 11c, 5c) demuxer.
4 * Copyright (C) 2020 Zane van Iperen (zane@zanevaniperen.com)
6 * This file is part of FFmpeg.
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "avio_internal.h"
26 #include "libavutil/intreadwrite.h"
27 #include "libavutil/avassert.h"
28 #include "libavutil/channel_layout.h"
29 #include "libavutil/internal.h"
30 #include "libavutil/mem.h"
32 #define PP_BNK_MAX_READ_SIZE 4096
33 #define PP_BNK_FILE_HEADER_SIZE 20
34 #define PP_BNK_TRACK_SIZE 20
36 typedef struct PPBnkHeader
{
37 uint32_t bank_id
; /*< Bank ID, useless for our purposes. */
38 uint32_t sample_rate
; /*< Sample rate of the contained tracks. */
39 uint32_t always1
; /*< Unknown, always seems to be 1. */
40 uint32_t track_count
; /*< Number of tracks in the file. */
41 uint32_t flags
; /*< Flags. */
44 typedef struct PPBnkTrack
{
45 uint32_t id
; /*< Track ID. Usually track[i].id == track[i-1].id + 1, but not always */
46 uint32_t size
; /*< Size of the data in bytes. */
47 uint32_t sample_rate
; /*< Sample rate. */
48 uint32_t always1_1
; /*< Unknown, always seems to be 1. */
49 uint32_t always1_2
; /*< Unknown, always seems to be 1. */
52 typedef struct PPBnkCtxTrack
{
58 typedef struct PPBnkCtx
{
60 PPBnkCtxTrack
*tracks
;
61 uint32_t current_track
;
66 PP_BNK_FLAG_PERSIST
= (1 << 0), /*< This is a large file, keep in memory. */
67 PP_BNK_FLAG_MUSIC
= (1 << 1), /*< This is music. */
68 PP_BNK_FLAG_MASK
= (PP_BNK_FLAG_PERSIST
| PP_BNK_FLAG_MUSIC
)
71 static void pp_bnk_parse_header(PPBnkHeader
*hdr
, const uint8_t *buf
)
73 hdr
->bank_id
= AV_RL32(buf
+ 0);
74 hdr
->sample_rate
= AV_RL32(buf
+ 4);
75 hdr
->always1
= AV_RL32(buf
+ 8);
76 hdr
->track_count
= AV_RL32(buf
+ 12);
77 hdr
->flags
= AV_RL32(buf
+ 16);
80 static void pp_bnk_parse_track(PPBnkTrack
*trk
, const uint8_t *buf
)
82 trk
->id
= AV_RL32(buf
+ 0);
83 trk
->size
= AV_RL32(buf
+ 4);
84 trk
->sample_rate
= AV_RL32(buf
+ 8);
85 trk
->always1_1
= AV_RL32(buf
+ 12);
86 trk
->always1_2
= AV_RL32(buf
+ 16);
89 static int pp_bnk_probe(const AVProbeData
*p
)
91 uint32_t sample_rate
= AV_RL32(p
->buf
+ 4);
92 uint32_t track_count
= AV_RL32(p
->buf
+ 12);
93 uint32_t flags
= AV_RL32(p
->buf
+ 16);
95 if (track_count
== 0 || track_count
> INT_MAX
)
98 if ((sample_rate
!= 5512) && (sample_rate
!= 11025) &&
99 (sample_rate
!= 22050) && (sample_rate
!= 44100))
102 /* Check the first track header. */
103 if (AV_RL32(p
->buf
+ 28) != sample_rate
)
106 if ((flags
& ~PP_BNK_FLAG_MASK
) != 0)
109 return AVPROBE_SCORE_MAX
/ 4 + 1;
112 static int pp_bnk_read_header(AVFormatContext
*s
)
116 AVCodecParameters
*par
;
117 PPBnkCtx
*ctx
= s
->priv_data
;
118 uint8_t buf
[FFMAX(PP_BNK_FILE_HEADER_SIZE
, PP_BNK_TRACK_SIZE
)];
121 if ((ret
= ffio_read_size(s
->pb
, buf
, PP_BNK_FILE_HEADER_SIZE
)) < 0)
124 pp_bnk_parse_header(&hdr
, buf
);
126 if (hdr
.track_count
== 0 || hdr
.track_count
> INT_MAX
)
127 return AVERROR_INVALIDDATA
;
129 if (hdr
.sample_rate
== 0 || hdr
.sample_rate
> INT_MAX
)
130 return AVERROR_INVALIDDATA
;
132 if (hdr
.always1
!= 1) {
133 avpriv_request_sample(s
, "Non-one header value");
134 return AVERROR_PATCHWELCOME
;
137 ctx
->track_count
= hdr
.track_count
;
139 if (!(ctx
->tracks
= av_malloc_array(hdr
.track_count
, sizeof(PPBnkCtxTrack
))))
140 return AVERROR(ENOMEM
);
142 /* Parse and validate each track. */
143 for (int i
= 0; i
< hdr
.track_count
; i
++) {
145 PPBnkCtxTrack
*trk
= ctx
->tracks
+ i
;
147 ret
= avio_read(s
->pb
, buf
, PP_BNK_TRACK_SIZE
);
148 if (ret
< 0 && ret
!= AVERROR_EOF
)
151 /* Short byte-count or EOF, we have a truncated file. */
152 if (ret
!= PP_BNK_TRACK_SIZE
) {
153 av_log(s
, AV_LOG_WARNING
, "File truncated at %d/%u track(s)\n",
155 ctx
->track_count
= i
;
159 pp_bnk_parse_track(&e
, buf
);
161 /* The individual sample rates of all tracks must match that of the file header. */
162 if (e
.sample_rate
!= hdr
.sample_rate
)
163 return AVERROR_INVALIDDATA
;
165 if (e
.always1_1
!= 1 || e
.always1_2
!= 1) {
166 avpriv_request_sample(s
, "Non-one track header values");
167 return AVERROR_PATCHWELCOME
;
170 trk
->data_offset
= avio_tell(s
->pb
);
171 trk
->data_size
= e
.size
;
175 * Skip over the data to the next stream header.
176 * Sometimes avio_skip() doesn't detect EOF. If it doesn't, either:
177 * - the avio_read() above will, or
178 * - pp_bnk_read_packet() will read a truncated last track.
180 if ((ret
= avio_skip(s
->pb
, e
.size
)) == AVERROR_EOF
) {
181 ctx
->track_count
= i
+ 1;
182 av_log(s
, AV_LOG_WARNING
,
183 "Track %d has truncated data, assuming track count == %d\n",
184 i
, ctx
->track_count
);
186 } else if (ret
< 0) {
191 /* File is only a header. */
192 if (ctx
->track_count
== 0)
193 return AVERROR_INVALIDDATA
;
195 ctx
->is_music
= (hdr
.flags
& PP_BNK_FLAG_MUSIC
) &&
196 (ctx
->track_count
== 2) &&
197 (ctx
->tracks
[0].data_size
== ctx
->tracks
[1].data_size
);
199 /* Build the streams. */
200 for (int i
= 0; i
< (ctx
->is_music
? 1 : ctx
->track_count
); i
++) {
201 if (!(st
= avformat_new_stream(s
, NULL
)))
202 return AVERROR(ENOMEM
);
205 par
->codec_type
= AVMEDIA_TYPE_AUDIO
;
206 par
->codec_id
= AV_CODEC_ID_ADPCM_IMA_CUNNING
;
207 par
->format
= AV_SAMPLE_FMT_S16P
;
209 av_channel_layout_default(&par
->ch_layout
, ctx
->is_music
+ 1);
210 par
->sample_rate
= hdr
.sample_rate
;
211 par
->bits_per_coded_sample
= 4;
212 par
->block_align
= 1;
213 par
->bit_rate
= par
->sample_rate
* (int64_t)par
->bits_per_coded_sample
*
214 par
->ch_layout
.nb_channels
;
216 avpriv_set_pts_info(st
, 64, 1, par
->sample_rate
);
218 st
->duration
= ctx
->tracks
[i
].data_size
* 2;
224 static int pp_bnk_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
226 PPBnkCtx
*ctx
= s
->priv_data
;
229 * Read a packet from each track, round-robin style.
230 * This method is nasty, but needed to avoid "Too many packets buffered" errors.
232 for (int i
= 0; i
< ctx
->track_count
; i
++, ctx
->current_track
++)
238 ctx
->current_track
%= ctx
->track_count
;
240 trk
= ctx
->tracks
+ ctx
->current_track
;
242 if (trk
->bytes_read
== trk
->data_size
)
245 if ((ret
= avio_seek(s
->pb
, trk
->data_offset
+ trk
->bytes_read
, SEEK_SET
)) < 0)
247 else if (ret
!= trk
->data_offset
+ trk
->bytes_read
)
248 return AVERROR_INVALIDDATA
;
250 size
= FFMIN(trk
->data_size
- trk
->bytes_read
, PP_BNK_MAX_READ_SIZE
);
252 if (!ctx
->is_music
) {
253 ret
= av_get_packet(s
->pb
, pkt
, size
);
254 if (ret
== AVERROR_EOF
) {
255 /* If we've hit EOF, don't attempt this track again. */
256 trk
->data_size
= trk
->bytes_read
;
260 if (!pkt
->data
&& (ret
= av_new_packet(pkt
, size
* 2)) < 0)
262 ret
= avio_read(s
->pb
, pkt
->data
+ size
* ctx
->current_track
, size
);
263 if (ret
>= 0 && ret
!= size
) {
264 /* Only return stereo packets if both tracks could be read. */
271 trk
->bytes_read
+= ret
;
272 pkt
->flags
&= ~AV_PKT_FLAG_CORRUPT
;
273 pkt
->stream_index
= ctx
->current_track
;
274 pkt
->duration
= ret
* 2;
277 if (pkt
->stream_index
== 0)
280 pkt
->stream_index
= 0;
283 ctx
->current_track
++;
287 /* If we reach here, we're done. */
291 static int pp_bnk_read_close(AVFormatContext
*s
)
293 PPBnkCtx
*ctx
= s
->priv_data
;
295 av_freep(&ctx
->tracks
);
300 static int pp_bnk_seek(AVFormatContext
*s
, int stream_index
,
301 int64_t pts
, int flags
)
303 PPBnkCtx
*ctx
= s
->priv_data
;
306 return AVERROR(EINVAL
);
309 av_assert0(stream_index
== 0);
310 ctx
->tracks
[0].bytes_read
= 0;
311 ctx
->tracks
[1].bytes_read
= 0;
313 ctx
->tracks
[stream_index
].bytes_read
= 0;
319 const FFInputFormat ff_pp_bnk_demuxer
= {
321 .p
.long_name
= NULL_IF_CONFIG_SMALL("Pro Pinball Series Soundbank"),
322 .priv_data_size
= sizeof(PPBnkCtx
),
323 .flags_internal
= FF_INFMT_FLAG_INIT_CLEANUP
,
324 .read_probe
= pp_bnk_probe
,
325 .read_header
= pp_bnk_read_header
,
326 .read_packet
= pp_bnk_read_packet
,
327 .read_close
= pp_bnk_read_close
,
328 .read_seek
= pp_bnk_seek
,