2 * VMware Screen Codec (VMnc) decoder
3 * Copyright (c) 2006 Konstantin Shishkov
5 * This file is part of Libav.
7 * Libav 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.
12 * Libav 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.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with Libav; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 * VMware Screen Codec (VMnc) decoder
25 * As Alex Beregszaszi discovered, this is effectively RFB data dump
31 #include "libavutil/common.h"
32 #include "libavutil/intreadwrite.h"
35 #include "bytestream.h"
38 MAGIC_WMVd
= 0x574D5664,
48 HT_RAW
= 1, // tile is raw
49 HT_BKG
= 2, // background color is present
50 HT_FG
= 4, // foreground color is present
51 HT_SUB
= 8, // subrects are present
52 HT_CLR
= 16 // each subrect has own color
58 typedef struct VmncContext
{
59 AVCodecContext
*avctx
;
73 uint8_t *curbits
, *curmask
;
77 /* read pixel value from stream */
78 static av_always_inline
int vmnc_get_pixel(GetByteContext
*gb
, int bpp
, int be
)
80 switch (bpp
* 2 + be
) {
83 return bytestream2_get_byte(gb
);
85 return bytestream2_get_le16(gb
);
87 return bytestream2_get_be16(gb
);
89 return bytestream2_get_le32(gb
);
91 return bytestream2_get_be32(gb
);
96 static void load_cursor(VmncContext
*c
)
99 const int bpp
= c
->bpp2
;
100 uint8_t *dst8
= c
->curbits
;
101 uint16_t *dst16
= (uint16_t *)c
->curbits
;
102 uint32_t *dst32
= (uint32_t *)c
->curbits
;
104 for (j
= 0; j
< c
->cur_h
; j
++) {
105 for (i
= 0; i
< c
->cur_w
; i
++) {
106 p
= vmnc_get_pixel(&c
->gb
, bpp
, c
->bigendian
);
116 dst16
= (uint16_t*)c
->curmask
;
117 dst32
= (uint32_t*)c
->curmask
;
118 for (j
= 0; j
< c
->cur_h
; j
++) {
119 for (i
= 0; i
< c
->cur_w
; i
++) {
120 p
= vmnc_get_pixel(&c
->gb
, bpp
, c
->bigendian
);
131 static void put_cursor(uint8_t *dst
, int stride
, VmncContext
*c
, int dx
, int dy
)
136 if (c
->width
< c
->cur_x
+ c
->cur_w
)
137 w
= c
->width
- c
->cur_x
;
139 if (c
->height
< c
->cur_y
+ c
->cur_h
)
140 h
= c
->height
- c
->cur_y
;
152 if ((w
< 1) || (h
< 1))
154 dst
+= x
* c
->bpp2
+ y
* stride
;
157 uint8_t *cd
= c
->curbits
, *msk
= c
->curmask
;
158 for (j
= 0; j
< h
; j
++) {
159 for (i
= 0; i
< w
; i
++)
160 dst
[i
] = (dst
[i
] & cd
[i
]) ^ msk
[i
];
165 } else if (c
->bpp2
== 2) {
166 uint16_t *cd
= (uint16_t*)c
->curbits
, *msk
= (uint16_t*)c
->curmask
;
168 for (j
= 0; j
< h
; j
++) {
169 dst2
= (uint16_t*)dst
;
170 for (i
= 0; i
< w
; i
++)
171 dst2
[i
] = (dst2
[i
] & cd
[i
]) ^ msk
[i
];
176 } else if (c
->bpp2
== 4) {
177 uint32_t *cd
= (uint32_t*)c
->curbits
, *msk
= (uint32_t*)c
->curmask
;
179 for (j
= 0; j
< h
; j
++) {
180 dst2
= (uint32_t*)dst
;
181 for (i
= 0; i
< w
; i
++)
182 dst2
[i
] = (dst2
[i
] & cd
[i
]) ^ msk
[i
];
190 /* fill rectangle with given color */
191 static av_always_inline
void paint_rect(uint8_t *dst
, int dx
, int dy
,
192 int w
, int h
, int color
,
196 dst
+= dx
* bpp
+ dy
* stride
;
198 for (j
= 0; j
< h
; j
++) {
199 memset(dst
, color
, w
);
202 } else if (bpp
== 2) {
204 for (j
= 0; j
< h
; j
++) {
205 dst2
= (uint16_t*)dst
;
206 for (i
= 0; i
< w
; i
++)
210 } else if (bpp
== 4) {
212 for (j
= 0; j
< h
; j
++) {
213 dst2
= (uint32_t*)dst
;
214 for (i
= 0; i
< w
; i
++)
221 static av_always_inline
void paint_raw(uint8_t *dst
, int w
, int h
,
222 GetByteContext
*gb
, int bpp
,
226 for (j
= 0; j
< h
; j
++) {
227 for (i
= 0; i
< w
; i
++) {
228 p
= vmnc_get_pixel(gb
, bpp
, be
);
234 ((uint16_t*)dst
)[i
] = p
;
237 ((uint32_t*)dst
)[i
] = p
;
245 static int decode_hextile(VmncContext
*c
, uint8_t* dst
, GetByteContext
*gb
,
246 int w
, int h
, int stride
)
249 int bg
= 0, fg
= 0, rects
, color
, flags
, xy
, wh
;
250 const int bpp
= c
->bpp2
;
252 int bw
= 16, bh
= 16;
254 for (j
= 0; j
< h
; j
+= 16) {
259 for (i
= 0; i
< w
; i
+= 16, dst2
+= 16 * bpp
) {
260 if (bytestream2_get_bytes_left(gb
) <= 0) {
261 av_log(c
->avctx
, AV_LOG_ERROR
, "Premature end of data!\n");
262 return AVERROR_INVALIDDATA
;
266 flags
= bytestream2_get_byte(gb
);
267 if (flags
& HT_RAW
) {
268 if (bytestream2_get_bytes_left(gb
) < bw
* bh
* bpp
) {
269 av_log(c
->avctx
, AV_LOG_ERROR
, "Premature end of data!\n");
270 return AVERROR_INVALIDDATA
;
272 paint_raw(dst2
, bw
, bh
, gb
, bpp
, c
->bigendian
, stride
);
275 bg
= vmnc_get_pixel(gb
, bpp
, c
->bigendian
);
277 fg
= vmnc_get_pixel(gb
, bpp
, c
->bigendian
);
280 rects
= bytestream2_get_byte(gb
);
281 color
= !!(flags
& HT_CLR
);
283 paint_rect(dst2
, 0, 0, bw
, bh
, bg
, bpp
, stride
);
285 if (bytestream2_get_bytes_left(gb
) < rects
* (color
* bpp
+ 2)) {
286 av_log(c
->avctx
, AV_LOG_ERROR
, "Premature end of data!\n");
287 return AVERROR_INVALIDDATA
;
289 for (k
= 0; k
< rects
; k
++) {
290 int rect_x
, rect_y
, rect_w
, rect_h
;
292 fg
= vmnc_get_pixel(gb
, bpp
, c
->bigendian
);
293 xy
= bytestream2_get_byte(gb
);
294 wh
= bytestream2_get_byte(gb
);
298 rect_w
= (wh
>> 4) + 1;
299 rect_h
= (wh
& 0xF) + 1;
301 if (rect_x
+ rect_w
> bw
|| rect_y
+ rect_h
> bh
) {
302 av_log(c
->avctx
, AV_LOG_ERROR
, "Invalid subrect\n");
303 return AVERROR_INVALIDDATA
;
306 paint_rect(dst2
, rect_x
, rect_y
,
307 rect_w
, rect_h
, fg
, bpp
, stride
);
316 static void reset_buffers(VmncContext
*c
)
318 av_freep(&c
->curbits
);
319 av_freep(&c
->curmask
);
320 av_freep(&c
->screendta
);
321 c
->cur_w
= c
->cur_h
= 0;
324 static int decode_frame(AVCodecContext
*avctx
, void *data
, int *got_frame
,
327 const uint8_t *buf
= avpkt
->data
;
328 int buf_size
= avpkt
->size
;
329 VmncContext
* const c
= avctx
->priv_data
;
330 GetByteContext
*gb
= &c
->gb
;
332 int dx
, dy
, w
, h
, depth
, enc
, chunks
, res
, size_left
, ret
;
334 if ((ret
= ff_reget_buffer(avctx
, c
->pic
)) < 0) {
335 av_log(avctx
, AV_LOG_ERROR
, "reget_buffer() failed\n");
339 bytestream2_init(gb
, buf
, buf_size
);
341 c
->pic
->key_frame
= 0;
342 c
->pic
->pict_type
= AV_PICTURE_TYPE_P
;
344 // restore screen after cursor
348 if (c
->width
< c
->cur_x
+ w
)
349 w
= c
->width
- c
->cur_x
;
351 if (c
->height
< c
->cur_y
+ h
)
352 h
= c
->height
- c
->cur_y
;
363 if ((w
> 0) && (h
> 0)) {
364 outptr
= c
->pic
->data
[0] + dx
* c
->bpp2
+ dy
* c
->pic
->linesize
[0];
365 for (i
= 0; i
< h
; i
++) {
366 memcpy(outptr
, c
->screendta
+ i
* c
->cur_w
* c
->bpp2
,
368 outptr
+= c
->pic
->linesize
[0];
372 bytestream2_skip(gb
, 2);
373 chunks
= bytestream2_get_be16(gb
);
375 dx
= bytestream2_get_be16(gb
);
376 dy
= bytestream2_get_be16(gb
);
377 w
= bytestream2_get_be16(gb
);
378 h
= bytestream2_get_be16(gb
);
379 enc
= bytestream2_get_be32(gb
);
380 outptr
= c
->pic
->data
[0] + dx
* c
->bpp2
+ dy
* c
->pic
->linesize
[0];
381 size_left
= bytestream2_get_bytes_left(gb
);
383 case MAGIC_WMVd
: // cursor
384 if (size_left
< 2 + w
* h
* c
->bpp2
* 2) {
385 av_log(avctx
, AV_LOG_ERROR
,
386 "Premature end of data! (need %i got %i)\n",
387 2 + w
* h
* c
->bpp2
* 2, size_left
);
388 return AVERROR_INVALIDDATA
;
390 bytestream2_skip(gb
, 2);
395 if ((c
->cur_hx
> c
->cur_w
) || (c
->cur_hy
> c
->cur_h
)) {
396 av_log(avctx
, AV_LOG_ERROR
,
397 "Cursor hot spot is not in image: "
398 "%ix%i of %ix%i cursor size\n",
399 c
->cur_hx
, c
->cur_hy
, c
->cur_w
, c
->cur_h
);
400 c
->cur_hx
= c
->cur_hy
= 0;
402 if (c
->cur_w
* c
->cur_h
>= INT_MAX
/ c
->bpp2
) {
404 return AVERROR(EINVAL
);
406 int screen_size
= c
->cur_w
* c
->cur_h
* c
->bpp2
;
407 if ((ret
= av_reallocp(&c
->curbits
, screen_size
)) < 0 ||
408 (ret
= av_reallocp(&c
->curmask
, screen_size
)) < 0 ||
409 (ret
= av_reallocp(&c
->screendta
, screen_size
)) < 0) {
416 case MAGIC_WMVe
: // unknown
417 bytestream2_skip(gb
, 2);
419 case MAGIC_WMVf
: // update cursor position
420 c
->cur_x
= dx
- c
->cur_hx
;
421 c
->cur_y
= dy
- c
->cur_hy
;
423 case MAGIC_WMVg
: // unknown
424 bytestream2_skip(gb
, 10);
426 case MAGIC_WMVh
: // unknown
427 bytestream2_skip(gb
, 4);
429 case MAGIC_WMVi
: // ServerInitialization struct
430 c
->pic
->key_frame
= 1;
431 c
->pic
->pict_type
= AV_PICTURE_TYPE_I
;
432 depth
= bytestream2_get_byte(gb
);
433 if (depth
!= c
->bpp
) {
434 av_log(avctx
, AV_LOG_WARNING
, "Depth mismatch. "
435 "Container %i bpp / Codec %i bpp\n", c
->bpp
, depth
);
437 if (depth
!= 8 && depth
!= 16 && depth
!= 32) {
438 av_log(avctx
, AV_LOG_ERROR
,
439 "Unsupported codec bitdepth %i\n", depth
);
440 return AVERROR_INVALIDDATA
;
445 c
->bpp2
= c
->bpp
/ 8;
447 bytestream2_skip(gb
, 1);
448 c
->bigendian
= bytestream2_get_byte(gb
);
449 if (c
->bigendian
& (~1)) {
450 av_log(avctx
, AV_LOG_INFO
,
451 "Invalid header: bigendian flag = %i\n", c
->bigendian
);
452 return AVERROR_INVALIDDATA
;
454 //skip the rest of pixel format data
455 bytestream2_skip(gb
, 13);
457 case MAGIC_WMVj
: // unknown
458 bytestream2_skip(gb
, 2);
460 case 0x00000000: // raw rectangle data
461 if ((dx
+ w
> c
->width
) || (dy
+ h
> c
->height
)) {
462 av_log(avctx
, AV_LOG_ERROR
,
463 "Incorrect frame size: %ix%i+%ix%i of %ix%i\n",
464 w
, h
, dx
, dy
, c
->width
, c
->height
);
465 return AVERROR_INVALIDDATA
;
467 if (size_left
< w
* h
* c
->bpp2
) {
468 av_log(avctx
, AV_LOG_ERROR
,
469 "Premature end of data! (need %i got %i)\n",
470 w
* h
* c
->bpp2
, size_left
);
471 return AVERROR_INVALIDDATA
;
473 paint_raw(outptr
, w
, h
, gb
, c
->bpp2
, c
->bigendian
,
474 c
->pic
->linesize
[0]);
476 case 0x00000005: // HexTile encoded rectangle
477 if ((dx
+ w
> c
->width
) || (dy
+ h
> c
->height
)) {
478 av_log(avctx
, AV_LOG_ERROR
,
479 "Incorrect frame size: %ix%i+%ix%i of %ix%i\n",
480 w
, h
, dx
, dy
, c
->width
, c
->height
);
481 return AVERROR_INVALIDDATA
;
483 res
= decode_hextile(c
, outptr
, gb
, w
, h
, c
->pic
->linesize
[0]);
488 av_log(avctx
, AV_LOG_ERROR
, "Unsupported block type 0x%08X\n", enc
);
489 chunks
= 0; // leave chunks decoding loop
494 // save screen data before painting cursor
496 if (c
->width
< c
->cur_x
+ w
)
497 w
= c
->width
- c
->cur_x
;
499 if (c
->height
< c
->cur_y
+ h
)
500 h
= c
->height
- c
->cur_y
;
511 if ((w
> 0) && (h
> 0)) {
512 outptr
= c
->pic
->data
[0] + dx
* c
->bpp2
+ dy
* c
->pic
->linesize
[0];
513 for (i
= 0; i
< h
; i
++) {
514 memcpy(c
->screendta
+ i
* c
->cur_w
* c
->bpp2
, outptr
,
516 outptr
+= c
->pic
->linesize
[0];
518 outptr
= c
->pic
->data
[0];
519 put_cursor(outptr
, c
->pic
->linesize
[0], c
, c
->cur_x
, c
->cur_y
);
523 if ((ret
= av_frame_ref(data
, c
->pic
)) < 0)
526 /* always report that the buffer was completely consumed */
530 static av_cold
int decode_init(AVCodecContext
*avctx
)
532 VmncContext
* const c
= avctx
->priv_data
;
535 c
->width
= avctx
->width
;
536 c
->height
= avctx
->height
;
537 c
->bpp
= avctx
->bits_per_coded_sample
;
538 c
->bpp2
= c
->bpp
/ 8;
542 avctx
->pix_fmt
= AV_PIX_FMT_PAL8
;
545 avctx
->pix_fmt
= AV_PIX_FMT_RGB555
;
548 /* 24 bits is not technically supported, but some clients might
549 * mistakenly set it -- delay the actual check until decode_frame() */
551 avctx
->pix_fmt
= AV_PIX_FMT_RGB32
;
554 av_log(avctx
, AV_LOG_ERROR
, "Unsupported bitdepth %i\n", c
->bpp
);
555 return AVERROR_INVALIDDATA
;
558 c
->pic
= av_frame_alloc();
560 return AVERROR(ENOMEM
);
565 static av_cold
int decode_end(AVCodecContext
*avctx
)
567 VmncContext
* const c
= avctx
->priv_data
;
569 av_frame_free(&c
->pic
);
573 av_free(c
->screendta
);
577 AVCodec ff_vmnc_decoder
= {
579 .long_name
= NULL_IF_CONFIG_SMALL("VMware Screen Codec / VMware Video"),
580 .type
= AVMEDIA_TYPE_VIDEO
,
581 .id
= AV_CODEC_ID_VMNC
,
582 .priv_data_size
= sizeof(VmncContext
),
585 .decode
= decode_frame
,
586 .capabilities
= AV_CODEC_CAP_DR1
,