/src/ffmpeg/libavformat/alp.c
Line | Count | Source |
1 | | /* |
2 | | * LEGO Racers ALP (.tun & .pcm) (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 "libavutil/channel_layout.h" |
26 | | #include "avformat.h" |
27 | | #include "avio_internal.h" |
28 | | #include "demux.h" |
29 | | #include "internal.h" |
30 | | #include "mux.h" |
31 | | #include "rawenc.h" |
32 | | #include "libavutil/intreadwrite.h" |
33 | | #include "libavutil/internal.h" |
34 | | #include "libavutil/opt.h" |
35 | | |
36 | 959k | #define ALP_TAG MKTAG('A', 'L', 'P', ' ') |
37 | 12.0k | #define ALP_MAX_READ_SIZE 4096 |
38 | | |
39 | | typedef struct ALPHeader { |
40 | | uint32_t magic; /*< Magic Number, {'A', 'L', 'P', ' '} */ |
41 | | uint32_t header_size; /*< Header size (after this). */ |
42 | | char adpcm[6]; /*< "ADPCM" */ |
43 | | uint8_t unk1; /*< Unknown */ |
44 | | uint8_t num_channels; /*< Channel Count. */ |
45 | | uint32_t sample_rate; /*< Sample rate, only if header_size >= 12. */ |
46 | | } ALPHeader; |
47 | | |
48 | | typedef enum ALPType { |
49 | | ALP_TYPE_AUTO = 0, /*< Autodetect based on file extension. */ |
50 | | ALP_TYPE_TUN = 1, /*< Force a .TUN file. */ |
51 | | ALP_TYPE_PCM = 2, /*< Force a .PCM file. */ |
52 | | } ALPType; |
53 | | |
54 | | typedef struct ALPMuxContext { |
55 | | const AVClass *class; |
56 | | ALPType type; |
57 | | } ALPMuxContext; |
58 | | |
59 | | #if CONFIG_ALP_DEMUXER |
60 | | static int alp_probe(const AVProbeData *p) |
61 | 958k | { |
62 | 958k | uint32_t i; |
63 | | |
64 | 958k | if (AV_RL32(p->buf) != ALP_TAG) |
65 | 957k | return 0; |
66 | | |
67 | | /* Only allowed header sizes are 8 and 12. */ |
68 | 544 | i = AV_RL32(p->buf + 4); |
69 | 544 | if (i != 8 && i != 12) |
70 | 268 | return 0; |
71 | | |
72 | 276 | if (strncmp("ADPCM", p->buf + 8, 6) != 0) |
73 | 226 | return 0; |
74 | | |
75 | 50 | return AVPROBE_SCORE_MAX - 1; |
76 | 276 | } |
77 | | |
78 | | static int alp_read_header(AVFormatContext *s) |
79 | 1.24k | { |
80 | 1.24k | int ret; |
81 | 1.24k | AVStream *st; |
82 | 1.24k | ALPHeader *hdr = s->priv_data; |
83 | 1.24k | AVCodecParameters *par; |
84 | | |
85 | 1.24k | if ((hdr->magic = avio_rl32(s->pb)) != ALP_TAG) |
86 | 195 | return AVERROR_INVALIDDATA; |
87 | | |
88 | 1.04k | hdr->header_size = avio_rl32(s->pb); |
89 | | |
90 | 1.04k | if (hdr->header_size != 8 && hdr->header_size != 12) { |
91 | 62 | return AVERROR_INVALIDDATA; |
92 | 62 | } |
93 | | |
94 | 984 | if ((ret = ffio_read_size(s->pb, hdr->adpcm, sizeof(hdr->adpcm))) < 0) |
95 | 15 | return ret; |
96 | | |
97 | 969 | if (strncmp("ADPCM", hdr->adpcm, sizeof(hdr->adpcm)) != 0) |
98 | 51 | return AVERROR_INVALIDDATA; |
99 | | |
100 | 918 | hdr->unk1 = avio_r8(s->pb); |
101 | 918 | hdr->num_channels = avio_r8(s->pb); |
102 | | |
103 | 918 | if (hdr->header_size == 8) { |
104 | | /* .TUN music file */ |
105 | 399 | hdr->sample_rate = 22050; |
106 | | |
107 | 519 | } else { |
108 | | /* .PCM sound file */ |
109 | 519 | hdr->sample_rate = avio_rl32(s->pb); |
110 | 519 | } |
111 | | |
112 | 918 | if (hdr->sample_rate > 44100) { |
113 | 28 | avpriv_request_sample(s, "Sample Rate > 44100"); |
114 | 28 | return AVERROR_PATCHWELCOME; |
115 | 28 | } |
116 | | |
117 | 890 | if (!(st = avformat_new_stream(s, NULL))) |
118 | 0 | return AVERROR(ENOMEM); |
119 | | |
120 | 890 | par = st->codecpar; |
121 | 890 | par->codec_type = AVMEDIA_TYPE_AUDIO; |
122 | 890 | par->codec_id = AV_CODEC_ID_ADPCM_IMA_ALP; |
123 | 890 | par->format = AV_SAMPLE_FMT_S16; |
124 | 890 | par->sample_rate = hdr->sample_rate; |
125 | | |
126 | 890 | if (hdr->num_channels > 2 || hdr->num_channels == 0) |
127 | 22 | return AVERROR_INVALIDDATA; |
128 | | |
129 | 868 | av_channel_layout_default(&par->ch_layout, hdr->num_channels); |
130 | 868 | par->bits_per_coded_sample = 4; |
131 | 868 | par->block_align = 1; |
132 | 868 | par->bit_rate = par->ch_layout.nb_channels * |
133 | 868 | par->sample_rate * |
134 | 868 | par->bits_per_coded_sample; |
135 | | |
136 | 868 | avpriv_set_pts_info(st, 64, 1, par->sample_rate); |
137 | 868 | return 0; |
138 | 890 | } |
139 | | |
140 | | static int alp_read_packet(AVFormatContext *s, AVPacket *pkt) |
141 | 12.0k | { |
142 | 12.0k | int ret; |
143 | 12.0k | AVCodecParameters *par = s->streams[0]->codecpar; |
144 | | |
145 | 12.0k | if ((ret = av_get_packet(s->pb, pkt, ALP_MAX_READ_SIZE)) < 0) |
146 | 1.21k | return ret; |
147 | | |
148 | 10.8k | pkt->flags &= ~AV_PKT_FLAG_CORRUPT; |
149 | 10.8k | pkt->stream_index = 0; |
150 | 10.8k | pkt->duration = ret * 2 / par->ch_layout.nb_channels; |
151 | | |
152 | 10.8k | return 0; |
153 | 12.0k | } |
154 | | |
155 | | static int alp_seek(AVFormatContext *s, int stream_index, |
156 | | int64_t pts, int flags) |
157 | 0 | { |
158 | 0 | const ALPHeader *hdr = s->priv_data; |
159 | |
|
160 | 0 | if (pts != 0) |
161 | 0 | return AVERROR(EINVAL); |
162 | | |
163 | 0 | return avio_seek(s->pb, hdr->header_size + 8, SEEK_SET); |
164 | 0 | } |
165 | | |
166 | | const FFInputFormat ff_alp_demuxer = { |
167 | | .p.name = "alp", |
168 | | .p.long_name = NULL_IF_CONFIG_SMALL("LEGO Racers ALP"), |
169 | | .priv_data_size = sizeof(ALPHeader), |
170 | | .read_probe = alp_probe, |
171 | | .read_header = alp_read_header, |
172 | | .read_packet = alp_read_packet, |
173 | | .read_seek = alp_seek, |
174 | | }; |
175 | | #endif |
176 | | |
177 | | #if CONFIG_ALP_MUXER |
178 | | |
179 | | static int alp_write_init(AVFormatContext *s) |
180 | | { |
181 | | ALPMuxContext *alp = s->priv_data; |
182 | | AVCodecParameters *par; |
183 | | |
184 | | if (alp->type == ALP_TYPE_AUTO) { |
185 | | if (av_match_ext(s->url, "pcm")) |
186 | | alp->type = ALP_TYPE_PCM; |
187 | | else |
188 | | alp->type = ALP_TYPE_TUN; |
189 | | } |
190 | | |
191 | | par = s->streams[0]->codecpar; |
192 | | |
193 | | if (par->ch_layout.nb_channels > 2) { |
194 | | av_log(s, AV_LOG_ERROR, "A maximum of 2 channels are supported\n"); |
195 | | return AVERROR(EINVAL); |
196 | | } |
197 | | |
198 | | if (par->sample_rate > 44100) { |
199 | | av_log(s, AV_LOG_ERROR, "Sample rate too large\n"); |
200 | | return AVERROR(EINVAL); |
201 | | } |
202 | | |
203 | | if (alp->type == ALP_TYPE_TUN && par->sample_rate != 22050) { |
204 | | av_log(s, AV_LOG_ERROR, "Sample rate must be 22050 for TUN files\n"); |
205 | | return AVERROR(EINVAL); |
206 | | } |
207 | | return 0; |
208 | | } |
209 | | |
210 | | static int alp_write_header(AVFormatContext *s) |
211 | | { |
212 | | ALPMuxContext *alp = s->priv_data; |
213 | | AVCodecParameters *par = s->streams[0]->codecpar; |
214 | | |
215 | | avio_wl32(s->pb, ALP_TAG); |
216 | | avio_wl32(s->pb, alp->type == ALP_TYPE_PCM ? 12 : 8); |
217 | | avio_write(s->pb, "ADPCM", 6); |
218 | | avio_w8(s->pb, 0); |
219 | | avio_w8(s->pb, par->ch_layout.nb_channels); |
220 | | if (alp->type == ALP_TYPE_PCM) |
221 | | avio_wl32(s->pb, par->sample_rate); |
222 | | |
223 | | return 0; |
224 | | } |
225 | | |
226 | | enum { AE = AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }; |
227 | | |
228 | | static const AVOption alp_options[] = { |
229 | | { |
230 | | .name = "type", |
231 | | .help = "set file type", |
232 | | .offset = offsetof(ALPMuxContext, type), |
233 | | .type = AV_OPT_TYPE_INT, |
234 | | .default_val = {.i64 = ALP_TYPE_AUTO}, |
235 | | .min = ALP_TYPE_AUTO, |
236 | | .max = ALP_TYPE_PCM, |
237 | | .flags = AE, |
238 | | .unit = "type", |
239 | | }, |
240 | | { |
241 | | .name = "auto", |
242 | | .help = "autodetect based on file extension", |
243 | | .offset = 0, |
244 | | .type = AV_OPT_TYPE_CONST, |
245 | | .default_val = {.i64 = ALP_TYPE_AUTO}, |
246 | | .min = 0, |
247 | | .max = 0, |
248 | | .flags = AE, |
249 | | .unit = "type" |
250 | | }, |
251 | | { |
252 | | .name = "tun", |
253 | | .help = "force .tun, used for music", |
254 | | .offset = 0, |
255 | | .type = AV_OPT_TYPE_CONST, |
256 | | .default_val = {.i64 = ALP_TYPE_TUN}, |
257 | | .min = 0, |
258 | | .max = 0, |
259 | | .flags = AE, |
260 | | .unit = "type" |
261 | | }, |
262 | | { |
263 | | .name = "pcm", |
264 | | .help = "force .pcm, used for sfx", |
265 | | .offset = 0, |
266 | | .type = AV_OPT_TYPE_CONST, |
267 | | .default_val = {.i64 = ALP_TYPE_PCM}, |
268 | | .min = 0, |
269 | | .max = 0, |
270 | | .flags = AE, |
271 | | .unit = "type" |
272 | | }, |
273 | | { NULL } |
274 | | }; |
275 | | |
276 | | static const AVClass alp_muxer_class = { |
277 | | .class_name = "alp", |
278 | | .item_name = av_default_item_name, |
279 | | .option = alp_options, |
280 | | .version = LIBAVUTIL_VERSION_INT |
281 | | }; |
282 | | |
283 | | const FFOutputFormat ff_alp_muxer = { |
284 | | .p.name = "alp", |
285 | | .p.long_name = NULL_IF_CONFIG_SMALL("LEGO Racers ALP"), |
286 | | .p.extensions = "tun,pcm", |
287 | | .p.audio_codec = AV_CODEC_ID_ADPCM_IMA_ALP, |
288 | | .p.video_codec = AV_CODEC_ID_NONE, |
289 | | .p.subtitle_codec = AV_CODEC_ID_NONE, |
290 | | .p.priv_class = &alp_muxer_class, |
291 | | .flags_internal = FF_OFMT_FLAG_MAX_ONE_OF_EACH | |
292 | | FF_OFMT_FLAG_ONLY_DEFAULT_CODECS, |
293 | | .init = alp_write_init, |
294 | | .write_header = alp_write_header, |
295 | | .write_packet = ff_raw_write_packet, |
296 | | .priv_data_size = sizeof(ALPMuxContext) |
297 | | }; |
298 | | #endif |