1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * Simon & Schuster Interactive VAG (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 "avio_internal.h" 28cabdff1aSopenharmony_ci#include "internal.h" 29cabdff1aSopenharmony_ci#include "rawenc.h" 30cabdff1aSopenharmony_ci#include "libavutil/intreadwrite.h" 31cabdff1aSopenharmony_ci 32cabdff1aSopenharmony_ci#define KVAG_TAG MKTAG('K', 'V', 'A', 'G') 33cabdff1aSopenharmony_ci#define KVAG_HEADER_SIZE 14 34cabdff1aSopenharmony_ci#define KVAG_MAX_READ_SIZE 4096 35cabdff1aSopenharmony_ci 36cabdff1aSopenharmony_citypedef struct KVAGHeader { 37cabdff1aSopenharmony_ci uint32_t magic; 38cabdff1aSopenharmony_ci uint32_t data_size; 39cabdff1aSopenharmony_ci int sample_rate; 40cabdff1aSopenharmony_ci uint16_t stereo; 41cabdff1aSopenharmony_ci} KVAGHeader; 42cabdff1aSopenharmony_ci 43cabdff1aSopenharmony_ci#if CONFIG_KVAG_DEMUXER 44cabdff1aSopenharmony_cistatic int kvag_probe(const AVProbeData *p) 45cabdff1aSopenharmony_ci{ 46cabdff1aSopenharmony_ci if (AV_RL32(p->buf) != KVAG_TAG) 47cabdff1aSopenharmony_ci return 0; 48cabdff1aSopenharmony_ci 49cabdff1aSopenharmony_ci return AVPROBE_SCORE_EXTENSION + 1; 50cabdff1aSopenharmony_ci} 51cabdff1aSopenharmony_ci 52cabdff1aSopenharmony_cistatic int kvag_read_header(AVFormatContext *s) 53cabdff1aSopenharmony_ci{ 54cabdff1aSopenharmony_ci int ret; 55cabdff1aSopenharmony_ci AVStream *st; 56cabdff1aSopenharmony_ci KVAGHeader hdr; 57cabdff1aSopenharmony_ci AVCodecParameters *par; 58cabdff1aSopenharmony_ci uint8_t buf[KVAG_HEADER_SIZE]; 59cabdff1aSopenharmony_ci 60cabdff1aSopenharmony_ci if (!(st = avformat_new_stream(s, NULL))) 61cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 62cabdff1aSopenharmony_ci 63cabdff1aSopenharmony_ci if ((ret = ffio_read_size(s->pb, buf, KVAG_HEADER_SIZE)) < 0) 64cabdff1aSopenharmony_ci return ret; 65cabdff1aSopenharmony_ci 66cabdff1aSopenharmony_ci hdr.magic = AV_RL32(buf + 0); 67cabdff1aSopenharmony_ci hdr.data_size = AV_RL32(buf + 4); 68cabdff1aSopenharmony_ci hdr.sample_rate = AV_RL32(buf + 8); 69cabdff1aSopenharmony_ci hdr.stereo = AV_RL16(buf + 12); 70cabdff1aSopenharmony_ci 71cabdff1aSopenharmony_ci if (hdr.sample_rate <= 0) 72cabdff1aSopenharmony_ci return AVERROR_INVALIDDATA; 73cabdff1aSopenharmony_ci 74cabdff1aSopenharmony_ci par = st->codecpar; 75cabdff1aSopenharmony_ci par->codec_type = AVMEDIA_TYPE_AUDIO; 76cabdff1aSopenharmony_ci par->codec_id = AV_CODEC_ID_ADPCM_IMA_SSI; 77cabdff1aSopenharmony_ci par->format = AV_SAMPLE_FMT_S16; 78cabdff1aSopenharmony_ci 79cabdff1aSopenharmony_ci av_channel_layout_default(&par->ch_layout, !!hdr.stereo + 1); 80cabdff1aSopenharmony_ci par->sample_rate = hdr.sample_rate; 81cabdff1aSopenharmony_ci par->bits_per_coded_sample = 4; 82cabdff1aSopenharmony_ci par->block_align = 1; 83cabdff1aSopenharmony_ci par->bit_rate = par->ch_layout.nb_channels * 84cabdff1aSopenharmony_ci (uint64_t)par->sample_rate * 85cabdff1aSopenharmony_ci par->bits_per_coded_sample; 86cabdff1aSopenharmony_ci 87cabdff1aSopenharmony_ci avpriv_set_pts_info(st, 64, 1, par->sample_rate); 88cabdff1aSopenharmony_ci st->start_time = 0; 89cabdff1aSopenharmony_ci st->duration = hdr.data_size * 90cabdff1aSopenharmony_ci (8 / par->bits_per_coded_sample) / 91cabdff1aSopenharmony_ci par->ch_layout.nb_channels; 92cabdff1aSopenharmony_ci 93cabdff1aSopenharmony_ci return 0; 94cabdff1aSopenharmony_ci} 95cabdff1aSopenharmony_ci 96cabdff1aSopenharmony_cistatic int kvag_read_packet(AVFormatContext *s, AVPacket *pkt) 97cabdff1aSopenharmony_ci{ 98cabdff1aSopenharmony_ci int ret; 99cabdff1aSopenharmony_ci AVCodecParameters *par = s->streams[0]->codecpar; 100cabdff1aSopenharmony_ci 101cabdff1aSopenharmony_ci if ((ret = av_get_packet(s->pb, pkt, KVAG_MAX_READ_SIZE)) < 0) 102cabdff1aSopenharmony_ci return ret; 103cabdff1aSopenharmony_ci 104cabdff1aSopenharmony_ci pkt->flags &= ~AV_PKT_FLAG_CORRUPT; 105cabdff1aSopenharmony_ci pkt->stream_index = 0; 106cabdff1aSopenharmony_ci pkt->duration = ret * (8 / par->bits_per_coded_sample) / par->ch_layout.nb_channels; 107cabdff1aSopenharmony_ci 108cabdff1aSopenharmony_ci return 0; 109cabdff1aSopenharmony_ci} 110cabdff1aSopenharmony_ci 111cabdff1aSopenharmony_cistatic int kvag_seek(AVFormatContext *s, int stream_index, 112cabdff1aSopenharmony_ci int64_t pts, int flags) 113cabdff1aSopenharmony_ci{ 114cabdff1aSopenharmony_ci if (pts != 0) 115cabdff1aSopenharmony_ci return AVERROR(EINVAL); 116cabdff1aSopenharmony_ci 117cabdff1aSopenharmony_ci return avio_seek(s->pb, KVAG_HEADER_SIZE, SEEK_SET); 118cabdff1aSopenharmony_ci} 119cabdff1aSopenharmony_ci 120cabdff1aSopenharmony_ciconst AVInputFormat ff_kvag_demuxer = { 121cabdff1aSopenharmony_ci .name = "kvag", 122cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("Simon & Schuster Interactive VAG"), 123cabdff1aSopenharmony_ci .read_probe = kvag_probe, 124cabdff1aSopenharmony_ci .read_header = kvag_read_header, 125cabdff1aSopenharmony_ci .read_packet = kvag_read_packet, 126cabdff1aSopenharmony_ci .read_seek = kvag_seek, 127cabdff1aSopenharmony_ci}; 128cabdff1aSopenharmony_ci#endif 129cabdff1aSopenharmony_ci 130cabdff1aSopenharmony_ci#if CONFIG_KVAG_MUXER 131cabdff1aSopenharmony_cistatic int kvag_write_init(AVFormatContext *s) 132cabdff1aSopenharmony_ci{ 133cabdff1aSopenharmony_ci AVCodecParameters *par; 134cabdff1aSopenharmony_ci 135cabdff1aSopenharmony_ci if (s->nb_streams != 1) { 136cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "KVAG files have exactly one stream\n"); 137cabdff1aSopenharmony_ci return AVERROR(EINVAL); 138cabdff1aSopenharmony_ci } 139cabdff1aSopenharmony_ci 140cabdff1aSopenharmony_ci par = s->streams[0]->codecpar; 141cabdff1aSopenharmony_ci 142cabdff1aSopenharmony_ci if (par->codec_id != AV_CODEC_ID_ADPCM_IMA_SSI) { 143cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "%s codec not supported\n", 144cabdff1aSopenharmony_ci avcodec_get_name(par->codec_id)); 145cabdff1aSopenharmony_ci return AVERROR(EINVAL); 146cabdff1aSopenharmony_ci } 147cabdff1aSopenharmony_ci 148cabdff1aSopenharmony_ci if (par->ch_layout.nb_channels > 2) { 149cabdff1aSopenharmony_ci av_log(s, AV_LOG_ERROR, "KVAG files only support up to 2 channels\n"); 150cabdff1aSopenharmony_ci return AVERROR(EINVAL); 151cabdff1aSopenharmony_ci } 152cabdff1aSopenharmony_ci 153cabdff1aSopenharmony_ci if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) { 154cabdff1aSopenharmony_ci av_log(s, AV_LOG_WARNING, "Stream not seekable, unable to write output file\n"); 155cabdff1aSopenharmony_ci return AVERROR(EINVAL); 156cabdff1aSopenharmony_ci } 157cabdff1aSopenharmony_ci 158cabdff1aSopenharmony_ci return 0; 159cabdff1aSopenharmony_ci} 160cabdff1aSopenharmony_ci 161cabdff1aSopenharmony_cistatic int kvag_write_header(AVFormatContext *s) 162cabdff1aSopenharmony_ci{ 163cabdff1aSopenharmony_ci uint8_t buf[KVAG_HEADER_SIZE]; 164cabdff1aSopenharmony_ci AVCodecParameters *par = s->streams[0]->codecpar; 165cabdff1aSopenharmony_ci 166cabdff1aSopenharmony_ci AV_WL32(buf + 0, KVAG_TAG); 167cabdff1aSopenharmony_ci AV_WL32(buf + 4, 0); /* Data size, we fix this up later. */ 168cabdff1aSopenharmony_ci AV_WL32(buf + 8, par->sample_rate); 169cabdff1aSopenharmony_ci AV_WL16(buf + 12, par->ch_layout.nb_channels == 2); 170cabdff1aSopenharmony_ci 171cabdff1aSopenharmony_ci avio_write(s->pb, buf, sizeof(buf)); 172cabdff1aSopenharmony_ci return 0; 173cabdff1aSopenharmony_ci} 174cabdff1aSopenharmony_ci 175cabdff1aSopenharmony_cistatic int kvag_write_trailer(AVFormatContext *s) 176cabdff1aSopenharmony_ci{ 177cabdff1aSopenharmony_ci int64_t file_size, data_size; 178cabdff1aSopenharmony_ci 179cabdff1aSopenharmony_ci file_size = avio_tell(s->pb); 180cabdff1aSopenharmony_ci data_size = file_size - KVAG_HEADER_SIZE; 181cabdff1aSopenharmony_ci if (data_size < UINT32_MAX) { 182cabdff1aSopenharmony_ci avio_seek(s->pb, 4, SEEK_SET); 183cabdff1aSopenharmony_ci avio_wl32(s->pb, (uint32_t)data_size); 184cabdff1aSopenharmony_ci avio_seek(s->pb, file_size, SEEK_SET); 185cabdff1aSopenharmony_ci } else { 186cabdff1aSopenharmony_ci av_log(s, AV_LOG_WARNING, 187cabdff1aSopenharmony_ci "Filesize %"PRId64" invalid for KVAG, output file will be broken\n", 188cabdff1aSopenharmony_ci file_size); 189cabdff1aSopenharmony_ci } 190cabdff1aSopenharmony_ci 191cabdff1aSopenharmony_ci return 0; 192cabdff1aSopenharmony_ci} 193cabdff1aSopenharmony_ci 194cabdff1aSopenharmony_ciconst AVOutputFormat ff_kvag_muxer = { 195cabdff1aSopenharmony_ci .name = "kvag", 196cabdff1aSopenharmony_ci .long_name = NULL_IF_CONFIG_SMALL("Simon & Schuster Interactive VAG"), 197cabdff1aSopenharmony_ci .extensions = "vag", 198cabdff1aSopenharmony_ci .audio_codec = AV_CODEC_ID_ADPCM_IMA_SSI, 199cabdff1aSopenharmony_ci .video_codec = AV_CODEC_ID_NONE, 200cabdff1aSopenharmony_ci .init = kvag_write_init, 201cabdff1aSopenharmony_ci .write_header = kvag_write_header, 202cabdff1aSopenharmony_ci .write_packet = ff_raw_write_packet, 203cabdff1aSopenharmony_ci .write_trailer = kvag_write_trailer 204cabdff1aSopenharmony_ci}; 205cabdff1aSopenharmony_ci#endif 206