FFmpeg
Loading...
Searching...
No Matches
apm.c
Go to the documentation of this file.
1/*
2 * Rayman 2 APM (De)muxer
3 *
4 * Copyright (C) 2020 Zane van Iperen (zane@zanevaniperen.com)
5 *
6 * This file is part of FFmpeg.
7 *
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.
12 *
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.
17 *
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
21 */
22
23#include "config_components.h"
24
25#include "avformat.h"
26#include "avio_internal.h"
27#include "demux.h"
28#include "internal.h"
29#include "mux.h"
30#include "rawenc.h"
32#include "libavutil/internal.h"
34
35#define APM_FILE_HEADER_SIZE 20
36#define APM_FILE_EXTRADATA_SIZE 80
37#define APM_EXTRADATA_SIZE 28
38
39#define APM_MAX_READ_SIZE 4096
40
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')
44
54
55typedef struct APMExtraData {
56 uint32_t magic;
57 uint32_t file_size;
58 uint32_t data_size;
59 uint32_t unk1;
60 uint32_t unk2;
62 uint32_t unk3[7];
63 uint32_t data;
65
66#if CONFIG_APM_DEMUXER
67static void apm_parse_extradata(APMExtraData *ext, const uint8_t *buf)
68{
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);
74
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);
82
83 for (int i = 0; i < FF_ARRAY_ELEMS(ext->unk3); i++)
84 ext->unk3[i] = AV_RL32(buf + 48 + (i * 4));
85
86 ext->data = AV_RL32(buf + 76);
87}
88
89static int apm_probe(const AVProbeData *p)
90{
91 if (AV_RL16(p->buf) != APM_TAG_CODEC)
92 return 0;
93
94 if (p->buf_size < 100)
95 return 0;
96
97 if (AV_RL32(p->buf + 20) != APM_TAG_VS12)
98 return 0;
99
100 if (AV_RL32(p->buf + 96) != APM_TAG_DATA)
101 return 0;
102
103 return AVPROBE_SCORE_MAX - 1;
104}
105
106static int apm_read_header(AVFormatContext *s)
107{
108 int64_t ret;
109 AVStream *st;
110 APMExtraData extradata;
112 uint8_t buf[APM_FILE_EXTRADATA_SIZE];
113 int channels;
114
115 if (!(st = avformat_new_stream(s, NULL)))
116 return AVERROR(ENOMEM);
117
118 /*
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.
121 */
122 if (avio_rl16(s->pb) != APM_TAG_CODEC)
123 return AVERROR_INVALIDDATA;
124
125 par = st->codecpar;
126 channels = avio_rl16(s->pb);
127 par->sample_rate = avio_rl32(s->pb);
128
129 /* Skip the bitrate, it's usually wrong anyway. */
130 if ((ret = avio_skip(s->pb, 4)) < 0)
131 return ret;
132
133 par->block_align = avio_rl16(s->pb);
135
137 return AVERROR_INVALIDDATA;
138
139 /* 8 = bits per sample * max channels */
140 if (par->sample_rate > (INT_MAX / 8))
141 return AVERROR_INVALIDDATA;
142
143 if (par->bits_per_coded_sample != 4)
144 return AVERROR_INVALIDDATA;
145
146 if (channels > 2 || channels == 0)
147 return AVERROR_INVALIDDATA;
148
153 par->bit_rate = par->ch_layout.nb_channels *
154 (int64_t)par->sample_rate *
156
157 if ((ret = ffio_read_size(s->pb, buf, APM_FILE_EXTRADATA_SIZE)) < 0)
158 return ret;
159
160 apm_parse_extradata(&extradata, buf);
161
162 if (extradata.magic != APM_TAG_VS12 || extradata.data != APM_TAG_DATA)
163 return AVERROR_INVALIDDATA;
164
165 if (extradata.state.has_saved) {
166 avpriv_request_sample(s, "Saved Samples");
168 }
169
170 if ((ret = ff_alloc_extradata(par, APM_EXTRADATA_SIZE)) < 0)
171 return ret;
172
173 /* Use the entire state as extradata. */
174 memcpy(par->extradata, buf + 20, APM_EXTRADATA_SIZE);
175
176 avpriv_set_pts_info(st, 64, 1, par->sample_rate);
177 st->start_time = 0;
178 st->duration = extradata.data_size *
179 (8 / par->bits_per_coded_sample) /
181 return 0;
182}
183
184static int apm_read_packet(AVFormatContext *s, AVPacket *pkt)
185{
186 int ret;
187 AVCodecParameters *par = s->streams[0]->codecpar;
188
189 /*
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.
193 */
194
195 if ((ret = av_get_packet(s->pb, pkt, APM_MAX_READ_SIZE)) < 0)
196 return ret;
197
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;
201
202 return 0;
203}
204
206 .p.name = "apm",
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
211};
212#endif
213
214#if CONFIG_APM_MUXER
215static int apm_write_init(AVFormatContext *s)
216{
217 AVCodecParameters *par = s->streams[0]->codecpar;
218
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);
222 }
223
224 if (par->sample_rate > (INT_MAX / 8)) {
225 av_log(s, AV_LOG_ERROR, "Sample rate too large\n");
226 return AVERROR(EINVAL);
227 }
228
230 av_log(s, AV_LOG_ERROR, "Invalid/missing extradata\n");
231 return AVERROR(EINVAL);
232 }
233
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);
237 }
238
239 return 0;
240}
241
242static int apm_write_header(AVFormatContext *s)
243{
244 uint8_t buf[APM_FILE_EXTRADATA_SIZE] = { 0 };
245 AVCodecParameters *par = s->streams[0]->codecpar;
246
247 /*
248 * Bodge a WAVEFORMATEX manually, ff_put_wav_header() can't
249 * be used because of the extra 2 bytes.
250 */
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);
259
260 /*
261 * Build the extradata. Assume the codec's given us correct data.
262 * File and data sizes are fixed later.
263 */
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 */
268
270 return 0;
271}
272
273static int apm_write_trailer(AVFormatContext *s)
274{
275 int64_t file_size, data_size;
276
277 file_size = avio_tell(s->pb);
278 data_size = file_size - (APM_FILE_HEADER_SIZE + APM_FILE_EXTRADATA_SIZE);
279
280 if (file_size >= UINT32_MAX) {
282 "Filesize %"PRId64" invalid for APM, output file will be broken\n",
283 file_size);
284 return AVERROR(ERANGE);
285 }
286
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);
290
291 return 0;
292}
293
295 .p.name = "apm",
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 |
303 .init = apm_write_init,
304 .write_header = apm_write_header,
305 .write_packet = ff_raw_write_packet,
306 .write_trailer = apm_write_trailer
307};
308#endif
const FFInputFormat ff_apm_demuxer
const FFOutputFormat ff_apm_muxer
#define APM_FILE_EXTRADATA_SIZE
Definition apm.c:36
#define APM_FILE_HEADER_SIZE
Definition apm.c:35
#define APM_TAG_DATA
Definition apm.c:43
#define APM_EXTRADATA_SIZE
Definition apm.c:37
#define APM_TAG_CODEC
Definition apm.c:41
#define APM_MAX_READ_SIZE
Definition apm.c:39
#define APM_TAG_VS12
Definition apm.c:42
channels
Definition aptx.h:31
int32_t
void avpriv_set_pts_info(AVStream *st, int pts_wrap_bits, unsigned int pts_num, unsigned int pts_den)
Set the time base and wrapping info for a given stream.
Definition avformat.c:834
Main libavformat public API header.
#define AVPROBE_SCORE_MAX
maximum score
Definition avformat.h:483
int av_get_packet(AVIOContext *s, AVPacket *pkt, int size)
Allocate and read the payload of a packet and initialize its fields with default values.
Definition utils.c:98
int64_t avio_seek(AVIOContext *s, int64_t offset, int whence)
fseek() equivalent for AVIOContext.
Definition aviobuf.c:236
#define AVIO_SEEKABLE_NORMAL
Seeking works like for a local file.
Definition avio.h:41
void avio_wl32(AVIOContext *s, unsigned int val)
Definition aviobuf.c:360
void avio_wl16(AVIOContext *s, unsigned int val)
Definition aviobuf.c:440
static av_always_inline int64_t avio_tell(AVIOContext *s)
ftell() equivalent for AVIOContext.
Definition avio.h:494
unsigned int avio_rl16(AVIOContext *s)
Definition aviobuf.c:717
int64_t avio_skip(AVIOContext *s, int64_t offset)
Skip given number of bytes forward.
Definition aviobuf.c:321
unsigned int avio_rl32(AVIOContext *s)
Definition aviobuf.c:733
void avio_write(AVIOContext *s, const unsigned char *buf, int size)
Definition aviobuf.c:206
int ffio_read_size(AVIOContext *s, unsigned char *buf, int size)
Read size bytes from AVIOContext into buf.
Definition aviobuf.c:665
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define s(width, name)
Definition cbs_vp9.c:198
Public libavutil channel layout APIs header.
#define NULL
Definition coverity.c:32
long long int64_t
Definition coverity.c:34
static AVPacket * pkt
@ AV_CODEC_ID_NONE
Definition codec_id.h:48
@ AV_CODEC_ID_ADPCM_IMA_APM
Definition codec_id.h:415
#define AV_PKT_FLAG_CORRUPT
The packet content is corrupted.
Definition packet.h:651
AVStream * avformat_new_stream(AVFormatContext *s, const struct AVCodec *c)
Add a new stream to a media file.
void av_channel_layout_default(AVChannelLayout *ch_layout, int nb_channels)
Get the default channel layout for a given number of channels.
#define AVERROR_PATCHWELCOME
Not yet implemented in FFmpeg, patches welcome.
Definition error.h:64
#define AVERROR_INVALIDDATA
Invalid data found when processing input.
Definition error.h:61
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
@ AVMEDIA_TYPE_AUDIO
Definition avutil.h:201
@ AV_SAMPLE_FMT_S16
signed 16 bits
Definition samplefmt.h:58
#define AV_WL32(p, v)
#define AV_RL32(p)
#define AV_RL16(p)
int ff_alloc_extradata(AVCodecParameters *par, int size)
Allocate extradata with additional AV_INPUT_BUFFER_PADDING_SIZE at end which is always set to 0.
Definition utils.c:237
int ff_raw_write_packet(AVFormatContext *s, AVPacket *pkt)
Definition rawenc.c:31
common internal API header
#define NULL_IF_CONFIG_SMALL(x)
Return NULL if CONFIG_SMALL is true, otherwise the argument without modification.
Definition internal.h:88
#define FF_OFMT_FLAG_MAX_ONE_OF_EACH
If this flag is set, it indicates that for each codec type whose corresponding default codec (i....
Definition mux.h:50
#define FF_OFMT_FLAG_ONLY_DEFAULT_CODECS
If this flag is set, then the only permitted audio/video/subtitle codec ids are AVOutputFormat....
Definition mux.h:59
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
#define FF_ARRAY_ELEMS(a)
uint32_t data_size
Definition apm.c:58
uint32_t data
Definition apm.c:63
uint32_t unk3[7]
Definition apm.c:62
APMState state
Definition apm.c:61
uint32_t file_size
Definition apm.c:57
uint32_t unk1
Definition apm.c:59
uint32_t unk2
Definition apm.c:60
uint32_t magic
Definition apm.c:56
Definition apm.c:45
int32_t saved_l
Definition apm.c:52
int32_t predictor_r
Definition apm.c:47
int32_t has_saved
Definition apm.c:46
int32_t step_index_r
Definition apm.c:48
int32_t predictor_l
Definition apm.c:50
int32_t saved_r
Definition apm.c:49
int32_t step_index_l
Definition apm.c:51
int nb_channels
Number of channels in this layout.
This struct describes the properties of an encoded stream.
Definition codec_par.h:49
int extradata_size
Size of the extradata content in bytes.
Definition codec_par.h:75
int bits_per_coded_sample
The number of bits per sample in the codedwords.
Definition codec_par.h:113
AVChannelLayout ch_layout
The channel layout and number of channels.
Definition codec_par.h:207
int64_t bit_rate
The average bitrate of the encoded data (in bits per second).
Definition codec_par.h:99
enum AVMediaType codec_type
General type of the encoded data.
Definition codec_par.h:53
int block_align
The number of bytes per coded audio frame, required by some formats.
Definition codec_par.h:221
uint8_t * extradata
Extra binary data needed for initializing the decoder, codec-dependent.
Definition codec_par.h:71
enum AVCodecID codec_id
Specific type of the encoded data (the codec used).
Definition codec_par.h:57
int sample_rate
The number of audio samples per second.
Definition codec_par.h:213
Format I/O context.
Definition avformat.h:1333
This structure stores compressed data.
Definition packet.h:580
This structure contains the data a format has to probe a file.
Definition avformat.h:471
Stream structure.
Definition avformat.h:766
AVCodecParameters * codecpar
Codec parameters associated with this stream.
Definition avformat.h:789
int64_t duration
Decoding: duration of the stream, in stream time base.
Definition avformat.h:825
int64_t start_time
Decoding: pts of the first frame of the stream in presentation order, in stream time base.
Definition avformat.h:815
#define avpriv_request_sample(...)
#define av_log(a,...)