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