Coverage Report

Created: 2026-05-16 07:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/wcmv.c
Line
Count
Source
1
/*
2
 * WinCAM Motion Video decoder
3
 *
4
 * Copyright (c) 2018 Paul B Mahol
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 <stdio.h>
24
25
#include "libavutil/imgutils.h"
26
27
#include "avcodec.h"
28
#include "bytestream.h"
29
#include "codec_internal.h"
30
#include "decode.h"
31
#include "zlib_wrapper.h"
32
33
#include <zlib.h>
34
35
typedef struct WCMVContext {
36
    int         bpp;
37
    FFZStream   zstream;
38
    AVFrame    *prev_frame;
39
    uint8_t     block_data[65536*8];
40
} WCMVContext;
41
42
static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
43
                        int *got_frame, AVPacket *avpkt)
44
153k
{
45
153k
    WCMVContext *s = avctx->priv_data;
46
153k
    z_stream *const zstream = &s->zstream.zstream;
47
153k
    int skip, blocks, zret, ret, intra = 0, flags = 0, bpp = s->bpp;
48
153k
    GetByteContext gb;
49
153k
    uint8_t *dst;
50
51
153k
    ret = inflateReset(zstream);
52
153k
    if (ret != Z_OK) {
53
0
        av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret);
54
0
        return AVERROR_EXTERNAL;
55
0
    }
56
57
153k
    bytestream2_init(&gb, avpkt->data, avpkt->size);
58
153k
    blocks = bytestream2_get_le16(&gb);
59
153k
    if (!blocks)
60
110k
        flags |= FF_REGET_BUFFER_FLAG_READONLY;
61
62
153k
    if ((ret = ff_reget_buffer(avctx, s->prev_frame, flags)) < 0)
63
20.7k
        return ret;
64
65
132k
    if (blocks > 5) {
66
24.1k
        GetByteContext bgb;
67
24.1k
        int x = 0, size;
68
69
24.1k
        if (blocks * 8 >= 0xFFFF)
70
3.83k
            size = bytestream2_get_le24(&gb);
71
20.2k
        else if (blocks * 8 >= 0xFF)
72
7.08k
            size = bytestream2_get_le16(&gb);
73
13.1k
        else
74
13.1k
            size = bytestream2_get_byte(&gb);
75
76
24.1k
        skip = bytestream2_tell(&gb);
77
24.1k
        if (size > avpkt->size - skip)
78
6.25k
            return AVERROR_INVALIDDATA;
79
80
17.8k
        zstream->next_in   = avpkt->data + skip;
81
17.8k
        zstream->avail_in  = size;
82
17.8k
        zstream->next_out  = s->block_data;
83
17.8k
        zstream->avail_out = sizeof(s->block_data);
84
85
17.8k
        zret = inflate(zstream, Z_FINISH);
86
17.8k
        if (zret != Z_STREAM_END) {
87
15.3k
            av_log(avctx, AV_LOG_ERROR,
88
15.3k
                   "Inflate failed with return code: %d.\n", zret);
89
15.3k
            return AVERROR_INVALIDDATA;
90
15.3k
        }
91
92
2.54k
        ret = inflateReset(zstream);
93
2.54k
        if (ret != Z_OK) {
94
0
            av_log(avctx, AV_LOG_ERROR, "Inflate reset error: %d\n", ret);
95
0
            return AVERROR_EXTERNAL;
96
0
        }
97
98
2.54k
        bytestream2_skip(&gb, size);
99
2.54k
        bytestream2_init(&bgb, s->block_data, blocks * 8);
100
101
462k
        for (int i = 0; i < blocks; i++) {
102
460k
            int w, h;
103
104
460k
            bytestream2_skip(&bgb, 4);
105
460k
            w = bytestream2_get_le16(&bgb);
106
460k
            h = bytestream2_get_le16(&bgb);
107
460k
            if (x + bpp * (int64_t)w * h > INT_MAX)
108
211
                return AVERROR_INVALIDDATA;
109
459k
            x += bpp * w * h;
110
459k
        }
111
112
2.33k
        if (x >= 0xFFFF)
113
539
            bytestream2_skip(&gb, 3);
114
1.79k
        else if (x >= 0xFF)
115
636
            bytestream2_skip(&gb, 2);
116
1.15k
        else
117
1.15k
            bytestream2_skip(&gb, 1);
118
119
2.33k
        skip = bytestream2_tell(&gb);
120
121
2.33k
        zstream->next_in  = avpkt->data + skip;
122
2.33k
        zstream->avail_in = avpkt->size - skip;
123
124
2.33k
        bytestream2_init(&gb, s->block_data, blocks * 8);
125
108k
    } else if (blocks) {
126
15.4k
        int x = 0;
127
128
15.4k
        bytestream2_seek(&gb, 2, SEEK_SET);
129
130
31.8k
        for (int i = 0; i < blocks; i++) {
131
16.7k
            int w, h;
132
133
16.7k
            bytestream2_skip(&gb, 4);
134
16.7k
            w = bytestream2_get_le16(&gb);
135
16.7k
            h = bytestream2_get_le16(&gb);
136
16.7k
            if (x + bpp * (int64_t)w * h > INT_MAX)
137
420
                return AVERROR_INVALIDDATA;
138
16.3k
            x += bpp * w * h;
139
16.3k
        }
140
141
15.0k
        if (x >= 0xFFFF)
142
1.74k
            bytestream2_skip(&gb, 3);
143
13.2k
        else if (x >= 0xFF)
144
3.65k
            bytestream2_skip(&gb, 2);
145
9.62k
        else
146
9.62k
            bytestream2_skip(&gb, 1);
147
148
15.0k
        skip = bytestream2_tell(&gb);
149
150
15.0k
        zstream->next_in  = avpkt->data + skip;
151
15.0k
        zstream->avail_in = avpkt->size - skip;
152
153
15.0k
        bytestream2_seek(&gb, 2, SEEK_SET);
154
15.0k
    }
155
156
110k
    if (bytestream2_get_bytes_left(&gb) < 8LL * blocks)
157
1.00k
        return AVERROR_INVALIDDATA;
158
159
109k
    if (!avctx->frame_num) {
160
9.41k
        ptrdiff_t linesize[4] = { s->prev_frame->linesize[0], 0, 0, 0 };
161
9.41k
        av_image_fill_black(s->prev_frame->data, linesize, avctx->pix_fmt, 0,
162
9.41k
                            avctx->width, avctx->height);
163
9.41k
    }
164
165
548k
    for (int block = 0; block < blocks; block++) {
166
449k
        int x, y, w, h;
167
168
449k
        x = bytestream2_get_le16(&gb);
169
449k
        y = bytestream2_get_le16(&gb);
170
449k
        w = bytestream2_get_le16(&gb);
171
449k
        h = bytestream2_get_le16(&gb);
172
173
449k
        if (blocks == 1 && x == 0 && y == 0 && w == avctx->width && h == avctx->height)
174
671
            intra = 1;
175
176
449k
        if (x + w > avctx->width || y + h > avctx->height)
177
3.95k
            return AVERROR_INVALIDDATA;
178
179
445k
        if (w > avctx->width || h > avctx->height)
180
0
            return AVERROR_INVALIDDATA;
181
182
445k
        dst = s->prev_frame->data[0] + (avctx->height - y - 1) * s->prev_frame->linesize[0] + x * bpp;
183
827k
        for (int i = 0; i < h; i++) {
184
388k
            zstream->next_out  = dst;
185
388k
            zstream->avail_out = w * bpp;
186
187
388k
            zret = inflate(zstream, Z_SYNC_FLUSH);
188
388k
            if (zret != Z_OK && zret != Z_STREAM_END) {
189
6.28k
                av_log(avctx, AV_LOG_ERROR,
190
6.28k
                       "Inflate failed with return code: %d.\n", zret);
191
6.28k
                return AVERROR_INVALIDDATA;
192
6.28k
            }
193
194
381k
            dst -= s->prev_frame->linesize[0];
195
381k
        }
196
445k
    }
197
198
99.2k
    if (intra)
199
205
        s->prev_frame->flags |= AV_FRAME_FLAG_KEY;
200
99.0k
    else
201
99.0k
        s->prev_frame->flags &= ~AV_FRAME_FLAG_KEY;
202
99.2k
    s->prev_frame->pict_type = intra ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
203
204
99.2k
    if ((ret = av_frame_ref(frame, s->prev_frame)) < 0)
205
0
        return ret;
206
207
99.2k
    *got_frame = 1;
208
209
99.2k
    return avpkt->size;
210
99.2k
}
211
212
static av_cold int decode_init(AVCodecContext *avctx)
213
1.45k
{
214
1.45k
    WCMVContext *s = avctx->priv_data;
215
216
1.45k
    switch (avctx->bits_per_coded_sample) {
217
757
    case 16: avctx->pix_fmt = AV_PIX_FMT_RGB565LE; break;
218
156
    case 24: avctx->pix_fmt = AV_PIX_FMT_BGR24;  break;
219
420
    case 32: avctx->pix_fmt = AV_PIX_FMT_BGRA;   break;
220
121
    default: av_log(avctx, AV_LOG_ERROR, "Unsupported bits_per_coded_sample: %d\n",
221
121
                    avctx->bits_per_coded_sample);
222
121
             return AVERROR_PATCHWELCOME;
223
1.45k
    }
224
225
1.33k
    s->bpp = avctx->bits_per_coded_sample >> 3;
226
227
1.33k
    s->prev_frame = av_frame_alloc();
228
1.33k
    if (!s->prev_frame)
229
0
        return AVERROR(ENOMEM);
230
231
1.33k
    return ff_inflate_init(&s->zstream, avctx);
232
1.33k
}
233
234
static av_cold int decode_close(AVCodecContext *avctx)
235
1.45k
{
236
1.45k
    WCMVContext *s = avctx->priv_data;
237
238
1.45k
    av_frame_free(&s->prev_frame);
239
1.45k
    ff_inflate_end(&s->zstream);
240
241
1.45k
    return 0;
242
1.45k
}
243
244
const FFCodec ff_wcmv_decoder = {
245
    .p.name           = "wcmv",
246
    CODEC_LONG_NAME("WinCAM Motion Video"),
247
    .p.type           = AVMEDIA_TYPE_VIDEO,
248
    .p.id             = AV_CODEC_ID_WCMV,
249
    .priv_data_size   = sizeof(WCMVContext),
250
    .init             = decode_init,
251
    .close            = decode_close,
252
    FF_CODEC_DECODE_CB(decode_frame),
253
    .p.capabilities   = AV_CODEC_CAP_DR1,
254
    .caps_internal    = FF_CODEC_CAP_INIT_CLEANUP,
255
};