Coverage Report

Created: 2026-10-28 08:13

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