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