Coverage Report

Created: 2025-08-28 07:12

/src/ffmpeg/libavcodec/vmnc.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * VMware Screen Codec (VMnc) decoder
3
 * Copyright (c) 2006 Konstantin Shishkov
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
 * VMware Screen Codec (VMnc) decoder
25
 * As Alex Beregszaszi discovered, this is effectively RFB data dump
26
 */
27
28
#include "libavutil/common.h"
29
#include "libavutil/mem.h"
30
#include "avcodec.h"
31
#include "codec_internal.h"
32
#include "decode.h"
33
#include "bytestream.h"
34
35
enum EncTypes {
36
    MAGIC_WMVd = 0x574D5664,
37
    MAGIC_WMVe,
38
    MAGIC_WMVf,
39
    MAGIC_WMVg,
40
    MAGIC_WMVh,
41
    MAGIC_WMVi,
42
    MAGIC_WMVj
43
};
44
45
enum HexTile_Flags {
46
    HT_RAW =  1, // tile is raw
47
    HT_BKG =  2, // background color is present
48
    HT_FG  =  4, // foreground color is present
49
    HT_SUB =  8, // subrects are present
50
    HT_CLR = 16  // each subrect has own color
51
};
52
53
/*
54
 * Decoder context
55
 */
56
typedef struct VmncContext {
57
    AVCodecContext *avctx;
58
    AVFrame *pic;
59
60
    int bpp;
61
    int bpp2;
62
    int bigendian;
63
    uint8_t pal[768];
64
    int width, height;
65
    GetByteContext gb;
66
67
    /* cursor data */
68
    int cur_w, cur_h;
69
    int cur_x, cur_y;
70
    int cur_hx, cur_hy;
71
    uint8_t *curbits, *curmask;
72
    uint8_t *screendta;
73
} VmncContext;
74
75
/* read pixel value from stream */
76
static av_always_inline int vmnc_get_pixel(GetByteContext *gb, int bpp, int be)
77
1.49M
{
78
1.49M
    switch (bpp * 2 + be) {
79
1.06M
    case 2:
80
1.06M
    case 3:
81
1.06M
        return bytestream2_get_byte(gb);
82
92.6k
    case 4:
83
92.6k
        return bytestream2_get_le16(gb);
84
55.6k
    case 5:
85
55.6k
        return bytestream2_get_be16(gb);
86
123k
    case 8:
87
123k
        return bytestream2_get_le32(gb);
88
96.6k
    case 9:
89
96.6k
        return bytestream2_get_be32(gb);
90
58.1k
    default: return 0;
91
1.49M
    }
92
1.49M
}
93
94
static void load_cursor(VmncContext *c)
95
2.39k
{
96
2.39k
    int i, j, p;
97
2.39k
    const int bpp   = c->bpp2;
98
2.39k
    uint8_t *dst8   =             c->curbits;
99
2.39k
    uint16_t *dst16 = (uint16_t *)c->curbits;
100
2.39k
    uint32_t *dst32 = (uint32_t *)c->curbits;
101
102
1.05M
    for (j = 0; j < c->cur_h; j++) {
103
1.60M
        for (i = 0; i < c->cur_w; i++) {
104
557k
            p = vmnc_get_pixel(&c->gb, bpp, c->bigendian);
105
557k
            if (bpp == 1)
106
525k
                *dst8++ = p;
107
557k
            if (bpp == 2)
108
16.3k
                *dst16++ = p;
109
557k
            if (bpp == 4)
110
15.4k
                *dst32++ = p;
111
557k
        }
112
1.05M
    }
113
2.39k
    dst8  =            c->curmask;
114
2.39k
    dst16 = (uint16_t*)c->curmask;
115
2.39k
    dst32 = (uint32_t*)c->curmask;
116
1.05M
    for (j = 0; j < c->cur_h; j++) {
117
1.60M
        for (i = 0; i < c->cur_w; i++) {
118
557k
            p = vmnc_get_pixel(&c->gb, bpp, c->bigendian);
119
557k
            if (bpp == 1)
120
525k
                *dst8++ = p;
121
557k
            if (bpp == 2)
122
16.3k
                *dst16++ = p;
123
557k
            if (bpp == 4)
124
15.4k
                *dst32++ = p;
125
557k
        }
126
1.05M
    }
127
2.39k
}
128
129
static void put_cursor(uint8_t *dst, int stride, VmncContext *c, int dx, int dy)
130
2.64k
{
131
2.64k
    int i, j;
132
2.64k
    int w, h, x, y;
133
2.64k
    w = c->cur_w;
134
2.64k
    if (c->width < c->cur_x + c->cur_w)
135
314
        w = c->width - c->cur_x;
136
2.64k
    h = c->cur_h;
137
2.64k
    if (c->height < c->cur_y + c->cur_h)
138
240
        h = c->height - c->cur_y;
139
2.64k
    x = c->cur_x;
140
2.64k
    y = c->cur_y;
141
2.64k
    if (x < 0) {
142
262
        w += x;
143
262
        x  = 0;
144
262
    }
145
2.64k
    if (y < 0) {
146
303
        h += y;
147
303
        y  = 0;
148
303
    }
149
150
2.64k
    if ((w < 1) || (h < 1))
151
0
        return;
152
2.64k
    dst += x * c->bpp2 + y * stride;
153
154
2.64k
    if (c->bpp2 == 1) {
155
1.51k
        uint8_t *cd = c->curbits, *msk = c->curmask;
156
259k
        for (j = 0; j < h; j++) {
157
729k
            for (i = 0; i < w; i++)
158
472k
                dst[i] = (dst[i] & cd[i]) ^ msk[i];
159
257k
            msk += c->cur_w;
160
257k
            cd  += c->cur_w;
161
257k
            dst += stride;
162
257k
        }
163
1.51k
    } else if (c->bpp2 == 2) {
164
473
        uint16_t *cd = (uint16_t*)c->curbits, *msk = (uint16_t*)c->curmask;
165
473
        uint16_t *dst2;
166
1.74k
        for (j = 0; j < h; j++) {
167
1.27k
            dst2 = (uint16_t*)dst;
168
14.2k
            for (i = 0; i < w; i++)
169
12.9k
                dst2[i] = (dst2[i] & cd[i]) ^ msk[i];
170
1.27k
            msk += c->cur_w;
171
1.27k
            cd  += c->cur_w;
172
1.27k
            dst += stride;
173
1.27k
        }
174
657
    } else if (c->bpp2 == 4) {
175
657
        uint32_t *cd = (uint32_t*)c->curbits, *msk = (uint32_t*)c->curmask;
176
657
        uint32_t *dst2;
177
2.88k
        for (j = 0; j < h; j++) {
178
2.22k
            dst2 = (uint32_t*)dst;
179
17.6k
            for (i = 0; i < w; i++)
180
15.4k
                dst2[i] = (dst2[i] & cd[i]) ^ msk[i];
181
2.22k
            msk += c->cur_w;
182
2.22k
            cd  += c->cur_w;
183
2.22k
            dst += stride;
184
2.22k
        }
185
657
    }
186
2.64k
}
187
188
/* fill rectangle with given color */
189
static av_always_inline void paint_rect(uint8_t *dst, int dx, int dy,
190
                                        int w, int h, int color,
191
                                        int bpp, int stride)
192
135k
{
193
135k
    int i, j;
194
135k
    dst += dx * bpp + dy * stride;
195
135k
    if (bpp == 1) {
196
774k
        for (j = 0; j < h; j++) {
197
694k
            memset(dst, color, w);
198
694k
            dst += stride;
199
694k
        }
200
80.6k
    } else if (bpp == 2) {
201
33.6k
        uint16_t *dst2;
202
342k
        for (j = 0; j < h; j++) {
203
308k
            dst2 = (uint16_t*)dst;
204
4.19M
            for (i = 0; i < w; i++)
205
3.88M
                *dst2++ = color;
206
308k
            dst += stride;
207
308k
        }
208
33.6k
    } else if (bpp == 4) {
209
21.1k
        uint32_t *dst2;
210
184k
        for (j = 0; j < h; j++) {
211
163k
            dst2 = (uint32_t*)dst;
212
2.06M
            for (i = 0; i < w; i++)
213
1.90M
                dst2[i] = color;
214
163k
            dst += stride;
215
163k
        }
216
21.1k
    }
217
135k
}
218
219
static av_always_inline void paint_raw(uint8_t *dst, int w, int h,
220
                                       GetByteContext *gb, int bpp,
221
                                       int be, int stride)
222
4.58k
{
223
4.58k
    int i, j, p;
224
874k
    for (j = 0; j < h; j++) {
225
1.20M
        for (i = 0; i < w; i++) {
226
331k
            p = vmnc_get_pixel(gb, bpp, be);
227
331k
            switch (bpp) {
228
158k
            case 1:
229
158k
                dst[i] = p;
230
158k
                break;
231
54.7k
            case 2:
232
54.7k
                ((uint16_t*)dst)[i] = p;
233
54.7k
                break;
234
118k
            case 4:
235
118k
                ((uint32_t*)dst)[i] = p;
236
118k
                break;
237
331k
            }
238
331k
        }
239
869k
        dst += stride;
240
869k
    }
241
4.58k
}
242
243
static int decode_hextile(VmncContext *c, uint8_t* dst, GetByteContext *gb,
244
                          int w, int h, int stride)
245
7.34k
{
246
7.34k
    int i, j, k;
247
7.34k
    int bg = 0, fg = 0, rects, color, flags, xy, wh;
248
7.34k
    const int bpp = c->bpp2;
249
7.34k
    uint8_t *dst2;
250
7.34k
    int bw = 16, bh = 16;
251
252
39.6k
    for (j = 0; j < h; j += 16) {
253
38.3k
        dst2 = dst;
254
38.3k
        bw   = 16;
255
38.3k
        if (j + 16 > h)
256
2.91k
            bh = h - j;
257
85.8k
        for (i = 0; i < w; i += 16, dst2 += 16 * bpp) {
258
53.5k
            if (bytestream2_get_bytes_left(gb) <= 0) {
259
2.06k
                av_log(c->avctx, AV_LOG_ERROR, "Premature end of data!\n");
260
2.06k
                return AVERROR_INVALIDDATA;
261
2.06k
            }
262
51.4k
            if (i + 16 > w)
263
8.67k
                bw = w - i;
264
51.4k
            flags = bytestream2_get_byte(gb);
265
51.4k
            if (flags & HT_RAW) {
266
5.11k
                if (bytestream2_get_bytes_left(gb) < bw * bh * bpp) {
267
1.80k
                    av_log(c->avctx, AV_LOG_ERROR, "Premature end of data!\n");
268
1.80k
                    return AVERROR_INVALIDDATA;
269
1.80k
                }
270
3.31k
                paint_raw(dst2, bw, bh, gb, bpp, c->bigendian, stride);
271
46.3k
            } else {
272
46.3k
                if (flags & HT_BKG)
273
12.5k
                    bg = vmnc_get_pixel(gb, bpp, c->bigendian);
274
46.3k
                if (flags & HT_FG)
275
10.8k
                    fg = vmnc_get_pixel(gb, bpp, c->bigendian);
276
46.3k
                rects = 0;
277
46.3k
                if (flags & HT_SUB)
278
5.81k
                    rects = bytestream2_get_byte(gb);
279
46.3k
                color = !!(flags & HT_CLR);
280
281
46.3k
                paint_rect(dst2, 0, 0, bw, bh, bg, bpp, stride);
282
283
46.3k
                if (bytestream2_get_bytes_left(gb) < rects * (color * bpp + 2)) {
284
1.37k
                    av_log(c->avctx, AV_LOG_ERROR, "Premature end of data!\n");
285
1.37k
                    return AVERROR_INVALIDDATA;
286
1.37k
                }
287
134k
                for (k = 0; k < rects; k++) {
288
89.9k
                    int rect_x, rect_y, rect_w, rect_h;
289
89.9k
                    if (color)
290
22.1k
                        fg = vmnc_get_pixel(gb, bpp, c->bigendian);
291
89.9k
                    xy = bytestream2_get_byte(gb);
292
89.9k
                    wh = bytestream2_get_byte(gb);
293
294
89.9k
                    rect_x = xy >> 4;
295
89.9k
                    rect_y = xy & 0xF;
296
89.9k
                    rect_w = (wh >> 4) + 1;
297
89.9k
                    rect_h = (wh & 0xF) + 1;
298
299
89.9k
                    if (rect_x + rect_w > w - i || rect_y + rect_h > h - j) {
300
835
                        av_log(c->avctx, AV_LOG_ERROR, "Rectangle outside picture\n");
301
835
                        return AVERROR_INVALIDDATA;
302
835
                    }
303
304
89.0k
                    paint_rect(dst2, rect_x, rect_y,
305
89.0k
                               rect_w, rect_h, fg, bpp, stride);
306
89.0k
                }
307
44.9k
            }
308
51.4k
        }
309
32.3k
        dst += stride * 16;
310
32.3k
    }
311
1.27k
    return 0;
312
7.34k
}
313
314
static void reset_buffers(VmncContext *c)
315
0
{
316
0
    av_freep(&c->curbits);
317
0
    av_freep(&c->curmask);
318
0
    av_freep(&c->screendta);
319
0
    c->cur_w = c->cur_h = 0;
320
0
    c->cur_hx = c->cur_hy = 0;
321
322
0
}
323
324
static int decode_frame(AVCodecContext *avctx, AVFrame *rframe,
325
                        int *got_frame, AVPacket *avpkt)
326
148k
{
327
148k
    const uint8_t *buf = avpkt->data;
328
148k
    int buf_size       = avpkt->size;
329
148k
    VmncContext * const c = avctx->priv_data;
330
148k
    GetByteContext *gb = &c->gb;
331
148k
    uint8_t *outptr;
332
148k
    int dx, dy, w, h, depth, enc, chunks, res, size_left, ret;
333
334
148k
    bytestream2_init(gb, buf, buf_size);
335
148k
    bytestream2_skip(gb, 2);
336
148k
    chunks = bytestream2_get_be16(gb);
337
148k
    if (12LL * chunks > bytestream2_get_bytes_left(gb))
338
6.46k
        return AVERROR_INVALIDDATA;
339
340
141k
    if ((ret = ff_reget_buffer(avctx, c->pic, 0)) < 0)
341
8.46k
        return ret;
342
343
133k
    c->pic->flags &= ~AV_FRAME_FLAG_KEY;
344
133k
    c->pic->pict_type = AV_PICTURE_TYPE_P;
345
346
    // restore screen after cursor
347
133k
    if (c->screendta) {
348
5.45k
        int i;
349
5.45k
        w = c->cur_w;
350
5.45k
        if (c->width < c->cur_x + w)
351
374
            w = c->width - c->cur_x;
352
5.45k
        h = c->cur_h;
353
5.45k
        if (c->height < c->cur_y + h)
354
348
            h = c->height - c->cur_y;
355
5.45k
        dx = c->cur_x;
356
5.45k
        if (dx < 0) {
357
611
            w += dx;
358
611
            dx = 0;
359
611
        }
360
5.45k
        dy = c->cur_y;
361
5.45k
        if (dy < 0) {
362
601
            h += dy;
363
601
            dy = 0;
364
601
        }
365
5.45k
        if ((w > 0) && (h > 0)) {
366
4.85k
            outptr = c->pic->data[0] + dx * c->bpp2 + dy * c->pic->linesize[0];
367
300k
            for (i = 0; i < h; i++) {
368
295k
                memcpy(outptr, c->screendta + i * c->cur_w * c->bpp2,
369
295k
                       w * c->bpp2);
370
295k
                outptr += c->pic->linesize[0];
371
295k
            }
372
4.85k
        }
373
5.45k
    }
374
375
143k
    while (chunks--) {
376
20.9k
        if (bytestream2_get_bytes_left(gb) < 12) {
377
1.13k
            av_log(avctx, AV_LOG_ERROR, "Premature end of data!\n");
378
1.13k
            return -1;
379
1.13k
        }
380
19.8k
        dx  = bytestream2_get_be16(gb);
381
19.8k
        dy  = bytestream2_get_be16(gb);
382
19.8k
        w   = bytestream2_get_be16(gb);
383
19.8k
        h   = bytestream2_get_be16(gb);
384
19.8k
        enc = bytestream2_get_be32(gb);
385
19.8k
        if ((dx + w > c->width) || (dy + h > c->height)) {
386
2.48k
            av_log(avctx, AV_LOG_ERROR,
387
2.48k
                    "Incorrect frame size: %ix%i+%ix%i of %ix%i\n",
388
2.48k
                    w, h, dx, dy, c->width, c->height);
389
2.48k
            return AVERROR_INVALIDDATA;
390
2.48k
        }
391
17.3k
        outptr = c->pic->data[0] + dx * c->bpp2 + dy * c->pic->linesize[0];
392
17.3k
        size_left = bytestream2_get_bytes_left(gb);
393
17.3k
        switch (enc) {
394
2.68k
        case MAGIC_WMVd: // cursor
395
2.68k
            if (w*(int64_t)h*c->bpp2 > INT_MAX/2 - 2) {
396
0
                av_log(avctx, AV_LOG_ERROR, "dimensions too large\n");
397
0
                return AVERROR_INVALIDDATA;
398
0
            }
399
2.68k
            if (size_left < 2 + w * h * c->bpp2 * 2) {
400
295
                av_log(avctx, AV_LOG_ERROR,
401
295
                       "Premature end of data! (need %i got %i)\n",
402
295
                       2 + w * h * c->bpp2 * 2, size_left);
403
295
                return AVERROR_INVALIDDATA;
404
295
            }
405
2.39k
            bytestream2_skip(gb, 2);
406
2.39k
            c->cur_w  = w;
407
2.39k
            c->cur_h  = h;
408
2.39k
            c->cur_hx = dx;
409
2.39k
            c->cur_hy = dy;
410
2.39k
            if ((c->cur_hx > c->cur_w) || (c->cur_hy > c->cur_h)) {
411
1.36k
                av_log(avctx, AV_LOG_ERROR,
412
1.36k
                       "Cursor hot spot is not in image: "
413
1.36k
                       "%ix%i of %ix%i cursor size\n",
414
1.36k
                       c->cur_hx, c->cur_hy, c->cur_w, c->cur_h);
415
1.36k
                c->cur_hx = c->cur_hy = 0;
416
1.36k
            }
417
2.39k
            if (c->cur_w * c->cur_h >= INT_MAX / c->bpp2) {
418
0
                reset_buffers(c);
419
0
                return AVERROR(EINVAL);
420
2.39k
            } else {
421
2.39k
                int screen_size = c->cur_w * c->cur_h * c->bpp2;
422
2.39k
                if ((ret = av_reallocp(&c->curbits, screen_size)) < 0 ||
423
2.39k
                    (ret = av_reallocp(&c->curmask, screen_size)) < 0 ||
424
2.39k
                    (ret = av_reallocp(&c->screendta, screen_size)) < 0) {
425
0
                    reset_buffers(c);
426
0
                    return ret;
427
0
                }
428
2.39k
            }
429
2.39k
            load_cursor(c);
430
2.39k
            break;
431
460
        case MAGIC_WMVe: // unknown
432
460
            bytestream2_skip(gb, 2);
433
460
            break;
434
359
        case MAGIC_WMVf: // update cursor position
435
359
            c->cur_x = dx - c->cur_hx;
436
359
            c->cur_y = dy - c->cur_hy;
437
359
            break;
438
838
        case MAGIC_WMVg: // unknown
439
838
            bytestream2_skip(gb, 10);
440
838
            break;
441
810
        case MAGIC_WMVh: // unknown
442
810
            bytestream2_skip(gb, 4);
443
810
            break;
444
1.42k
        case MAGIC_WMVi: // ServerInitialization struct
445
1.42k
            c->pic->flags |= AV_FRAME_FLAG_KEY;
446
1.42k
            c->pic->pict_type = AV_PICTURE_TYPE_I;
447
1.42k
            depth = bytestream2_get_byte(gb);
448
1.42k
            if (depth != c->bpp) {
449
1.14k
                av_log(avctx, AV_LOG_INFO,
450
1.14k
                       "Depth mismatch. Container %i bpp, "
451
1.14k
                       "Frame data: %i bpp\n",
452
1.14k
                       c->bpp, depth);
453
1.14k
            }
454
1.42k
            bytestream2_skip(gb, 1);
455
1.42k
            c->bigendian = bytestream2_get_byte(gb);
456
1.42k
            if (c->bigendian & (~1)) {
457
563
                av_log(avctx, AV_LOG_INFO,
458
563
                       "Invalid header: bigendian flag = %i\n", c->bigendian);
459
563
                return AVERROR_INVALIDDATA;
460
563
            }
461
            //skip the rest of pixel format data
462
859
            bytestream2_skip(gb, 13);
463
859
            break;
464
636
        case MAGIC_WMVj: // unknown
465
636
            bytestream2_skip(gb, 2);
466
636
            break;
467
1.77k
        case 0x00000000: // raw rectangle data
468
1.77k
            if (size_left < w * h * c->bpp2) {
469
507
                av_log(avctx, AV_LOG_ERROR,
470
507
                       "Premature end of data! (need %i got %i)\n",
471
507
                       w * h * c->bpp2, size_left);
472
507
                return AVERROR_INVALIDDATA;
473
507
            }
474
1.27k
            paint_raw(outptr, w, h, gb, c->bpp2, c->bigendian,
475
1.27k
                      c->pic->linesize[0]);
476
1.27k
            break;
477
7.34k
        case 0x00000005: // HexTile encoded rectangle
478
7.34k
            res = decode_hextile(c, outptr, gb, w, h, c->pic->linesize[0]);
479
7.34k
            if (res < 0)
480
6.07k
                return res;
481
1.27k
            break;
482
1.27k
        default:
483
1.01k
            av_log(avctx, AV_LOG_ERROR, "Unsupported block type 0x%08X\n", enc);
484
1.01k
            chunks = 0; // leave chunks decoding loop
485
17.3k
        }
486
17.3k
    }
487
122k
    if (c->screendta) {
488
3.18k
        int i;
489
        // save screen data before painting cursor
490
3.18k
        w = c->cur_w;
491
3.18k
        if (c->width < c->cur_x + w)
492
351
            w = c->width - c->cur_x;
493
3.18k
        h = c->cur_h;
494
3.18k
        if (c->height < c->cur_y + h)
495
291
            h = c->height - c->cur_y;
496
3.18k
        dx = c->cur_x;
497
3.18k
        if (dx < 0) {
498
546
            w += dx;
499
546
            dx = 0;
500
546
        }
501
3.18k
        dy = c->cur_y;
502
3.18k
        if (dy < 0) {
503
559
            h += dy;
504
559
            dy = 0;
505
559
        }
506
3.18k
        if ((w > 0) && (h > 0)) {
507
2.64k
            outptr = c->pic->data[0] + dx * c->bpp2 + dy * c->pic->linesize[0];
508
263k
            for (i = 0; i < h; i++) {
509
261k
                memcpy(c->screendta + i * c->cur_w * c->bpp2, outptr,
510
261k
                       w * c->bpp2);
511
261k
                outptr += c->pic->linesize[0];
512
261k
            }
513
2.64k
            outptr = c->pic->data[0];
514
2.64k
            put_cursor(outptr, c->pic->linesize[0], c, c->cur_x, c->cur_y);
515
2.64k
        }
516
3.18k
    }
517
122k
    *got_frame = 1;
518
122k
    if ((ret = av_frame_ref(rframe, c->pic)) < 0)
519
0
        return ret;
520
521
    /* always report that the buffer was completely consumed */
522
122k
    return buf_size;
523
122k
}
524
525
static av_cold int decode_init(AVCodecContext *avctx)
526
1.36k
{
527
1.36k
    VmncContext * const c = avctx->priv_data;
528
529
1.36k
    c->avctx  = avctx;
530
1.36k
    c->width  = avctx->width;
531
1.36k
    c->height = avctx->height;
532
1.36k
    c->bpp    = avctx->bits_per_coded_sample;
533
534
1.36k
    switch (c->bpp) {
535
681
    case 8:
536
681
        avctx->pix_fmt = AV_PIX_FMT_PAL8;
537
681
        break;
538
241
    case 16:
539
241
        avctx->pix_fmt = AV_PIX_FMT_RGB555;
540
241
        break;
541
198
    case 24:
542
        /* 24 bits is not technically supported, but some clients might
543
         * mistakenly set it, so let's assume they actually meant 32 bits */
544
198
        c->bpp = 32;
545
304
    case 32:
546
304
        avctx->pix_fmt = AV_PIX_FMT_0RGB32;
547
304
        break;
548
139
    default:
549
139
        av_log(avctx, AV_LOG_ERROR, "Unsupported bitdepth %i\n", c->bpp);
550
139
        return AVERROR_INVALIDDATA;
551
1.36k
    }
552
1.22k
    c->bpp2 = c->bpp / 8;
553
554
1.22k
    c->pic = av_frame_alloc();
555
1.22k
    if (!c->pic)
556
0
        return AVERROR(ENOMEM);
557
558
1.22k
    return 0;
559
1.22k
}
560
561
static av_cold int decode_end(AVCodecContext *avctx)
562
1.22k
{
563
1.22k
    VmncContext * const c = avctx->priv_data;
564
565
1.22k
    av_frame_free(&c->pic);
566
567
1.22k
    av_freep(&c->curbits);
568
1.22k
    av_freep(&c->curmask);
569
1.22k
    av_freep(&c->screendta);
570
1.22k
    return 0;
571
1.22k
}
572
573
const FFCodec ff_vmnc_decoder = {
574
    .p.name         = "vmnc",
575
    CODEC_LONG_NAME("VMware Screen Codec / VMware Video"),
576
    .p.type         = AVMEDIA_TYPE_VIDEO,
577
    .p.id           = AV_CODEC_ID_VMNC,
578
    .priv_data_size = sizeof(VmncContext),
579
    .init           = decode_init,
580
    .close          = decode_end,
581
    FF_CODEC_DECODE_CB(decode_frame),
582
    .p.capabilities = AV_CODEC_CAP_DR1,
583
};