2 * LEGO Racers ALP (.tun & .pcm) (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"
25 #include "libavutil/channel_layout.h"
27 #include "avio_internal.h"
32 #include "libavutil/intreadwrite.h"
33 #include "libavutil/internal.h"
34 #include "libavutil/opt.h"
36 #define ALP_TAG MKTAG('A', 'L', 'P', ' ')
37 #define ALP_MAX_READ_SIZE 4096
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. */
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. */
54 typedef struct ALPMuxContext
{
59 #if CONFIG_ALP_DEMUXER
60 static int alp_probe(const AVProbeData
*p
)
64 if (AV_RL32(p
->buf
) != ALP_TAG
)
67 /* Only allowed header sizes are 8 and 12. */
68 i
= AV_RL32(p
->buf
+ 4);
69 if (i
!= 8 && i
!= 12)
72 if (strncmp("ADPCM", p
->buf
+ 8, 6) != 0)
75 return AVPROBE_SCORE_MAX
- 1;
78 static int alp_read_header(AVFormatContext
*s
)
82 ALPHeader
*hdr
= s
->priv_data
;
83 AVCodecParameters
*par
;
85 if ((hdr
->magic
= avio_rl32(s
->pb
)) != ALP_TAG
)
86 return AVERROR_INVALIDDATA
;
88 hdr
->header_size
= avio_rl32(s
->pb
);
90 if (hdr
->header_size
!= 8 && hdr
->header_size
!= 12) {
91 return AVERROR_INVALIDDATA
;
94 if ((ret
= ffio_read_size(s
->pb
, hdr
->adpcm
, sizeof(hdr
->adpcm
))) < 0)
97 if (strncmp("ADPCM", hdr
->adpcm
, sizeof(hdr
->adpcm
)) != 0)
98 return AVERROR_INVALIDDATA
;
100 hdr
->unk1
= avio_r8(s
->pb
);
101 hdr
->num_channels
= avio_r8(s
->pb
);
103 if (hdr
->header_size
== 8) {
104 /* .TUN music file */
105 hdr
->sample_rate
= 22050;
108 /* .PCM sound file */
109 hdr
->sample_rate
= avio_rl32(s
->pb
);
112 if (hdr
->sample_rate
> 44100) {
113 avpriv_request_sample(s
, "Sample Rate > 44100");
114 return AVERROR_PATCHWELCOME
;
117 if (!(st
= avformat_new_stream(s
, NULL
)))
118 return AVERROR(ENOMEM
);
121 par
->codec_type
= AVMEDIA_TYPE_AUDIO
;
122 par
->codec_id
= AV_CODEC_ID_ADPCM_IMA_ALP
;
123 par
->format
= AV_SAMPLE_FMT_S16
;
124 par
->sample_rate
= hdr
->sample_rate
;
126 if (hdr
->num_channels
> 2 || hdr
->num_channels
== 0)
127 return AVERROR_INVALIDDATA
;
129 av_channel_layout_default(&par
->ch_layout
, hdr
->num_channels
);
130 par
->bits_per_coded_sample
= 4;
131 par
->block_align
= 1;
132 par
->bit_rate
= par
->ch_layout
.nb_channels
*
134 par
->bits_per_coded_sample
;
136 avpriv_set_pts_info(st
, 64, 1, par
->sample_rate
);
140 static int alp_read_packet(AVFormatContext
*s
, AVPacket
*pkt
)
143 AVCodecParameters
*par
= s
->streams
[0]->codecpar
;
145 if ((ret
= av_get_packet(s
->pb
, pkt
, ALP_MAX_READ_SIZE
)) < 0)
148 pkt
->flags
&= ~AV_PKT_FLAG_CORRUPT
;
149 pkt
->stream_index
= 0;
150 pkt
->duration
= ret
* 2 / par
->ch_layout
.nb_channels
;
155 static int alp_seek(AVFormatContext
*s
, int stream_index
,
156 int64_t pts
, int flags
)
158 const ALPHeader
*hdr
= s
->priv_data
;
161 return AVERROR(EINVAL
);
163 return avio_seek(s
->pb
, hdr
->header_size
+ 8, SEEK_SET
);
166 const FFInputFormat ff_alp_demuxer
= {
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
,
179 static int alp_write_init(AVFormatContext
*s
)
181 ALPMuxContext
*alp
= s
->priv_data
;
182 AVCodecParameters
*par
;
184 if (alp
->type
== ALP_TYPE_AUTO
) {
185 if (av_match_ext(s
->url
, "pcm"))
186 alp
->type
= ALP_TYPE_PCM
;
188 alp
->type
= ALP_TYPE_TUN
;
191 par
= s
->streams
[0]->codecpar
;
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
);
198 if (par
->sample_rate
> 44100) {
199 av_log(s
, AV_LOG_ERROR
, "Sample rate too large\n");
200 return AVERROR(EINVAL
);
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
);
210 static int alp_write_header(AVFormatContext
*s
)
212 ALPMuxContext
*alp
= s
->priv_data
;
213 AVCodecParameters
*par
= s
->streams
[0]->codecpar
;
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);
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
);
226 enum { AE
= AV_OPT_FLAG_AUDIO_PARAM
| AV_OPT_FLAG_ENCODING_PARAM
};
228 static const AVOption alp_options
[] = {
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
,
242 .help
= "autodetect based on file extension",
244 .type
= AV_OPT_TYPE_CONST
,
245 .default_val
= {.i64
= ALP_TYPE_AUTO
},
253 .help
= "force .tun, used for music",
255 .type
= AV_OPT_TYPE_CONST
,
256 .default_val
= {.i64
= ALP_TYPE_TUN
},
264 .help
= "force .pcm, used for sfx",
266 .type
= AV_OPT_TYPE_CONST
,
267 .default_val
= {.i64
= ALP_TYPE_PCM
},
276 static const AVClass alp_muxer_class
= {
278 .item_name
= av_default_item_name
,
279 .option
= alp_options
,
280 .version
= LIBAVUTIL_VERSION_INT
283 const FFOutputFormat ff_alp_muxer
= {
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
)