• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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     uint32_t    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     par                         = st->codecpar;
72     par->codec_type             = AVMEDIA_TYPE_AUDIO;
73     par->codec_id               = AV_CODEC_ID_ADPCM_IMA_SSI;
74     par->format                 = AV_SAMPLE_FMT_S16;
75 
76     av_channel_layout_default(&par->ch_layout, !!hdr.stereo + 1);
77     par->sample_rate            = hdr.sample_rate;
78     par->bits_per_coded_sample  = 4;
79     par->block_align            = 1;
80     par->bit_rate               = par->ch_layout.nb_channels *
81                                   (uint64_t)par->sample_rate *
82                                   par->bits_per_coded_sample;
83 
84     avpriv_set_pts_info(st, 64, 1, par->sample_rate);
85     st->start_time              = 0;
86     st->duration                = hdr.data_size *
87                                   (8 / par->bits_per_coded_sample) /
88                                   par->ch_layout.nb_channels;
89 
90     return 0;
91 }
92 
kvag_read_packet(AVFormatContext * s,AVPacket * pkt)93 static int kvag_read_packet(AVFormatContext *s, AVPacket *pkt)
94 {
95     int ret;
96     AVCodecParameters *par = s->streams[0]->codecpar;
97 
98     if ((ret = av_get_packet(s->pb, pkt, KVAG_MAX_READ_SIZE)) < 0)
99         return ret;
100 
101     pkt->flags          &= ~AV_PKT_FLAG_CORRUPT;
102     pkt->stream_index   = 0;
103     pkt->duration       = ret * (8 / par->bits_per_coded_sample) / par->ch_layout.nb_channels;
104 
105     return 0;
106 }
107 
kvag_seek(AVFormatContext * s,int stream_index,int64_t pts,int flags)108 static int kvag_seek(AVFormatContext *s, int stream_index,
109                      int64_t pts, int flags)
110 {
111     if (pts != 0)
112         return AVERROR(EINVAL);
113 
114     return avio_seek(s->pb, KVAG_HEADER_SIZE, SEEK_SET);
115 }
116 
117 const AVInputFormat ff_kvag_demuxer = {
118     .name           = "kvag",
119     .long_name      = NULL_IF_CONFIG_SMALL("Simon & Schuster Interactive VAG"),
120     .read_probe     = kvag_probe,
121     .read_header    = kvag_read_header,
122     .read_packet    = kvag_read_packet,
123     .read_seek      = kvag_seek,
124 };
125 #endif
126 
127 #if CONFIG_KVAG_MUXER
kvag_write_init(AVFormatContext * s)128 static int kvag_write_init(AVFormatContext *s)
129 {
130     AVCodecParameters *par;
131 
132     if (s->nb_streams != 1) {
133         av_log(s, AV_LOG_ERROR, "KVAG files have exactly one stream\n");
134         return AVERROR(EINVAL);
135     }
136 
137     par = s->streams[0]->codecpar;
138 
139     if (par->codec_id != AV_CODEC_ID_ADPCM_IMA_SSI) {
140         av_log(s, AV_LOG_ERROR, "%s codec not supported\n",
141                avcodec_get_name(par->codec_id));
142         return AVERROR(EINVAL);
143     }
144 
145     if (par->ch_layout.nb_channels > 2) {
146         av_log(s, AV_LOG_ERROR, "KVAG files only support up to 2 channels\n");
147         return AVERROR(EINVAL);
148     }
149 
150     if (!(s->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
151         av_log(s, AV_LOG_WARNING, "Stream not seekable, unable to write output file\n");
152         return AVERROR(EINVAL);
153     }
154 
155     return 0;
156 }
157 
kvag_write_header(AVFormatContext * s)158 static int kvag_write_header(AVFormatContext *s)
159 {
160     uint8_t buf[KVAG_HEADER_SIZE];
161     AVCodecParameters *par = s->streams[0]->codecpar;
162 
163     AV_WL32(buf +  0, KVAG_TAG);
164     AV_WL32(buf +  4, 0); /* Data size, we fix this up later. */
165     AV_WL32(buf +  8, par->sample_rate);
166     AV_WL16(buf + 12, par->ch_layout.nb_channels == 2);
167 
168     avio_write(s->pb, buf, sizeof(buf));
169     return 0;
170 }
171 
kvag_write_trailer(AVFormatContext * s)172 static int kvag_write_trailer(AVFormatContext *s)
173 {
174     int64_t file_size, data_size;
175 
176     file_size = avio_tell(s->pb);
177     data_size = file_size - KVAG_HEADER_SIZE;
178     if (data_size < UINT32_MAX) {
179         avio_seek(s->pb, 4, SEEK_SET);
180         avio_wl32(s->pb, (uint32_t)data_size);
181         avio_seek(s->pb, file_size, SEEK_SET);
182     } else {
183         av_log(s, AV_LOG_WARNING,
184                "Filesize %"PRId64" invalid for KVAG, output file will be broken\n",
185                file_size);
186     }
187 
188     return 0;
189 }
190 
191 const AVOutputFormat ff_kvag_muxer = {
192     .name           = "kvag",
193     .long_name      = NULL_IF_CONFIG_SMALL("Simon & Schuster Interactive VAG"),
194     .extensions     = "vag",
195     .audio_codec    = AV_CODEC_ID_ADPCM_IMA_SSI,
196     .video_codec    = AV_CODEC_ID_NONE,
197     .init           = kvag_write_init,
198     .write_header   = kvag_write_header,
199     .write_packet   = ff_raw_write_packet,
200     .write_trailer  = kvag_write_trailer
201 };
202 #endif
203