Coverage Report

Created: 2026-05-16 07:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavformat/bethsoftvid.c
Line
Count
Source
1
/*
2
 * Bethsoft VID format Demuxer
3
 * Copyright (c) 2007 Nicholas Tung
4
 *
5
 * This file is part of FFmpeg.
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with FFmpeg; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
 */
21
22
/**
23
 * @file
24
 * @brief Bethesda Softworks VID (.vid) file demuxer
25
 * @author Nicholas Tung [ntung (at. ntung com] (2007-03)
26
 * @see http://wiki.multimedia.cx/index.php?title=Bethsoft_VID
27
 * @see http://www.svatopluk.com/andux/docs/dfvid.html
28
 */
29
30
#include "libavutil/attributes.h"
31
#include "libavutil/channel_layout.h"
32
#include "libavutil/imgutils.h"
33
#include "libavutil/intreadwrite.h"
34
#include "libavutil/mem.h"
35
#include "avformat.h"
36
#include "avio_internal.h"
37
#include "demux.h"
38
#include "internal.h"
39
#include "libavcodec/bethsoftvideo.h"
40
41
2.37k
#define BVID_PALETTE_SIZE 3 * 256
42
43
2.48k
#define DEFAULT_SAMPLE_RATE 11111
44
45
typedef struct BVID_DemuxContext
46
{
47
    int nframes;
48
    int sample_rate;        /**< audio sample rate */
49
    int width;              /**< video width       */
50
    int height;             /**< video height      */
51
    /** delay value between frames, added to individual frame delay.
52
     * custom units, which will be added to other custom units (~=16ms according
53
     * to free, unofficial documentation) */
54
    int bethsoft_global_delay;
55
    int video_index;        /**< video stream index */
56
    int audio_index;        /**< audio stream index */
57
    int has_palette;
58
    uint8_t palette[BVID_PALETTE_SIZE];
59
60
    int is_finished;
61
62
} BVID_DemuxContext;
63
64
static int vid_probe(const AVProbeData *p)
65
971k
{
66
    // little-endian VID tag, file starts with "VID\0"
67
971k
    if (AV_RL32(p->buf) != MKTAG('V', 'I', 'D', 0))
68
971k
        return 0;
69
70
470
    if (p->buf[4] != 2)
71
324
        return AVPROBE_SCORE_MAX / 4;
72
73
146
    return AVPROBE_SCORE_MAX;
74
470
}
75
76
static int vid_read_header(AVFormatContext *s)
77
2.66k
{
78
2.66k
    BVID_DemuxContext *vid = s->priv_data;
79
2.66k
    AVIOContext *pb = s->pb;
80
2.66k
    int ret;
81
82
    /* load main header. Contents:
83
    *    bytes: 'V' 'I' 'D'
84
    *    int16s: always_512, nframes, width, height, delay, always_14
85
    */
86
2.66k
    avio_skip(pb, 5);
87
2.66k
    vid->nframes = avio_rl16(pb);
88
2.66k
    vid->width   = avio_rl16(pb);
89
2.66k
    vid->height  = avio_rl16(pb);
90
2.66k
    vid->bethsoft_global_delay = avio_rl16(pb);
91
2.66k
    avio_rl16(pb);
92
93
2.66k
    ret = av_image_check_size(vid->width, vid->height, 0, s);
94
2.66k
    if (ret < 0)
95
174
        return ret;
96
97
    // wait until the first packet to create each stream
98
2.48k
    vid->video_index = -1;
99
2.48k
    vid->audio_index = -1;
100
2.48k
    vid->sample_rate = DEFAULT_SAMPLE_RATE;
101
2.48k
    s->ctx_flags |= AVFMTCTX_NOHEADER;
102
103
2.48k
    return 0;
104
2.66k
}
105
106
19.0M
#define BUFFER_PADDING_SIZE 1000
107
static int read_frame(BVID_DemuxContext *vid, AVIOContext *pb, AVPacket *pkt,
108
                      uint8_t block_type, AVFormatContext *s)
109
376k
{
110
376k
    uint8_t * vidbuf_start = NULL;
111
376k
    int vidbuf_nbytes = 0;
112
376k
    int code;
113
376k
    int bytes_copied = 0;
114
376k
    int position, duration, npixels;
115
376k
    unsigned int vidbuf_capacity;
116
376k
    int ret = 0;
117
376k
    AVStream *st;
118
119
376k
    if (vid->video_index < 0) {
120
1.72k
        st = avformat_new_stream(s, NULL);
121
1.72k
        if (!st)
122
0
            return AVERROR(ENOMEM);
123
1.72k
        vid->video_index = st->index;
124
1.72k
        if (vid->audio_index < 0) {
125
924
            avpriv_request_sample(s, "Using default video time base since "
126
924
                                  "having no audio packet before the first "
127
924
                                  "video packet");
128
924
        }
129
1.72k
        avpriv_set_pts_info(st, 64, 185, vid->sample_rate);
130
1.72k
        st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
131
1.72k
        st->codecpar->codec_id   = AV_CODEC_ID_BETHSOFTVID;
132
1.72k
        st->codecpar->width      = vid->width;
133
1.72k
        st->codecpar->height     = vid->height;
134
1.72k
    }
135
376k
    st      = s->streams[vid->video_index];
136
376k
    npixels = st->codecpar->width * st->codecpar->height;
137
138
376k
    vidbuf_start = av_malloc(vidbuf_capacity = BUFFER_PADDING_SIZE);
139
376k
    if(!vidbuf_start)
140
0
        return AVERROR(ENOMEM);
141
142
    // save the file position for the packet, include block type
143
376k
    position = avio_tell(pb) - 1;
144
145
376k
    vidbuf_start[vidbuf_nbytes++] = block_type;
146
147
    // get the current packet duration
148
376k
    duration = vid->bethsoft_global_delay + avio_rl16(pb);
149
150
    // set the y offset if it exists (decoder header data should be in data section)
151
376k
    if(block_type == VIDEO_YOFF_P_FRAME){
152
982
        ret = ffio_read_size(pb, &vidbuf_start[vidbuf_nbytes], 2);
153
982
        if (ret < 0)
154
32
            goto fail;
155
950
        vidbuf_nbytes += 2;
156
950
    }
157
158
18.6M
    do{
159
18.6M
        uint8_t *tmp = av_fast_realloc(vidbuf_start, &vidbuf_capacity,
160
18.6M
                                       vidbuf_nbytes + BUFFER_PADDING_SIZE);
161
18.6M
        if (!tmp) {
162
0
            ret = AVERROR(ENOMEM);
163
0
            goto fail;
164
0
        }
165
18.6M
        vidbuf_start = tmp;
166
167
18.6M
        code = avio_r8(pb);
168
18.6M
        vidbuf_start[vidbuf_nbytes++] = code;
169
170
18.6M
        if(code >= 0x80){ // rle sequence
171
14.1M
            if(block_type == VIDEO_I_FRAME)
172
2.51M
                vidbuf_start[vidbuf_nbytes++] = avio_r8(pb);
173
14.1M
        } else if(code){ // plain sequence
174
4.22M
            ret = ffio_read_size(pb, &vidbuf_start[vidbuf_nbytes], code);
175
4.22M
            if (ret < 0)
176
108
                goto fail;
177
4.22M
            vidbuf_nbytes += code;
178
4.22M
        }
179
18.6M
        bytes_copied += code & 0x7F;
180
18.6M
        if(bytes_copied == npixels){ // sometimes no stop character is given, need to keep track of bytes copied
181
            // may contain a 0 byte even if read all pixels
182
24.1k
            if(avio_r8(pb))
183
23.8k
                avio_seek(pb, -1, SEEK_CUR);
184
24.1k
            break;
185
24.1k
        }
186
18.6M
        if (bytes_copied > npixels) {
187
98
            ret = AVERROR_INVALIDDATA;
188
98
            goto fail;
189
98
        }
190
18.6M
    } while(code);
191
192
    // copy data into packet
193
376k
    if ((ret = av_new_packet(pkt, vidbuf_nbytes)) < 0)
194
0
        goto fail;
195
376k
    memcpy(pkt->data, vidbuf_start, vidbuf_nbytes);
196
197
376k
    pkt->pos = position;
198
376k
    pkt->stream_index = vid->video_index;
199
376k
    pkt->duration = duration;
200
376k
    if (block_type == VIDEO_I_FRAME)
201
301k
        pkt->flags |= AV_PKT_FLAG_KEY;
202
203
    /* if there is a new palette available, add it to packet side data */
204
376k
    if (vid->has_palette) {
205
634
        uint8_t *pdata = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE,
206
634
                                                 BVID_PALETTE_SIZE);
207
634
        if (!pdata) {
208
0
            ret = AVERROR(ENOMEM);
209
0
            av_log(s, AV_LOG_ERROR, "Failed to allocate palette side data\n");
210
0
            goto fail;
211
0
        }
212
634
        memcpy(pdata, vid->palette, BVID_PALETTE_SIZE);
213
634
        vid->has_palette = 0;
214
634
    }
215
216
376k
    vid->nframes--;  // used to check if all the frames were read
217
376k
fail:
218
376k
    av_free(vidbuf_start);
219
376k
    return ret;
220
376k
}
221
222
static int vid_read_packet(AVFormatContext *s,
223
                           AVPacket *pkt)
224
2.21M
{
225
2.21M
    BVID_DemuxContext *vid = s->priv_data;
226
2.21M
    AVIOContext *pb = s->pb;
227
2.21M
    unsigned char block_type;
228
2.21M
    int audio_length;
229
2.21M
    int ret_value;
230
231
2.21M
    if(vid->is_finished || avio_feof(pb))
232
2.65k
        return AVERROR_EOF;
233
234
2.20M
    block_type = avio_r8(pb);
235
2.20M
    switch(block_type){
236
1.10k
        case PALETTE_BLOCK:
237
1.10k
            if (vid->has_palette) {
238
398
                av_log(s, AV_LOG_WARNING, "discarding unused palette\n");
239
398
                vid->has_palette = 0;
240
398
            }
241
1.10k
            ret_value = ffio_read_size(pb, vid->palette, BVID_PALETTE_SIZE);
242
1.10k
            if (ret_value < 0)
243
56
                return ret_value;
244
1.04k
            vid->has_palette = 1;
245
1.04k
            return vid_read_packet(s, pkt);
246
247
1.25k
        case FIRST_AUDIO_BLOCK:
248
1.25k
            avio_rl16(pb);
249
            // soundblaster DAC used for sample rate, as on specification page (link above)
250
1.25k
            vid->sample_rate = 1000000 / (256 - avio_r8(pb));
251
1.25k
            av_fallthrough;
252
1.82M
        case AUDIO_BLOCK:
253
1.82M
            if (vid->audio_index < 0) {
254
1.44k
                AVStream *st = avformat_new_stream(s, NULL);
255
1.44k
                if (!st)
256
0
                    return AVERROR(ENOMEM);
257
1.44k
                vid->audio_index                 = st->index;
258
1.44k
                st->codecpar->codec_type            = AVMEDIA_TYPE_AUDIO;
259
1.44k
                st->codecpar->codec_id              = AV_CODEC_ID_PCM_U8;
260
1.44k
                st->codecpar->ch_layout             = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
261
1.44k
                st->codecpar->bits_per_coded_sample = 8;
262
1.44k
                st->codecpar->sample_rate           = vid->sample_rate;
263
1.44k
                st->codecpar->bit_rate              = 8 * st->codecpar->sample_rate;
264
1.44k
                st->start_time                   = 0;
265
1.44k
                avpriv_set_pts_info(st, 64, 1, vid->sample_rate);
266
1.44k
            }
267
1.82M
            audio_length = avio_rl16(pb);
268
1.82M
            if ((ret_value = av_get_packet(pb, pkt, audio_length)) != audio_length) {
269
223
                if (ret_value < 0)
270
133
                    return ret_value;
271
90
                av_log(s, AV_LOG_ERROR, "incomplete audio block\n");
272
90
                return AVERROR_INVALIDDATA;
273
223
            }
274
1.82M
            pkt->stream_index = vid->audio_index;
275
1.82M
            pkt->duration     = audio_length;
276
1.82M
            pkt->flags |= AV_PKT_FLAG_KEY;
277
1.82M
            return 0;
278
279
74.6k
        case VIDEO_P_FRAME:
280
75.6k
        case VIDEO_YOFF_P_FRAME:
281
376k
        case VIDEO_I_FRAME:
282
376k
            return read_frame(vid, pb, pkt, block_type, s);
283
284
34
        case EOF_BLOCK:
285
34
            if(vid->nframes != 0)
286
33
                av_log(s, AV_LOG_VERBOSE, "reached terminating character but not all frames read.\n");
287
34
            vid->is_finished = 1;
288
34
            return AVERROR_INVALIDDATA;
289
1.48k
        default:
290
1.48k
            av_log(s, AV_LOG_ERROR, "unknown block (character = %c, decimal = %d, hex = %x)!!!\n",
291
1.48k
                   block_type, block_type, block_type);
292
1.48k
            return AVERROR_INVALIDDATA;
293
2.20M
    }
294
2.20M
}
295
296
const FFInputFormat ff_bethsoftvid_demuxer = {
297
    .p.name         = "bethsoftvid",
298
    .p.long_name    = NULL_IF_CONFIG_SMALL("Bethesda Softworks VID"),
299
    .priv_data_size = sizeof(BVID_DemuxContext),
300
    .read_probe     = vid_probe,
301
    .read_header    = vid_read_header,
302
    .read_packet    = vid_read_packet,
303
};