1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * LEGO Racers ALP (.tun & .pcm) (de)muxer 3cabdff1aSopenharmony_ci * 4cabdff1aSopenharmony_ci * Copyright (C) 2020 Zane van Iperen (zane@zanevaniperen.com) 5cabdff1aSopenharmony_ci * 6cabdff1aSopenharmony_ci * This file is part of FFmpeg. 7cabdff1aSopenharmony_ci * 8cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 9cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 10cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 11cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 12cabdff1aSopenharmony_ci * 13cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 14cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 15cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16cabdff1aSopenharmony_ci * Lesser General Public License for more details. 17cabdff1aSopenharmony_ci * 18cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 19cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 20cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21cabdff1aSopenharmony_ci */ 22cabdff1aSopenharmony_ci 23cabdff1aSopenharmony_ci#include "config_components.h" 24cabdff1aSopenharmony_ci 25cabdff1aSopenharmony_ci#include "libavutil/channel_layout.h" 26cabdff1aSopenharmony_ci#include "avformat.h" 27cabdff1aSopenharmony_ci#include "internal.h" 28cabdff1aSopenharmony_ci#include "rawenc.h" 29cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h" 30cabdff1aSopenharmony_ci#include "libavutil/internal.h" 31cabdff1aSopenharmony_ci#include "libavutil/opt.h" 32cabdff1aSopenharmony_ci 33cabdff1aSopenharmony_ci#define ALP_TAG MKTAG('A', 'L', 'P', ' ') 34cabdff1aSopenharmony_ci#define ALP_MAX_READ_SIZE 4096 35cabdff1aSopenharmony_ci 36cabdff1aSopenharmony_citypedef struct ALPHeader { 37cabdff1aSopenharmony_ci uint32_t magic; /*< Magic Number, {'A', 'L', 'P', ' '} */ 38cabdff1aSopenharmony_ci uint32_t header_size; /*< Header size (after this). */ 39cabdff1aSopenharmony_ci char adpcm[6]; /*< "ADPCM" */ 40cabdff1aSopenharmony_ci uint8_t unk1; /*< Unknown */ 41cabdff1aSopenharmony_ci uint8_t num_channels; /*< Channel Count. */ 42cabdff1aSopenharmony_ci uint32_t sample_rate; /*< Sample rate, only if header_size >= 12. */ 43cabdff1aSopenharmony_ci} ALPHeader; 44cabdff1aSopenharmony_ci 45cabdff1aSopenharmony_citypedef enum ALPType { 46cabdff1aSopenharmony_ci ALP_TYPE_AUTO = 0, /*< Autodetect based on file extension. */ 47cabdff1aSopenharmony_ci ALP_TYPE_TUN = 1, /*< Force a .TUN file. */ 48cabdff1aSopenharmony_ci ALP_TYPE_PCM = 2, /*< Force a .PCM file. */ 49cabdff1aSopenharmony_ci} ALPType; 50cabdff1aSopenharmony_ci 51cabdff1aSopenharmony_citypedef struct ALPMuxContext { 52cabdff1aSopenharmony_ci const AVClass *class; 53cabdff1aSopenharmony_ci ALPType type; 54cabdff1aSopenharmony_ci} ALPMuxContext; 55cabdff1aSopenharmony_ci 56cabdff1aSopenharmony_ci#if CONFIG_ALP_DEMUXER 57cabdff1aSopenharmony_cistatic int alp_probe(const AVProbeData *p) 58cabdff1aSopenharmony_ci{ 59cabdff1aSopenharmony_ci uint32_t i; 60cabdff1aSopenharmony_ci 61cabdff1aSopenharmony_ci if (AV_RL32(p->buf) != ALP_TAG) 62cabdff1aSopenharmony_ci return 0; 63cabdff1aSopenharmony_ci 64cabdff1aSopenharmony_ci /* Only allowed header sizes are 8 and 12. */ 65cabdff1aSopenharmony_ci i = AV_RL32(p->buf + 4); 66cabdff1aSopenharmony_ci if (i != 8 && i != 12) 67cabdff1aSopenharmony_ci return 0; 68cabdff1aSopenharmony_ci 69cabdff1aSopenharmony_ci if (strncmp("ADPCM", p->buf + 8, 6) != 0) 70cabdff1aSopenharmony_ci return 0; 71cabdff1aSopenharmony_ci 72cabdff1aSopenharmony_ci return AVPROBE_SCORE_MAX - 1; 73cabdff1aSopenharmony_ci} 74cabdff1aSopenharmony_ci 75cabdff1aSopenharmony_cistatic int alp_read_header(AVFormatContext *s) 76cabdff1aSopenharmony_ci{ 77cabdff1aSopenharmony_ci int ret; 78cabdff1aSopenharmony_ci AVStream *st; 79cabdff1aSopenharmony_ci ALPHeader *hdr = s->priv_data; 80cabdff1aSopenharmony_ci AVCodecParameters *par; 81cabdff1aSopenharmony_ci 82cabdff1aSopenharmony_ci if ((hdr->magic = avio_rl32(s->pb)) != ALP_TAG) 83cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 84cabdff1aSopenharmony_ci 85cabdff1aSopenharmony_ci hdr->header_size = avio_rl32(s->pb); 86cabdff1aSopenharmony_ci 87cabdff1aSopenharmony_ci if (hdr->header_size != 8 && hdr->header_size != 12) { 88cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 89cabdff1aSopenharmony_ci } 90cabdff1aSopenharmony_ci 91cabdff1aSopenharmony_ci if ((ret = avio_read(s->pb, hdr->adpcm, sizeof(hdr->adpcm))) < 0) 92cabdff1aSopenharmony_ci return ret; 93cabdff1aSopenharmony_ci else if (ret != sizeof(hdr->adpcm)) 94cabdff1aSopenharmony_ci return AVERROR(EIO); 95cabdff1aSopenharmony_ci 96cabdff1aSopenharmony_ci if (strncmp("ADPCM", hdr->adpcm, sizeof(hdr->adpcm)) != 0) 97cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 98cabdff1aSopenharmony_ci 99cabdff1aSopenharmony_ci hdr->unk1 = avio_r8(s->pb); 100cabdff1aSopenharmony_ci hdr->num_channels = avio_r8(s->pb); 101cabdff1aSopenharmony_ci 102cabdff1aSopenharmony_ci if (hdr->header_size == 8) { 103cabdff1aSopenharmony_ci /* .TUN music file */ 104cabdff1aSopenharmony_ci hdr->sample_rate = 22050; 105cabdff1aSopenharmony_ci 106cabdff1aSopenharmony_ci } else { 107cabdff1aSopenharmony_ci /* .PCM sound file */ 108cabdff1aSopenharmony_ci hdr->sample_rate = avio_rl32(s->pb); 109cabdff1aSopenharmony_ci } 110cabdff1aSopenharmony_ci 111cabdff1aSopenharmony_ci if (hdr->sample_rate > 44100) { 112cabdff1aSopenharmony_ci avpriv_request_sample(s, "Sample Rate > 44100"); 113cabdff1aSopenharmony_ci return AVERROR_PATCHWELCOME; 114cabdff1aSopenharmony_ci } 115cabdff1aSopenharmony_ci 116cabdff1aSopenharmony_ci if (!(st = avformat_new_stream(s, NULL))) 117cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 118cabdff1aSopenharmony_ci 119cabdff1aSopenharmony_ci par = st->codecpar; 120cabdff1aSopenharmony_ci par->codec_type = AVMEDIA_TYPE_AUDIO; 121cabdff1aSopenharmony_ci par->codec_id = AV_CODEC_ID_ADPCM_IMA_ALP; 122cabdff1aSopenharmony_ci par->format = AV_SAMPLE_FMT_S16; 123cabdff1aSopenharmony_ci par->sample_rate = hdr->sample_rate; 124cabdff1aSopenharmony_ci 125cabdff1aSopenharmony_ci if (hdr->num_channels > 2 || hdr->num_channels == 0) 126cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 127cabdff1aSopenharmony_ci 128cabdff1aSopenharmony_ci av_channel_layout_default(&par->ch_layout, hdr->num_channels); 129cabdff1aSopenharmony_ci par->bits_per_coded_sample = 4; 130cabdff1aSopenharmony_ci par->block_align = 1; 131cabdff1aSopenharmony_ci par->bit_rate = par->ch_layout.nb_channels * 132cabdff1aSopenharmony_ci par->sample_rate * 133cabdff1aSopenharmony_ci par->bits_per_coded_sample; 134cabdff1aSopenharmony_ci 135cabdff1aSopenharmony_ci avpriv_set_pts_info(st, 64, 1, par->sample_rate); 136cabdff1aSopenharmony_ci return 0; 137cabdff1aSopenharmony_ci} 138cabdff1aSopenharmony_ci 139cabdff1aSopenharmony_cistatic int alp_read_packet(AVFormatContext *s, AVPacket *pkt) 140cabdff1aSopenharmony_ci{ 141cabdff1aSopenharmony_ci int ret; 142cabdff1aSopenharmony_ci AVCodecParameters *par = s->streams[0]->codecpar; 143cabdff1aSopenharmony_ci 144cabdff1aSopenharmony_ci if ((ret = av_get_packet(s->pb, pkt, ALP_MAX_READ_SIZE)) < 0) 145cabdff1aSopenharmony_ci return ret; 146cabdff1aSopenharmony_ci 147cabdff1aSopenharmony_ci pkt->flags &= ~AV_PKT_FLAG_CORRUPT; 148cabdff1aSopenharmony_ci pkt->stream_index = 0; 149cabdff1aSopenharmony_ci pkt->duration = ret * 2 / par->ch_layout.nb_channels; 150cabdff1aSopenharmony_ci 151cabdff1aSopenharmony_ci return 0; 152cabdff1aSopenharmony_ci} 153cabdff1aSopenharmony_ci 154cabdff1aSopenharmony_cistatic int alp_seek(AVFormatContext *s, int stream_index, 155cabdff1aSopenharmony_ci int64_t pts, int flags) 156cabdff1aSopenharmony_ci{ 157cabdff1aSopenharmony_ci const ALPHeader *hdr = s->priv_data; 158cabdff1aSopenharmony_ci 159cabdff1aSopenharmony_ci if (pts != 0) 160cabdff1aSopenharmony_ci return AVERROR(EINVAL); 161cabdff1aSopenharmony_ci 162cabdff1aSopenharmony_ci return avio_seek(s->pb, hdr->header_size + 8, SEEK_SET); 163cabdff1aSopenharmony_ci} 164cabdff1aSopenharmony_ci 165cabdff1aSopenharmony_ciconst AVInputFormat ff_alp_demuxer = { 166cabdff1aSopenharmony_ci .name = "alp", 167cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("LEGO Racers ALP"), 168cabdff1aSopenharmony_ci .priv_data_size = sizeof(ALPHeader), 169cabdff1aSopenharmony_ci .read_probe = alp_probe, 170cabdff1aSopenharmony_ci .read_header = alp_read_header, 171cabdff1aSopenharmony_ci .read_packet = alp_read_packet, 172cabdff1aSopenharmony_ci .read_seek = alp_seek, 173cabdff1aSopenharmony_ci}; 174cabdff1aSopenharmony_ci#endif 175cabdff1aSopenharmony_ci 176cabdff1aSopenharmony_ci#if CONFIG_ALP_MUXER 177cabdff1aSopenharmony_ci 178cabdff1aSopenharmony_cistatic int alp_write_init(AVFormatContext *s) 179cabdff1aSopenharmony_ci{ 180cabdff1aSopenharmony_ci ALPMuxContext *alp = s->priv_data; 181cabdff1aSopenharmony_ci AVCodecParameters *par; 182cabdff1aSopenharmony_ci 183cabdff1aSopenharmony_ci if (alp->type == ALP_TYPE_AUTO) { 184cabdff1aSopenharmony_ci if (av_match_ext(s->url, "pcm")) 185cabdff1aSopenharmony_ci alp->type = ALP_TYPE_PCM; 186cabdff1aSopenharmony_ci else 187cabdff1aSopenharmony_ci alp->type = ALP_TYPE_TUN; 188cabdff1aSopenharmony_ci } 189cabdff1aSopenharmony_ci 190cabdff1aSopenharmony_ci if (s->nb_streams != 1) { 191cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Too many streams\n"); 192cabdff1aSopenharmony_ci return AVERROR(EINVAL); 193cabdff1aSopenharmony_ci } 194cabdff1aSopenharmony_ci 195cabdff1aSopenharmony_ci par = s->streams[0]->codecpar; 196cabdff1aSopenharmony_ci 197cabdff1aSopenharmony_ci if (par->codec_id != AV_CODEC_ID_ADPCM_IMA_ALP) { 198cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "%s codec not supported\n", 199cabdff1aSopenharmony_ci avcodec_get_name(par->codec_id)); 200cabdff1aSopenharmony_ci return AVERROR(EINVAL); 201cabdff1aSopenharmony_ci } 202cabdff1aSopenharmony_ci 203cabdff1aSopenharmony_ci if (par->ch_layout.nb_channels > 2) { 204cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "A maximum of 2 channels are supported\n"); 205cabdff1aSopenharmony_ci return AVERROR(EINVAL); 206cabdff1aSopenharmony_ci } 207cabdff1aSopenharmony_ci 208cabdff1aSopenharmony_ci if (par->sample_rate > 44100) { 209cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Sample rate too large\n"); 210cabdff1aSopenharmony_ci return AVERROR(EINVAL); 211cabdff1aSopenharmony_ci } 212cabdff1aSopenharmony_ci 213cabdff1aSopenharmony_ci if (alp->type == ALP_TYPE_TUN && par->sample_rate != 22050) { 214cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Sample rate must be 22050 for TUN files\n"); 215cabdff1aSopenharmony_ci return AVERROR(EINVAL); 216cabdff1aSopenharmony_ci } 217cabdff1aSopenharmony_ci return 0; 218cabdff1aSopenharmony_ci} 219cabdff1aSopenharmony_ci 220cabdff1aSopenharmony_cistatic int alp_write_header(AVFormatContext *s) 221cabdff1aSopenharmony_ci{ 222cabdff1aSopenharmony_ci ALPMuxContext *alp = s->priv_data; 223cabdff1aSopenharmony_ci AVCodecParameters *par = s->streams[0]->codecpar; 224cabdff1aSopenharmony_ci 225cabdff1aSopenharmony_ci avio_wl32(s->pb, ALP_TAG); 226cabdff1aSopenharmony_ci avio_wl32(s->pb, alp->type == ALP_TYPE_PCM ? 12 : 8); 227cabdff1aSopenharmony_ci avio_write(s->pb, "ADPCM", 6); 228cabdff1aSopenharmony_ci avio_w8(s->pb, 0); 229cabdff1aSopenharmony_ci avio_w8(s->pb, par->ch_layout.nb_channels); 230cabdff1aSopenharmony_ci if (alp->type == ALP_TYPE_PCM) 231cabdff1aSopenharmony_ci avio_wl32(s->pb, par->sample_rate); 232cabdff1aSopenharmony_ci 233cabdff1aSopenharmony_ci return 0; 234cabdff1aSopenharmony_ci} 235cabdff1aSopenharmony_ci 236cabdff1aSopenharmony_cienum { AE = AV_OPT_FLAG_AUDIO_PARAM | AV_OPT_FLAG_ENCODING_PARAM }; 237cabdff1aSopenharmony_ci 238cabdff1aSopenharmony_cistatic const AVOption alp_options[] = { 239cabdff1aSopenharmony_ci { 240cabdff1aSopenharmony_ci .name = "type", 241cabdff1aSopenharmony_ci .help = "set file type", 242cabdff1aSopenharmony_ci .offset = offsetof(ALPMuxContext, type), 243cabdff1aSopenharmony_ci .type = AV_OPT_TYPE_INT, 244cabdff1aSopenharmony_ci .default_val = {.i64 = ALP_TYPE_AUTO}, 245cabdff1aSopenharmony_ci .min = ALP_TYPE_AUTO, 246cabdff1aSopenharmony_ci .max = ALP_TYPE_PCM, 247cabdff1aSopenharmony_ci .flags = AE, 248cabdff1aSopenharmony_ci .unit = "type", 249cabdff1aSopenharmony_ci }, 250cabdff1aSopenharmony_ci { 251cabdff1aSopenharmony_ci .name = "auto", 252cabdff1aSopenharmony_ci .help = "autodetect based on file extension", 253cabdff1aSopenharmony_ci .offset = 0, 254cabdff1aSopenharmony_ci .type = AV_OPT_TYPE_CONST, 255cabdff1aSopenharmony_ci .default_val = {.i64 = ALP_TYPE_AUTO}, 256cabdff1aSopenharmony_ci .min = 0, 257cabdff1aSopenharmony_ci .max = 0, 258cabdff1aSopenharmony_ci .flags = AE, 259cabdff1aSopenharmony_ci .unit = "type" 260cabdff1aSopenharmony_ci }, 261cabdff1aSopenharmony_ci { 262cabdff1aSopenharmony_ci .name = "tun", 263cabdff1aSopenharmony_ci .help = "force .tun, used for music", 264cabdff1aSopenharmony_ci .offset = 0, 265cabdff1aSopenharmony_ci .type = AV_OPT_TYPE_CONST, 266cabdff1aSopenharmony_ci .default_val = {.i64 = ALP_TYPE_TUN}, 267cabdff1aSopenharmony_ci .min = 0, 268cabdff1aSopenharmony_ci .max = 0, 269cabdff1aSopenharmony_ci .flags = AE, 270cabdff1aSopenharmony_ci .unit = "type" 271cabdff1aSopenharmony_ci }, 272cabdff1aSopenharmony_ci { 273cabdff1aSopenharmony_ci .name = "pcm", 274cabdff1aSopenharmony_ci .help = "force .pcm, used for sfx", 275cabdff1aSopenharmony_ci .offset = 0, 276cabdff1aSopenharmony_ci .type = AV_OPT_TYPE_CONST, 277cabdff1aSopenharmony_ci .default_val = {.i64 = ALP_TYPE_PCM}, 278cabdff1aSopenharmony_ci .min = 0, 279cabdff1aSopenharmony_ci .max = 0, 280cabdff1aSopenharmony_ci .flags = AE, 281cabdff1aSopenharmony_ci .unit = "type" 282cabdff1aSopenharmony_ci }, 283cabdff1aSopenharmony_ci { NULL } 284cabdff1aSopenharmony_ci}; 285cabdff1aSopenharmony_ci 286cabdff1aSopenharmony_cistatic const AVClass alp_muxer_class = { 287cabdff1aSopenharmony_ci .class_name = "alp", 288cabdff1aSopenharmony_ci .item_name = av_default_item_name, 289cabdff1aSopenharmony_ci .option = alp_options, 290cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT 291cabdff1aSopenharmony_ci}; 292cabdff1aSopenharmony_ci 293cabdff1aSopenharmony_ciconst AVOutputFormat ff_alp_muxer = { 294cabdff1aSopenharmony_ci .name = "alp", 295cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("LEGO Racers ALP"), 296cabdff1aSopenharmony_ci .extensions = "tun,pcm", 297cabdff1aSopenharmony_ci .audio_codec = AV_CODEC_ID_ADPCM_IMA_ALP, 298cabdff1aSopenharmony_ci .video_codec = AV_CODEC_ID_NONE, 299cabdff1aSopenharmony_ci .init = alp_write_init, 300cabdff1aSopenharmony_ci .write_header = alp_write_header, 301cabdff1aSopenharmony_ci .write_packet = ff_raw_write_packet, 302cabdff1aSopenharmony_ci .priv_class = &alp_muxer_class, 303cabdff1aSopenharmony_ci .priv_data_size = sizeof(ALPMuxContext) 304cabdff1aSopenharmony_ci}; 305cabdff1aSopenharmony_ci#endif 306