2 * Rayman 2 APM (De)muxer
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 "config_components.h"
26 #include "avio_internal.h"
31 #include "libavutil/channel_layout.h"
32 #include "libavutil/internal.h"
33 #include "libavutil/intreadwrite.h"
35 #define APM_FILE_HEADER_SIZE 20
36 #define APM_FILE_EXTRADATA_SIZE 80
37 #define APM_EXTRADATA_SIZE 28
39 #define APM_MAX_READ_SIZE 4096
41 #define APM_TAG_CODEC 0x2000
42 #define APM_TAG_VS12 MKTAG('v', 's', '1', '2')
43 #define APM_TAG_DATA MKTAG('D', 'A', 'T', 'A')
45 typedef struct APMState
{
55 typedef struct APMExtraData
{
66 #if CONFIG_APM_DEMUXER
67 static void apm_parse_extradata(APMExtraData
*ext
, const uint8_t *buf
)
69 ext
->magic
= AV_RL32(buf
+ 0);
70 ext
->file_size
= AV_RL32(buf
+ 4);
71 ext
->data_size
= AV_RL32(buf
+ 8);
72 ext
->unk1
= AV_RL32(buf
+ 12);
73 ext
->unk2
= AV_RL32(buf
+ 16);
75 ext
->state
.has_saved
= AV_RL32(buf
+ 20);
76 ext
->state
.predictor_r
= AV_RL32(buf
+ 24);
77 ext
->state
.step_index_r
= AV_RL32(buf
+ 28);
78 ext
->state
.saved_r
= AV_RL32(buf
+ 32);
79 ext
->state
.predictor_l
= AV_RL32(buf
+ 36);
80 ext
->state
.step_index_l
= AV_RL32(buf
+ 40);
81 ext
->state
.saved_l
= AV_RL32(buf
+ 44);
83 for (int i
= 0; i
< FF_ARRAY_ELEMS(ext
->unk3
); i
++)
84 ext
->unk3
[i
] = AV_RL32(buf
+ 48 + (i
* 4));
86 ext
->data
= AV_RL32(buf
+ 76);
89 static int apm_probe(const AVProbeData
*p
)
91 if (AV_RL16(p
->buf
) != APM_TAG_CODEC
)
94 if (p
->buf_size
< 100)
97 if (AV_RL32(p
->buf
+ 20) != APM_TAG_VS12
)
100 if (AV_RL32(p
->buf
+ 96) != APM_TAG_DATA
)
103 return AVPROBE_SCORE_MAX
- 1;
106 static int apm_read_header(AVFormatContext
*s
)
110 APMExtraData extradata
;
111 AVCodecParameters
*par
;
112 uint8_t buf
[APM_FILE_EXTRADATA_SIZE
];
115 if (!(st
= avformat_new_stream(s
, NULL
)))
116 return AVERROR(ENOMEM
);
119 * This is 98% a WAVEFORMATEX, but there's something screwy with the extradata
120 * that ff_get_wav_header() can't (and shouldn't) handle properly.
122 if (avio_rl16(s
->pb
) != APM_TAG_CODEC
)
123 return AVERROR_INVALIDDATA
;
126 channels
= avio_rl16(s
->pb
);
127 par
->sample_rate
= avio_rl32(s
->pb
);
129 /* Skip the bitrate, it's usually wrong anyway. */
130 if ((ret
= avio_skip(s
->pb
, 4)) < 0)
133 par
->block_align
= avio_rl16(s
->pb
);
134 par
->bits_per_coded_sample
= avio_rl16(s
->pb
);
136 if (avio_rl32(s
->pb
) != APM_FILE_EXTRADATA_SIZE
)
137 return AVERROR_INVALIDDATA
;
139 /* 8 = bits per sample * max channels */
140 if (par
->sample_rate
> (INT_MAX
/ 8))
141 return AVERROR_INVALIDDATA
;
143 if (par
->bits_per_coded_sample
!= 4)
144 return AVERROR_INVALIDDATA
;
146 if (channels
> 2 || channels
== 0)
147 return AVERROR_INVALIDDATA
;
149 av_channel_layout_default(&par
->ch_layout
, channels
);
150 par
->codec_type
= AVMEDIA_TYPE_AUDIO
;
151 par
->codec_id
= AV_CODEC_ID_ADPCM_IMA_APM
;
152 par
->format
= AV_SAMPLE_FMT_S16
;
153 par
->bit_rate
= par
->ch_layout
.nb_channels
*
154 (int64_t)par
->sample_rate
*
155 par
->bits_per_coded_sample
;
157 if ((ret
= ffio_read_size(s
->pb
, buf
, APM_FILE_EXTRADATA_SIZE
)) < 0)
160 apm_parse_extradata(&extradata
, buf
);
162 if (extradata
.magic
!= APM_TAG_VS12
|| extradata
.data
!= APM_TAG_DATA
)
163 return AVERROR_INVALIDDATA
;
165 if (extradata
.state
.has_saved
) {
166 avpriv_request_sample(s
, "Saved Samples");
167 return AVERROR_PATCHWELCOME
;
170 if ((ret
= ff_alloc_extradata(par
, APM_EXTRADATA_SIZE
)) < 0)
173 /* Use the entire state as extradata. */
174 memcpy(par
->extradata
, buf
+ 20, APM_EXTRADATA_SIZE
);
176 avpriv_set_pts_info(st
, 64, 1, par
->sample_rate
);
178 st
->duration
= extradata
.data_size
*
179 (8 / par
->bits_per_coded_sample
) /
180 par
->ch_layout
.nb_channels
;
184 static int apm_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
187 AVCodecParameters
*par
= s
->streams
[0]->codecpar
;
190 * For future reference: if files with the `has_saved` field set ever
191 * surface, `saved_l`, and `saved_r` will each contain 8 "saved" samples
192 * that should be sent to the decoder before the actual data.
195 if ((ret
= av_get_packet(s
->pb
, pkt
, APM_MAX_READ_SIZE
)) < 0)
198 pkt
->flags
&= ~AV_PKT_FLAG_CORRUPT
;
199 pkt
->stream_index
= 0;
200 pkt
->duration
= ret
* (8 / par
->bits_per_coded_sample
) / par
->ch_layout
.nb_channels
;
205 const FFInputFormat ff_apm_demuxer
= {
207 .p
.long_name
= NULL_IF_CONFIG_SMALL("Ubisoft Rayman 2 APM"),
208 .read_probe
= apm_probe
,
209 .read_header
= apm_read_header
,
210 .read_packet
= apm_read_packet
215 static int apm_write_init(AVFormatContext
*s
)
217 AVCodecParameters
*par
= s
->streams
[0]->codecpar
;
219 if (par
->ch_layout
.nb_channels
> 2) {
220 av_log(s
, AV_LOG_ERROR
, "APM files only support up to 2 channels\n");
221 return AVERROR(EINVAL
);
224 if (par
->sample_rate
> (INT_MAX
/ 8)) {
225 av_log(s
, AV_LOG_ERROR
, "Sample rate too large\n");
226 return AVERROR(EINVAL
);
229 if (par
->extradata_size
!= APM_EXTRADATA_SIZE
) {
230 av_log(s
, AV_LOG_ERROR
, "Invalid/missing extradata\n");
231 return AVERROR(EINVAL
);
234 if (!(s
->pb
->seekable
& AVIO_SEEKABLE_NORMAL
)) {
235 av_log(s
, AV_LOG_ERROR
, "Stream not seekable, unable to write output file\n");
236 return AVERROR(EINVAL
);
242 static int apm_write_header(AVFormatContext
*s
)
244 uint8_t buf
[APM_FILE_EXTRADATA_SIZE
] = { 0 };
245 AVCodecParameters
*par
= s
->streams
[0]->codecpar
;
248 * Bodge a WAVEFORMATEX manually, ff_put_wav_header() can't
249 * be used because of the extra 2 bytes.
251 avio_wl16(s
->pb
, APM_TAG_CODEC
);
252 avio_wl16(s
->pb
, par
->ch_layout
.nb_channels
);
253 avio_wl32(s
->pb
, par
->sample_rate
);
254 /* This is the wrong calculation, but it's what the original files have. */
255 avio_wl32(s
->pb
, par
->sample_rate
* par
->ch_layout
.nb_channels
* 2);
256 avio_wl16(s
->pb
, par
->block_align
);
257 avio_wl16(s
->pb
, par
->bits_per_coded_sample
);
258 avio_wl32(s
->pb
, APM_FILE_EXTRADATA_SIZE
);
261 * Build the extradata. Assume the codec's given us correct data.
262 * File and data sizes are fixed later.
264 AV_WL32(buf
+ 0, APM_TAG_VS12
); /* magic */
265 AV_WL32(buf
+ 12, 0xFFFFFFFF); /* unk1 */
266 memcpy( buf
+ 20, par
->extradata
, APM_EXTRADATA_SIZE
);
267 AV_WL32(buf
+ 76, APM_TAG_DATA
); /* data */
269 avio_write(s
->pb
, buf
, APM_FILE_EXTRADATA_SIZE
);
273 static int apm_write_trailer(AVFormatContext
*s
)
275 int64_t file_size
, data_size
;
277 file_size
= avio_tell(s
->pb
);
278 data_size
= file_size
- (APM_FILE_HEADER_SIZE
+ APM_FILE_EXTRADATA_SIZE
);
280 if (file_size
>= UINT32_MAX
) {
281 av_log(s
, AV_LOG_ERROR
,
282 "Filesize %"PRId64
" invalid for APM, output file will be broken\n",
284 return AVERROR(ERANGE
);
287 avio_seek(s
->pb
, 24, SEEK_SET
);
288 avio_wl32(s
->pb
, (uint32_t)file_size
);
289 avio_wl32(s
->pb
, (uint32_t)data_size
);
294 const FFOutputFormat ff_apm_muxer
= {
296 .p
.long_name
= NULL_IF_CONFIG_SMALL("Ubisoft Rayman 2 APM"),
297 .p
.extensions
= "apm",
298 .p
.audio_codec
= AV_CODEC_ID_ADPCM_IMA_APM
,
299 .p
.video_codec
= AV_CODEC_ID_NONE
,
300 .p
.subtitle_codec
= AV_CODEC_ID_NONE
,
301 .flags_internal
= FF_OFMT_FLAG_MAX_ONE_OF_EACH
|
302 FF_OFMT_FLAG_ONLY_DEFAULT_CODECS
,
303 .init
= apm_write_init
,
304 .write_header
= apm_write_header
,
305 .write_packet
= ff_raw_write_packet
,
306 .write_trailer
= apm_write_trailer