1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * Rayman 2 APM (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 "avformat.h" 26cabdff1aSopenharmony_ci#include "internal.h" 27cabdff1aSopenharmony_ci#include "rawenc.h" 28cabdff1aSopenharmony_ci#include "libavutil/channel_layout.h" 29cabdff1aSopenharmony_ci#include "libavutil/internal.h" 30cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h" 31cabdff1aSopenharmony_ci 32cabdff1aSopenharmony_ci#define APM_FILE_HEADER_SIZE 20 33cabdff1aSopenharmony_ci#define APM_FILE_EXTRADATA_SIZE 80 34cabdff1aSopenharmony_ci#define APM_EXTRADATA_SIZE 28 35cabdff1aSopenharmony_ci 36cabdff1aSopenharmony_ci#define APM_MAX_READ_SIZE 4096 37cabdff1aSopenharmony_ci 38cabdff1aSopenharmony_ci#define APM_TAG_CODEC 0x2000 39cabdff1aSopenharmony_ci#define APM_TAG_VS12 MKTAG('v', 's', '1', '2') 40cabdff1aSopenharmony_ci#define APM_TAG_DATA MKTAG('D', 'A', 'T', 'A') 41cabdff1aSopenharmony_ci 42cabdff1aSopenharmony_citypedef struct APMState { 43cabdff1aSopenharmony_ci int32_t has_saved; 44cabdff1aSopenharmony_ci int32_t predictor_r; 45cabdff1aSopenharmony_ci int32_t step_index_r; 46cabdff1aSopenharmony_ci int32_t saved_r; 47cabdff1aSopenharmony_ci int32_t predictor_l; 48cabdff1aSopenharmony_ci int32_t step_index_l; 49cabdff1aSopenharmony_ci int32_t saved_l; 50cabdff1aSopenharmony_ci} APMState; 51cabdff1aSopenharmony_ci 52cabdff1aSopenharmony_citypedef struct APMExtraData { 53cabdff1aSopenharmony_ci uint32_t magic; 54cabdff1aSopenharmony_ci uint32_t file_size; 55cabdff1aSopenharmony_ci uint32_t data_size; 56cabdff1aSopenharmony_ci uint32_t unk1; 57cabdff1aSopenharmony_ci uint32_t unk2; 58cabdff1aSopenharmony_ci APMState state; 59cabdff1aSopenharmony_ci uint32_t unk3[7]; 60cabdff1aSopenharmony_ci uint32_t data; 61cabdff1aSopenharmony_ci} APMExtraData; 62cabdff1aSopenharmony_ci 63cabdff1aSopenharmony_ci#if CONFIG_APM_DEMUXER 64cabdff1aSopenharmony_cistatic void apm_parse_extradata(APMExtraData *ext, const uint8_t *buf) 65cabdff1aSopenharmony_ci{ 66cabdff1aSopenharmony_ci ext->magic = AV_RL32(buf + 0); 67cabdff1aSopenharmony_ci ext->file_size = AV_RL32(buf + 4); 68cabdff1aSopenharmony_ci ext->data_size = AV_RL32(buf + 8); 69cabdff1aSopenharmony_ci ext->unk1 = AV_RL32(buf + 12); 70cabdff1aSopenharmony_ci ext->unk2 = AV_RL32(buf + 16); 71cabdff1aSopenharmony_ci 72cabdff1aSopenharmony_ci ext->state.has_saved = AV_RL32(buf + 20); 73cabdff1aSopenharmony_ci ext->state.predictor_r = AV_RL32(buf + 24); 74cabdff1aSopenharmony_ci ext->state.step_index_r = AV_RL32(buf + 28); 75cabdff1aSopenharmony_ci ext->state.saved_r = AV_RL32(buf + 32); 76cabdff1aSopenharmony_ci ext->state.predictor_l = AV_RL32(buf + 36); 77cabdff1aSopenharmony_ci ext->state.step_index_l = AV_RL32(buf + 40); 78cabdff1aSopenharmony_ci ext->state.saved_l = AV_RL32(buf + 44); 79cabdff1aSopenharmony_ci 80cabdff1aSopenharmony_ci for (int i = 0; i < FF_ARRAY_ELEMS(ext->unk3); i++) 81cabdff1aSopenharmony_ci ext->unk3[i] = AV_RL32(buf + 48 + (i * 4)); 82cabdff1aSopenharmony_ci 83cabdff1aSopenharmony_ci ext->data = AV_RL32(buf + 76); 84cabdff1aSopenharmony_ci} 85cabdff1aSopenharmony_ci 86cabdff1aSopenharmony_cistatic int apm_probe(const AVProbeData *p) 87cabdff1aSopenharmony_ci{ 88cabdff1aSopenharmony_ci if (AV_RL16(p->buf) != APM_TAG_CODEC) 89cabdff1aSopenharmony_ci return 0; 90cabdff1aSopenharmony_ci 91cabdff1aSopenharmony_ci if (p->buf_size < 100) 92cabdff1aSopenharmony_ci return 0; 93cabdff1aSopenharmony_ci 94cabdff1aSopenharmony_ci if (AV_RL32(p->buf + 20) != APM_TAG_VS12) 95cabdff1aSopenharmony_ci return 0; 96cabdff1aSopenharmony_ci 97cabdff1aSopenharmony_ci if (AV_RL32(p->buf + 96) != APM_TAG_DATA) 98cabdff1aSopenharmony_ci return 0; 99cabdff1aSopenharmony_ci 100cabdff1aSopenharmony_ci return AVPROBE_SCORE_MAX - 1; 101cabdff1aSopenharmony_ci} 102cabdff1aSopenharmony_ci 103cabdff1aSopenharmony_cistatic int apm_read_header(AVFormatContext *s) 104cabdff1aSopenharmony_ci{ 105cabdff1aSopenharmony_ci int64_t ret; 106cabdff1aSopenharmony_ci AVStream *st; 107cabdff1aSopenharmony_ci APMExtraData extradata; 108cabdff1aSopenharmony_ci AVCodecParameters *par; 109cabdff1aSopenharmony_ci uint8_t buf[APM_FILE_EXTRADATA_SIZE]; 110cabdff1aSopenharmony_ci int channels; 111cabdff1aSopenharmony_ci 112cabdff1aSopenharmony_ci if (!(st = avformat_new_stream(s, NULL))) 113cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 114cabdff1aSopenharmony_ci 115cabdff1aSopenharmony_ci /* 116cabdff1aSopenharmony_ci * This is 98% a WAVEFORMATEX, but there's something screwy with the extradata 117cabdff1aSopenharmony_ci * that ff_get_wav_header() can't (and shouldn't) handle properly. 118cabdff1aSopenharmony_ci */ 119cabdff1aSopenharmony_ci if (avio_rl16(s->pb) != APM_TAG_CODEC) 120cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 121cabdff1aSopenharmony_ci 122cabdff1aSopenharmony_ci par = st->codecpar; 123cabdff1aSopenharmony_ci channels = avio_rl16(s->pb); 124cabdff1aSopenharmony_ci par->sample_rate = avio_rl32(s->pb); 125cabdff1aSopenharmony_ci 126cabdff1aSopenharmony_ci /* Skip the bitrate, it's usually wrong anyway. */ 127cabdff1aSopenharmony_ci if ((ret = avio_skip(s->pb, 4)) < 0) 128cabdff1aSopenharmony_ci return ret; 129cabdff1aSopenharmony_ci 130cabdff1aSopenharmony_ci par->block_align = avio_rl16(s->pb); 131cabdff1aSopenharmony_ci par->bits_per_coded_sample = avio_rl16(s->pb); 132cabdff1aSopenharmony_ci 133cabdff1aSopenharmony_ci if (avio_rl32(s->pb) != APM_FILE_EXTRADATA_SIZE) 134cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 135cabdff1aSopenharmony_ci 136cabdff1aSopenharmony_ci /* 8 = bits per sample * max channels */ 137cabdff1aSopenharmony_ci if (par->sample_rate > (INT_MAX / 8)) 138cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 139cabdff1aSopenharmony_ci 140cabdff1aSopenharmony_ci if (par->bits_per_coded_sample != 4) 141cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 142cabdff1aSopenharmony_ci 143cabdff1aSopenharmony_ci if (channels > 2 || channels == 0) 144cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 145cabdff1aSopenharmony_ci 146cabdff1aSopenharmony_ci av_channel_layout_default(&par->ch_layout, channels); 147cabdff1aSopenharmony_ci par->codec_type = AVMEDIA_TYPE_AUDIO; 148cabdff1aSopenharmony_ci par->codec_id = AV_CODEC_ID_ADPCM_IMA_APM; 149cabdff1aSopenharmony_ci par->format = AV_SAMPLE_FMT_S16; 150cabdff1aSopenharmony_ci par->bit_rate = par->ch_layout.nb_channels * 151cabdff1aSopenharmony_ci (int64_t)par->sample_rate * 152cabdff1aSopenharmony_ci par->bits_per_coded_sample; 153cabdff1aSopenharmony_ci 154cabdff1aSopenharmony_ci if ((ret = avio_read(s->pb, buf, APM_FILE_EXTRADATA_SIZE)) < 0) 155cabdff1aSopenharmony_ci return ret; 156cabdff1aSopenharmony_ci else if (ret != APM_FILE_EXTRADATA_SIZE) 157cabdff1aSopenharmony_ci return AVERROR(EIO); 158cabdff1aSopenharmony_ci 159cabdff1aSopenharmony_ci apm_parse_extradata(&extradata, buf); 160cabdff1aSopenharmony_ci 161cabdff1aSopenharmony_ci if (extradata.magic != APM_TAG_VS12 || extradata.data != APM_TAG_DATA) 162cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 163cabdff1aSopenharmony_ci 164cabdff1aSopenharmony_ci if (extradata.state.has_saved) { 165cabdff1aSopenharmony_ci avpriv_request_sample(s, "Saved Samples"); 166cabdff1aSopenharmony_ci return AVERROR_PATCHWELCOME; 167cabdff1aSopenharmony_ci } 168cabdff1aSopenharmony_ci 169cabdff1aSopenharmony_ci if ((ret = ff_alloc_extradata(par, APM_EXTRADATA_SIZE)) < 0) 170cabdff1aSopenharmony_ci return ret; 171cabdff1aSopenharmony_ci 172cabdff1aSopenharmony_ci /* Use the entire state as extradata. */ 173cabdff1aSopenharmony_ci memcpy(par->extradata, buf + 20, APM_EXTRADATA_SIZE); 174cabdff1aSopenharmony_ci 175cabdff1aSopenharmony_ci avpriv_set_pts_info(st, 64, 1, par->sample_rate); 176cabdff1aSopenharmony_ci st->start_time = 0; 177cabdff1aSopenharmony_ci st->duration = extradata.data_size * 178cabdff1aSopenharmony_ci (8 / par->bits_per_coded_sample) / 179cabdff1aSopenharmony_ci par->ch_layout.nb_channels; 180cabdff1aSopenharmony_ci return 0; 181cabdff1aSopenharmony_ci} 182cabdff1aSopenharmony_ci 183cabdff1aSopenharmony_cistatic int apm_read_packet(AVFormatContext *s, AVPacket *pkt) 184cabdff1aSopenharmony_ci{ 185cabdff1aSopenharmony_ci int ret; 186cabdff1aSopenharmony_ci AVCodecParameters *par = s->streams[0]->codecpar; 187cabdff1aSopenharmony_ci 188cabdff1aSopenharmony_ci /* 189cabdff1aSopenharmony_ci * For future reference: if files with the `has_saved` field set ever 190cabdff1aSopenharmony_ci * surface, `saved_l`, and `saved_r` will each contain 8 "saved" samples 191cabdff1aSopenharmony_ci * that should be sent to the decoder before the actual data. 192cabdff1aSopenharmony_ci */ 193cabdff1aSopenharmony_ci 194cabdff1aSopenharmony_ci if ((ret = av_get_packet(s->pb, pkt, APM_MAX_READ_SIZE)) < 0) 195cabdff1aSopenharmony_ci return ret; 196cabdff1aSopenharmony_ci 197cabdff1aSopenharmony_ci pkt->flags &= ~AV_PKT_FLAG_CORRUPT; 198cabdff1aSopenharmony_ci pkt->stream_index = 0; 199cabdff1aSopenharmony_ci pkt->duration = ret * (8 / par->bits_per_coded_sample) / par->ch_layout.nb_channels; 200cabdff1aSopenharmony_ci 201cabdff1aSopenharmony_ci return 0; 202cabdff1aSopenharmony_ci} 203cabdff1aSopenharmony_ci 204cabdff1aSopenharmony_ciconst AVInputFormat ff_apm_demuxer = { 205cabdff1aSopenharmony_ci .name = "apm", 206cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("Ubisoft Rayman 2 APM"), 207cabdff1aSopenharmony_ci .read_probe = apm_probe, 208cabdff1aSopenharmony_ci .read_header = apm_read_header, 209cabdff1aSopenharmony_ci .read_packet = apm_read_packet 210cabdff1aSopenharmony_ci}; 211cabdff1aSopenharmony_ci#endif 212cabdff1aSopenharmony_ci 213cabdff1aSopenharmony_ci#if CONFIG_APM_MUXER 214cabdff1aSopenharmony_cistatic int apm_write_init(AVFormatContext *s) 215cabdff1aSopenharmony_ci{ 216cabdff1aSopenharmony_ci AVCodecParameters *par; 217cabdff1aSopenharmony_ci 218cabdff1aSopenharmony_ci if (s->nb_streams != 1) { 219cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "APM files have exactly one stream\n"); 220cabdff1aSopenharmony_ci return AVERROR(EINVAL); 221cabdff1aSopenharmony_ci } 222cabdff1aSopenharmony_ci 223cabdff1aSopenharmony_ci par = s->streams[0]->codecpar; 224cabdff1aSopenharmony_ci 225cabdff1aSopenharmony_ci if (par->codec_id != AV_CODEC_ID_ADPCM_IMA_APM) { 226cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "%s codec not supported\n", 227cabdff1aSopenharmony_ci avcodec_get_name(par->codec_id)); 228cabdff1aSopenharmony_ci return AVERROR(EINVAL); 229cabdff1aSopenharmony_ci } 230cabdff1aSopenharmony_ci 231cabdff1aSopenharmony_ci if (par->ch_layout.nb_channels > 2) { 232cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "APM files only support up to 2 channels\n"); 233cabdff1aSopenharmony_ci return AVERROR(EINVAL); 234cabdff1aSopenharmony_ci } 235cabdff1aSopenharmony_ci 236cabdff1aSopenharmony_ci if (par->sample_rate > (INT_MAX / 8)) { 237cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Sample rate too large\n"); 238cabdff1aSopenharmony_ci return AVERROR(EINVAL); 239cabdff1aSopenharmony_ci } 240cabdff1aSopenharmony_ci 241cabdff1aSopenharmony_ci if (par->extradata_size != APM_EXTRADATA_SIZE) { 242cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Invalid/missing extradata\n"); 243cabdff1aSopenharmony_ci return AVERROR(EINVAL); 244cabdff1aSopenharmony_ci } 245cabdff1aSopenharmony_ci 246cabdff1aSopenharmony_ci if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) { 247cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "Stream not seekable, unable to write output file\n"); 248cabdff1aSopenharmony_ci return AVERROR(EINVAL); 249cabdff1aSopenharmony_ci } 250cabdff1aSopenharmony_ci 251cabdff1aSopenharmony_ci return 0; 252cabdff1aSopenharmony_ci} 253cabdff1aSopenharmony_ci 254cabdff1aSopenharmony_cistatic int apm_write_header(AVFormatContext *s) 255cabdff1aSopenharmony_ci{ 256cabdff1aSopenharmony_ci uint8_t buf[APM_FILE_EXTRADATA_SIZE] = { 0 }; 257cabdff1aSopenharmony_ci AVCodecParameters *par = s->streams[0]->codecpar; 258cabdff1aSopenharmony_ci 259cabdff1aSopenharmony_ci /* 260cabdff1aSopenharmony_ci * Bodge a WAVEFORMATEX manually, ff_put_wav_header() can't 261cabdff1aSopenharmony_ci * be used because of the extra 2 bytes. 262cabdff1aSopenharmony_ci */ 263cabdff1aSopenharmony_ci avio_wl16(s->pb, APM_TAG_CODEC); 264cabdff1aSopenharmony_ci avio_wl16(s->pb, par->ch_layout.nb_channels); 265cabdff1aSopenharmony_ci avio_wl32(s->pb, par->sample_rate); 266cabdff1aSopenharmony_ci /* This is the wrong calculation, but it's what the orginal files have. */ 267cabdff1aSopenharmony_ci avio_wl32(s->pb, par->sample_rate * par->ch_layout.nb_channels * 2); 268cabdff1aSopenharmony_ci avio_wl16(s->pb, par->block_align); 269cabdff1aSopenharmony_ci avio_wl16(s->pb, par->bits_per_coded_sample); 270cabdff1aSopenharmony_ci avio_wl32(s->pb, APM_FILE_EXTRADATA_SIZE); 271cabdff1aSopenharmony_ci 272cabdff1aSopenharmony_ci /* 273cabdff1aSopenharmony_ci * Build the extradata. Assume the codec's given us correct data. 274cabdff1aSopenharmony_ci * File and data sizes are fixed later. 275cabdff1aSopenharmony_ci */ 276cabdff1aSopenharmony_ci AV_WL32(buf + 0, APM_TAG_VS12); /* magic */ 277cabdff1aSopenharmony_ci AV_WL32(buf + 12, 0xFFFFFFFF); /* unk1 */ 278cabdff1aSopenharmony_ci memcpy( buf + 20, par->extradata, APM_EXTRADATA_SIZE); 279cabdff1aSopenharmony_ci AV_WL32(buf + 76, APM_TAG_DATA); /* data */ 280cabdff1aSopenharmony_ci 281cabdff1aSopenharmony_ci avio_write(s->pb, buf, APM_FILE_EXTRADATA_SIZE); 282cabdff1aSopenharmony_ci return 0; 283cabdff1aSopenharmony_ci} 284cabdff1aSopenharmony_ci 285cabdff1aSopenharmony_cistatic int apm_write_trailer(AVFormatContext *s) 286cabdff1aSopenharmony_ci{ 287cabdff1aSopenharmony_ci int64_t file_size, data_size; 288cabdff1aSopenharmony_ci 289cabdff1aSopenharmony_ci file_size = avio_tell(s->pb); 290cabdff1aSopenharmony_ci data_size = file_size - (APM_FILE_HEADER_SIZE + APM_FILE_EXTRADATA_SIZE); 291cabdff1aSopenharmony_ci 292cabdff1aSopenharmony_ci if (file_size >= UINT32_MAX) { 293cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, 294cabdff1aSopenharmony_ci "Filesize %"PRId64" invalid for APM, output file will be broken\n", 295cabdff1aSopenharmony_ci file_size); 296cabdff1aSopenharmony_ci return AVERROR(ERANGE); 297cabdff1aSopenharmony_ci } 298cabdff1aSopenharmony_ci 299cabdff1aSopenharmony_ci avio_seek(s->pb, 24, SEEK_SET); 300cabdff1aSopenharmony_ci avio_wl32(s->pb, (uint32_t)file_size); 301cabdff1aSopenharmony_ci avio_wl32(s->pb, (uint32_t)data_size); 302cabdff1aSopenharmony_ci 303cabdff1aSopenharmony_ci return 0; 304cabdff1aSopenharmony_ci} 305cabdff1aSopenharmony_ci 306cabdff1aSopenharmony_ciconst AVOutputFormat ff_apm_muxer = { 307cabdff1aSopenharmony_ci .name = "apm", 308cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("Ubisoft Rayman 2 APM"), 309cabdff1aSopenharmony_ci .extensions = "apm", 310cabdff1aSopenharmony_ci .audio_codec = AV_CODEC_ID_ADPCM_IMA_APM, 311cabdff1aSopenharmony_ci .video_codec = AV_CODEC_ID_NONE, 312cabdff1aSopenharmony_ci .init = apm_write_init, 313cabdff1aSopenharmony_ci .write_header = apm_write_header, 314cabdff1aSopenharmony_ci .write_packet = ff_raw_write_packet, 315cabdff1aSopenharmony_ci .write_trailer = apm_write_trailer 316cabdff1aSopenharmony_ci}; 317cabdff1aSopenharmony_ci#endif 318