Coverage Report

Created: 2024-09-06 07:53

/src/ffmpeg/libavformat/bethsoftvid.c
Line
Count
Source (jump to first uncovered line)
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/channel_layout.h"
31
#include "libavutil/imgutils.h"
32
#include "libavutil/intreadwrite.h"
33
#include "libavutil/mem.h"
34
#include "avformat.h"
35
#include "demux.h"
36
#include "internal.h"
37
#include "libavcodec/bethsoftvideo.h"
38
39
5.43k
#define BVID_PALETTE_SIZE 3 * 256
40
41
1.85k
#define DEFAULT_SAMPLE_RATE 11111
42
43
typedef struct BVID_DemuxContext
44
{
45
    int nframes;
46
    int sample_rate;        /**< audio sample rate */
47
    int width;              /**< video width       */
48
    int height;             /**< video height      */
49
    /** delay value between frames, added to individual frame delay.
50
     * custom units, which will be added to other custom units (~=16ms according
51
     * to free, unofficial documentation) */
52
    int bethsoft_global_delay;
53
    int video_index;        /**< video stream index */
54
    int audio_index;        /**< audio stream index */
55
    int has_palette;
56
    uint8_t palette[BVID_PALETTE_SIZE];
57
58
    int is_finished;
59
60
} BVID_DemuxContext;
61
62
static int vid_probe(const AVProbeData *p)
63
358k
{
64
    // little-endian VID tag, file starts with "VID\0"
65
358k
    if (AV_RL32(p->buf) != MKTAG('V', 'I', 'D', 0))
66
358k
        return 0;
67
68
106
    if (p->buf[4] != 2)
69
73
        return AVPROBE_SCORE_MAX / 4;
70
71
33
    return AVPROBE_SCORE_MAX;
72
106
}
73
74
static int vid_read_header(AVFormatContext *s)
75
6.18k
{
76
6.18k
    BVID_DemuxContext *vid = s->priv_data;
77
6.18k
    AVIOContext *pb = s->pb;
78
6.18k
    int ret;
79
80
    /* load main header. Contents:
81
    *    bytes: 'V' 'I' 'D'
82
    *    int16s: always_512, nframes, width, height, delay, always_14
83
    */
84
6.18k
    avio_skip(pb, 5);
85
6.18k
    vid->nframes = avio_rl16(pb);
86
6.18k
    vid->width   = avio_rl16(pb);
87
6.18k
    vid->height  = avio_rl16(pb);
88
6.18k
    vid->bethsoft_global_delay = avio_rl16(pb);
89
6.18k
    avio_rl16(pb);
90
91
6.18k
    ret = av_image_check_size(vid->width, vid->height, 0, s);
92
6.18k
    if (ret < 0)
93
4.33k
        return ret;
94
95
    // wait until the first packet to create each stream
96
1.85k
    vid->video_index = -1;
97
1.85k
    vid->audio_index = -1;
98
1.85k
    vid->sample_rate = DEFAULT_SAMPLE_RATE;
99
1.85k
    s->ctx_flags |= AVFMTCTX_NOHEADER;
100
101
1.85k
    return 0;
102
6.18k
}
103
104
3.98M
#define BUFFER_PADDING_SIZE 1000
105
static int read_frame(BVID_DemuxContext *vid, AVIOContext *pb, AVPacket *pkt,
106
                      uint8_t block_type, AVFormatContext *s)
107
190k
{
108
190k
    uint8_t * vidbuf_start = NULL;
109
190k
    int vidbuf_nbytes = 0;
110
190k
    int code;
111
190k
    int bytes_copied = 0;
112
190k
    int position, duration, npixels;
113
190k
    unsigned int vidbuf_capacity;
114
190k
    int ret = 0;
115
190k
    AVStream *st;
116
117
190k
    if (vid->video_index < 0) {
118
1.21k
        st = avformat_new_stream(s, NULL);
119
1.21k
        if (!st)
120
0
            return AVERROR(ENOMEM);
121
1.21k
        vid->video_index = st->index;
122
1.21k
        if (vid->audio_index < 0) {
123
632
            avpriv_request_sample(s, "Using default video time base since "
124
632
                                  "having no audio packet before the first "
125
632
                                  "video packet");
126
632
        }
127
1.21k
        avpriv_set_pts_info(st, 64, 185, vid->sample_rate);
128
1.21k
        st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
129
1.21k
        st->codecpar->codec_id   = AV_CODEC_ID_BETHSOFTVID;
130
1.21k
        st->codecpar->width      = vid->width;
131
1.21k
        st->codecpar->height     = vid->height;
132
1.21k
    }
133
190k
    st      = s->streams[vid->video_index];
134
190k
    npixels = st->codecpar->width * st->codecpar->height;
135
136
190k
    vidbuf_start = av_malloc(vidbuf_capacity = BUFFER_PADDING_SIZE);
137
190k
    if(!vidbuf_start)
138
0
        return AVERROR(ENOMEM);
139
140
    // save the file position for the packet, include block type
141
190k
    position = avio_tell(pb) - 1;
142
143
190k
    vidbuf_start[vidbuf_nbytes++] = block_type;
144
145
    // get the current packet duration
146
190k
    duration = vid->bethsoft_global_delay + avio_rl16(pb);
147
148
    // set the y offset if it exists (decoder header data should be in data section)
149
190k
    if(block_type == VIDEO_YOFF_P_FRAME){
150
619
        if (avio_read(pb, &vidbuf_start[vidbuf_nbytes], 2) != 2) {
151
24
            ret = AVERROR(EIO);
152
24
            goto fail;
153
24
        }
154
595
        vidbuf_nbytes += 2;
155
595
    }
156
157
3.79M
    do{
158
3.79M
        uint8_t *tmp = av_fast_realloc(vidbuf_start, &vidbuf_capacity,
159
3.79M
                                       vidbuf_nbytes + BUFFER_PADDING_SIZE);
160
3.79M
        if (!tmp) {
161
0
            ret = AVERROR(ENOMEM);
162
0
            goto fail;
163
0
        }
164
3.79M
        vidbuf_start = tmp;
165
166
3.79M
        code = avio_r8(pb);
167
3.79M
        vidbuf_start[vidbuf_nbytes++] = code;
168
169
3.79M
        if(code >= 0x80){ // rle sequence
170
3.44M
            if(block_type == VIDEO_I_FRAME)
171
2.77M
                vidbuf_start[vidbuf_nbytes++] = avio_r8(pb);
172
3.44M
        } else if(code){ // plain sequence
173
163k
            if (avio_read(pb, &vidbuf_start[vidbuf_nbytes], code) != code) {
174
42
                ret = AVERROR(EIO);
175
42
                goto fail;
176
42
            }
177
163k
            vidbuf_nbytes += code;
178
163k
        }
179
3.79M
        bytes_copied += code & 0x7F;
180
3.79M
        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
1.36k
            if(avio_r8(pb))
183
1.16k
                avio_seek(pb, -1, SEEK_CUR);
184
1.36k
            break;
185
1.36k
        }
186
3.79M
        if (bytes_copied > npixels) {
187
52
            ret = AVERROR_INVALIDDATA;
188
52
            goto fail;
189
52
        }
190
3.79M
    } while(code);
191
192
    // copy data into packet
193
190k
    if ((ret = av_new_packet(pkt, vidbuf_nbytes)) < 0)
194
0
        goto fail;
195
190k
    memcpy(pkt->data, vidbuf_start, vidbuf_nbytes);
196
197
190k
    pkt->pos = position;
198
190k
    pkt->stream_index = vid->video_index;
199
190k
    pkt->duration = duration;
200
190k
    if (block_type == VIDEO_I_FRAME)
201
160k
        pkt->flags |= AV_PKT_FLAG_KEY;
202
203
    /* if there is a new palette available, add it to packet side data */
204
190k
    if (vid->has_palette) {
205
463
        uint8_t *pdata = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE,
206
463
                                                 BVID_PALETTE_SIZE);
207
463
        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
463
        memcpy(pdata, vid->palette, BVID_PALETTE_SIZE);
213
463
        vid->has_palette = 0;
214
463
    }
215
216
190k
    vid->nframes--;  // used to check if all the frames were read
217
190k
fail:
218
190k
    av_free(vidbuf_start);
219
190k
    return ret;
220
190k
}
221
222
static int vid_read_packet(AVFormatContext *s,
223
                           AVPacket *pkt)
224
1.75M
{
225
1.75M
    BVID_DemuxContext *vid = s->priv_data;
226
1.75M
    AVIOContext *pb = s->pb;
227
1.75M
    unsigned char block_type;
228
1.75M
    int audio_length;
229
1.75M
    int ret_value;
230
231
1.75M
    if(vid->is_finished || avio_feof(pb))
232
2.00k
        return AVERROR_EOF;
233
234
1.75M
    block_type = avio_r8(pb);
235
1.75M
    switch(block_type){
236
2.25k
        case PALETTE_BLOCK:
237
2.25k
            if (vid->has_palette) {
238
1.75k
                av_log(s, AV_LOG_WARNING, "discarding unused palette\n");
239
1.75k
                vid->has_palette = 0;
240
1.75k
            }
241
2.25k
            if (avio_read(pb, vid->palette, BVID_PALETTE_SIZE) != BVID_PALETTE_SIZE) {
242
24
                return AVERROR(EIO);
243
24
            }
244
2.23k
            vid->has_palette = 1;
245
2.23k
            return vid_read_packet(s, pkt);
246
247
868
        case FIRST_AUDIO_BLOCK:
248
868
            avio_rl16(pb);
249
            // soundblaster DAC used for sample rate, as on specification page (link above)
250
868
            vid->sample_rate = 1000000 / (256 - avio_r8(pb));
251
1.55M
        case AUDIO_BLOCK:
252
1.55M
            if (vid->audio_index < 0) {
253
1.08k
                AVStream *st = avformat_new_stream(s, NULL);
254
1.08k
                if (!st)
255
0
                    return AVERROR(ENOMEM);
256
1.08k
                vid->audio_index                 = st->index;
257
1.08k
                st->codecpar->codec_type            = AVMEDIA_TYPE_AUDIO;
258
1.08k
                st->codecpar->codec_id              = AV_CODEC_ID_PCM_U8;
259
1.08k
                st->codecpar->ch_layout             = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
260
1.08k
                st->codecpar->bits_per_coded_sample = 8;
261
1.08k
                st->codecpar->sample_rate           = vid->sample_rate;
262
1.08k
                st->codecpar->bit_rate              = 8 * st->codecpar->sample_rate;
263
1.08k
                st->start_time                   = 0;
264
1.08k
                avpriv_set_pts_info(st, 64, 1, vid->sample_rate);
265
1.08k
            }
266
1.55M
            audio_length = avio_rl16(pb);
267
1.55M
            if ((ret_value = av_get_packet(pb, pkt, audio_length)) != audio_length) {
268
170
                if (ret_value < 0)
269
101
                    return ret_value;
270
69
                av_log(s, AV_LOG_ERROR, "incomplete audio block\n");
271
69
                return AVERROR(EIO);
272
170
            }
273
1.55M
            pkt->stream_index = vid->audio_index;
274
1.55M
            pkt->duration     = audio_length;
275
1.55M
            pkt->flags |= AV_PKT_FLAG_KEY;
276
1.55M
            return 0;
277
278
29.9k
        case VIDEO_P_FRAME:
279
30.5k
        case VIDEO_YOFF_P_FRAME:
280
190k
        case VIDEO_I_FRAME:
281
190k
            return read_frame(vid, pb, pkt, block_type, s);
282
283
19
        case EOF_BLOCK:
284
19
            if(vid->nframes != 0)
285
16
                av_log(s, AV_LOG_VERBOSE, "reached terminating character but not all frames read.\n");
286
19
            vid->is_finished = 1;
287
19
            return AVERROR(EIO);
288
1.17k
        default:
289
1.17k
            av_log(s, AV_LOG_ERROR, "unknown block (character = %c, decimal = %d, hex = %x)!!!\n",
290
1.17k
                   block_type, block_type, block_type);
291
1.17k
            return AVERROR_INVALIDDATA;
292
1.75M
    }
293
1.75M
}
294
295
const FFInputFormat ff_bethsoftvid_demuxer = {
296
    .p.name         = "bethsoftvid",
297
    .p.long_name    = NULL_IF_CONFIG_SMALL("Bethesda Softworks VID"),
298
    .priv_data_size = sizeof(BVID_DemuxContext),
299
    .read_probe     = vid_probe,
300
    .read_header    = vid_read_header,
301
    .read_packet    = vid_read_packet,
302
};