Coverage Report

Created: 2026-05-23 07:06

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