Coverage Report

Created: 2026-10-28 08:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/bethsoftvideo.c
Line
Count
Source
1
/*
2
 * Bethesda VID video decoder
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 Video Decoder
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/common.h"
32
#include "avcodec.h"
33
#include "bethsoftvideo.h"
34
#include "bytestream.h"
35
#include "codec_internal.h"
36
#include "decode.h"
37
38
typedef struct BethsoftvidContext {
39
    AVFrame *frame;
40
    GetByteContext g;
41
} BethsoftvidContext;
42
43
static av_cold int bethsoftvid_decode_init(AVCodecContext *avctx)
44
775
{
45
775
    BethsoftvidContext *vid = avctx->priv_data;
46
775
    avctx->pix_fmt = AV_PIX_FMT_PAL8;
47
48
775
    vid->frame = av_frame_alloc();
49
775
    if (!vid->frame)
50
0
        return AVERROR(ENOMEM);
51
52
775
    return 0;
53
775
}
54
55
static int set_palette(BethsoftvidContext *ctx, GetByteContext *g)
56
14.8k
{
57
14.8k
    uint32_t *palette = (uint32_t *)ctx->frame->data[1];
58
14.8k
    int a;
59
60
14.8k
    if (bytestream2_get_bytes_left(g) < 256*3)
61
12.9k
        return AVERROR_INVALIDDATA;
62
63
465k
    for(a = 0; a < 256; a++){
64
463k
        palette[a] = 0xFFU << 24 | bytestream2_get_be24u(g) * 4;
65
463k
        palette[a] |= palette[a] >> 6 & 0x30303;
66
463k
    }
67
1.81k
    return 0;
68
14.8k
}
69
70
static int bethsoftvid_decode_frame(AVCodecContext *avctx, AVFrame *rframe,
71
                                    int *got_frame, AVPacket *avpkt)
72
151k
{
73
151k
    BethsoftvidContext * vid = avctx->priv_data;
74
151k
    char block_type;
75
151k
    uint8_t * dst;
76
151k
    uint8_t * frame_end;
77
151k
    int remaining = avctx->width;          // number of bytes remaining on a line
78
151k
    int wrap_to_next_line;
79
151k
    int code, ret;
80
151k
    int yoffset;
81
82
151k
    bytestream2_init(&vid->g, avpkt->data, avpkt->size);
83
151k
    block_type = bytestream2_get_byte(&vid->g);
84
151k
    if (block_type < 1 || block_type > 4)
85
23.6k
        return AVERROR_INVALIDDATA;
86
87
128k
    if ((ret = ff_reget_buffer(avctx, vid->frame, 0)) < 0)
88
6.39k
        return ret;
89
121k
    wrap_to_next_line = vid->frame->linesize[0] - avctx->width;
90
91
121k
    if (avpkt->side_data_elems > 0 &&
92
0
        avpkt->side_data[0].type == AV_PKT_DATA_PALETTE) {
93
0
        GetByteContext g;
94
0
        bytestream2_init(&g, avpkt->side_data[0].data,
95
0
                         avpkt->side_data[0].size);
96
0
        if ((ret = set_palette(vid, &g)) < 0)
97
0
            return ret;
98
0
    }
99
100
121k
    dst = vid->frame->data[0];
101
121k
    frame_end = vid->frame->data[0] + vid->frame->linesize[0] * avctx->height;
102
103
121k
    switch(block_type){
104
14.8k
        case PALETTE_BLOCK: {
105
14.8k
            *got_frame = 0;
106
14.8k
            if ((ret = set_palette(vid, &vid->g)) < 0) {
107
12.9k
                av_log(avctx, AV_LOG_ERROR, "error reading palette\n");
108
12.9k
                return ret;
109
12.9k
            }
110
1.81k
            return bytestream2_tell(&vid->g);
111
14.8k
        }
112
1.21k
        case VIDEO_YOFF_P_FRAME:
113
1.21k
            yoffset = bytestream2_get_le16(&vid->g);
114
1.21k
            if(yoffset >= avctx->height)
115
334
                return AVERROR_INVALIDDATA;
116
882
            dst += vid->frame->linesize[0] * yoffset;
117
882
            av_fallthrough;
118
3.34k
        case VIDEO_P_FRAME:
119
106k
        case VIDEO_I_FRAME:
120
106k
            break;
121
0
        default:
122
0
            return AVERROR_INVALIDDATA;
123
121k
    }
124
125
    // main code
126
121k
    while((code = bytestream2_get_byte(&vid->g))){
127
27.5k
        int length = code & 0x7f;
128
129
        // copy any bytes starting at the current position, and ending at the frame width
130
256k
        while(length > remaining){
131
241k
            if(code < 0x80)
132
147k
                bytestream2_get_buffer(&vid->g, dst, remaining);
133
93.8k
            else if(block_type == VIDEO_I_FRAME)
134
87.1k
                memset(dst, bytestream2_peek_byte(&vid->g), remaining);
135
241k
            length -= remaining;      // decrement the number of bytes to be copied
136
241k
            dst += remaining + wrap_to_next_line;    // skip over extra bytes at end of frame
137
241k
            remaining = avctx->width;
138
241k
            if(dst == frame_end)
139
12.6k
                goto end;
140
241k
        }
141
142
        // copy any remaining bytes after / if line overflows
143
14.8k
        if(code < 0x80)
144
12.3k
            bytestream2_get_buffer(&vid->g, dst, length);
145
2.52k
        else if(block_type == VIDEO_I_FRAME)
146
1.53k
            memset(dst, bytestream2_get_byte(&vid->g), length);
147
14.8k
        remaining -= length;
148
14.8k
        dst += length;
149
14.8k
    }
150
106k
    end:
151
152
106k
    if ((ret = av_frame_ref(rframe, vid->frame)) < 0)
153
0
        return ret;
154
155
106k
    *got_frame = 1;
156
157
106k
    return avpkt->size;
158
106k
}
159
160
static av_cold int bethsoftvid_decode_end(AVCodecContext *avctx)
161
775
{
162
775
    BethsoftvidContext * vid = avctx->priv_data;
163
775
    av_frame_free(&vid->frame);
164
775
    return 0;
165
775
}
166
167
const FFCodec ff_bethsoftvid_decoder = {
168
    .p.name         = "bethsoftvid",
169
    CODEC_LONG_NAME("Bethesda VID video"),
170
    .p.type         = AVMEDIA_TYPE_VIDEO,
171
    .p.id           = AV_CODEC_ID_BETHSOFTVID,
172
    .priv_data_size = sizeof(BethsoftvidContext),
173
    .init           = bethsoftvid_decode_init,
174
    .close          = bethsoftvid_decode_end,
175
    FF_CODEC_DECODE_CB(bethsoftvid_decode_frame),
176
    .p.capabilities = AV_CODEC_CAP_DR1,
177
};