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"
36 MAGIC_WMVd
= 0x574D5664,
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
56 typedef struct VmncContext
{
57 AVCodecContext
*avctx
;
70 uint8_t* curbits
, *curmask
;
74 /* read pixel value from stream */
75 static av_always_inline
int vmnc_get_pixel(const uint8_t* buf
, int bpp
, int be
) {
76 switch(bpp
* 2 + be
) {
79 case 4: return AV_RL16(buf
);
80 case 5: return AV_RB16(buf
);
81 case 8: return AV_RL32(buf
);
82 case 9: return AV_RB32(buf
);
87 static void load_cursor(VmncContext
*c
, const uint8_t *src
)
90 const int bpp
= c
->bpp2
;
91 uint8_t *dst8
= c
->curbits
;
92 uint16_t *dst16
= (uint16_t*)c
->curbits
;
93 uint32_t *dst32
= (uint32_t*)c
->curbits
;
95 for(j
= 0; j
< c
->cur_h
; j
++) {
96 for(i
= 0; i
< c
->cur_w
; i
++) {
97 p
= vmnc_get_pixel(src
, bpp
, c
->bigendian
);
99 if(bpp
== 1) *dst8
++ = p
;
100 if(bpp
== 2) *dst16
++ = p
;
101 if(bpp
== 4) *dst32
++ = p
;
105 dst16
= (uint16_t*)c
->curmask
;
106 dst32
= (uint32_t*)c
->curmask
;
107 for(j
= 0; j
< c
->cur_h
; j
++) {
108 for(i
= 0; i
< c
->cur_w
; i
++) {
109 p
= vmnc_get_pixel(src
, bpp
, c
->bigendian
);
111 if(bpp
== 1) *dst8
++ = p
;
112 if(bpp
== 2) *dst16
++ = p
;
113 if(bpp
== 4) *dst32
++ = p
;
118 static void put_cursor(uint8_t *dst
, int stride
, VmncContext
*c
, int dx
, int dy
)
123 if(c
->width
< c
->cur_x
+ c
->cur_w
) w
= c
->width
- c
->cur_x
;
125 if(c
->height
< c
->cur_y
+ c
->cur_h
) h
= c
->height
- c
->cur_y
;
137 if((w
< 1) || (h
< 1)) return;
138 dst
+= x
* c
->bpp2
+ y
* stride
;
141 uint8_t* cd
= c
->curbits
, *msk
= c
->curmask
;
142 for(j
= 0; j
< h
; j
++) {
143 for(i
= 0; i
< w
; i
++)
144 dst
[i
] = (dst
[i
] & cd
[i
]) ^ msk
[i
];
149 } else if(c
->bpp2
== 2) {
150 uint16_t* cd
= (uint16_t*)c
->curbits
, *msk
= (uint16_t*)c
->curmask
;
152 for(j
= 0; j
< h
; j
++) {
153 dst2
= (uint16_t*)dst
;
154 for(i
= 0; i
< w
; i
++)
155 dst2
[i
] = (dst2
[i
] & cd
[i
]) ^ msk
[i
];
160 } else if(c
->bpp2
== 4) {
161 uint32_t* cd
= (uint32_t*)c
->curbits
, *msk
= (uint32_t*)c
->curmask
;
163 for(j
= 0; j
< h
; j
++) {
164 dst2
= (uint32_t*)dst
;
165 for(i
= 0; i
< w
; i
++)
166 dst2
[i
] = (dst2
[i
] & cd
[i
]) ^ msk
[i
];
174 /* fill rectangle with given color */
175 static av_always_inline
void paint_rect(uint8_t *dst
, int dx
, int dy
, int w
, int h
, int color
, int bpp
, int stride
)
178 dst
+= dx
* bpp
+ dy
* stride
;
180 for(j
= 0; j
< h
; j
++) {
181 memset(dst
, color
, w
);
186 for(j
= 0; j
< h
; j
++) {
187 dst2
= (uint16_t*)dst
;
188 for(i
= 0; i
< w
; i
++) {
195 for(j
= 0; j
< h
; j
++) {
196 dst2
= (uint32_t*)dst
;
197 for(i
= 0; i
< w
; i
++) {
205 static av_always_inline
void paint_raw(uint8_t *dst
, int w
, int h
, const uint8_t* src
, int bpp
, int be
, int stride
)
208 for(j
= 0; j
< h
; j
++) {
209 for(i
= 0; i
< w
; i
++) {
210 p
= vmnc_get_pixel(src
, bpp
, be
);
217 ((uint16_t*)dst
)[i
] = p
;
220 ((uint32_t*)dst
)[i
] = p
;
228 static int decode_hextile(VmncContext
*c
, uint8_t* dst
, const uint8_t* src
, int ssize
, int w
, int h
, int stride
)
231 int bg
= 0, fg
= 0, rects
, color
, flags
, xy
, wh
;
232 const int bpp
= c
->bpp2
;
234 int bw
= 16, bh
= 16;
235 const uint8_t *ssrc
=src
;
237 for(j
= 0; j
< h
; j
+= 16) {
240 if(j
+ 16 > h
) bh
= h
- j
;
241 for(i
= 0; i
< w
; i
+= 16, dst2
+= 16 * bpp
) {
242 if(src
- ssrc
>= ssize
) {
243 av_log(c
->avctx
, AV_LOG_ERROR
, "Premature end of data!\n");
246 if(i
+ 16 > w
) bw
= w
- i
;
249 if(src
- ssrc
> ssize
- bw
* bh
* bpp
) {
250 av_log(c
->avctx
, AV_LOG_ERROR
, "Premature end of data!\n");
253 paint_raw(dst2
, bw
, bh
, src
, bpp
, c
->bigendian
, stride
);
254 src
+= bw
* bh
* bpp
;
257 bg
= vmnc_get_pixel(src
, bpp
, c
->bigendian
); src
+= bpp
;
260 fg
= vmnc_get_pixel(src
, bpp
, c
->bigendian
); src
+= bpp
;
265 color
= !!(flags
& HT_CLR
);
267 paint_rect(dst2
, 0, 0, bw
, bh
, bg
, bpp
, stride
);
269 if(src
- ssrc
> ssize
- rects
* (color
* bpp
+ 2)) {
270 av_log(c
->avctx
, AV_LOG_ERROR
, "Premature end of data!\n");
273 for(k
= 0; k
< rects
; k
++) {
275 fg
= vmnc_get_pixel(src
, bpp
, c
->bigendian
); src
+= bpp
;
279 paint_rect(dst2
, xy
>> 4, xy
& 0xF, (wh
>>4)+1, (wh
& 0xF)+1, fg
, bpp
, stride
);
288 static int decode_frame(AVCodecContext
*avctx
, void *data
, int *got_frame
,
291 const uint8_t *buf
= avpkt
->data
;
292 int buf_size
= avpkt
->size
;
293 VmncContext
* const c
= avctx
->priv_data
;
295 const uint8_t *src
= buf
;
296 int dx
, dy
, w
, h
, depth
, enc
, chunks
, res
, size_left
;
298 c
->pic
.reference
= 1;
299 c
->pic
.buffer_hints
= FF_BUFFER_HINTS_VALID
| FF_BUFFER_HINTS_PRESERVE
| FF_BUFFER_HINTS_REUSABLE
;
300 if(avctx
->reget_buffer(avctx
, &c
->pic
) < 0){
301 av_log(avctx
, AV_LOG_ERROR
, "reget_buffer() failed\n");
305 c
->pic
.key_frame
= 0;
306 c
->pic
.pict_type
= AV_PICTURE_TYPE_P
;
308 //restore screen after cursor
312 if(c
->width
< c
->cur_x
+ w
) w
= c
->width
- c
->cur_x
;
314 if(c
->height
< c
->cur_y
+ h
) h
= c
->height
- c
->cur_y
;
325 if((w
> 0) && (h
> 0)) {
326 outptr
= c
->pic
.data
[0] + dx
* c
->bpp2
+ dy
* c
->pic
.linesize
[0];
327 for(i
= 0; i
< h
; i
++) {
328 memcpy(outptr
, c
->screendta
+ i
* c
->cur_w
* c
->bpp2
, w
* c
->bpp2
);
329 outptr
+= c
->pic
.linesize
[0];
334 chunks
= AV_RB16(src
); src
+= 2;
336 dx
= AV_RB16(src
); src
+= 2;
337 dy
= AV_RB16(src
); src
+= 2;
338 w
= AV_RB16(src
); src
+= 2;
339 h
= AV_RB16(src
); src
+= 2;
340 enc
= AV_RB32(src
); src
+= 4;
341 outptr
= c
->pic
.data
[0] + dx
* c
->bpp2
+ dy
* c
->pic
.linesize
[0];
342 size_left
= buf_size
- (src
- buf
);
344 case MAGIC_WMVd
: // cursor
345 if(size_left
< 2 + w
* h
* c
->bpp2
* 2) {
346 av_log(avctx
, AV_LOG_ERROR
, "Premature end of data! (need %i got %i)\n", 2 + w
* h
* c
->bpp2
* 2, size_left
);
354 if((c
->cur_hx
> c
->cur_w
) || (c
->cur_hy
> c
->cur_h
)) {
355 av_log(avctx
, AV_LOG_ERROR
, "Cursor hot spot is not in image: %ix%i of %ix%i cursor size\n", c
->cur_hx
, c
->cur_hy
, c
->cur_w
, c
->cur_h
);
356 c
->cur_hx
= c
->cur_hy
= 0;
358 c
->curbits
= av_realloc(c
->curbits
, c
->cur_w
* c
->cur_h
* c
->bpp2
);
359 c
->curmask
= av_realloc(c
->curmask
, c
->cur_w
* c
->cur_h
* c
->bpp2
);
360 c
->screendta
= av_realloc(c
->screendta
, c
->cur_w
* c
->cur_h
* c
->bpp2
);
362 src
+= w
* h
* c
->bpp2
* 2;
364 case MAGIC_WMVe
: // unknown
367 case MAGIC_WMVf
: // update cursor position
368 c
->cur_x
= dx
- c
->cur_hx
;
369 c
->cur_y
= dy
- c
->cur_hy
;
371 case MAGIC_WMVg
: // unknown
374 case MAGIC_WMVh
: // unknown
377 case MAGIC_WMVi
: // ServerInitialization struct
378 c
->pic
.key_frame
= 1;
379 c
->pic
.pict_type
= AV_PICTURE_TYPE_I
;
381 if(depth
!= c
->bpp
) {
382 av_log(avctx
, AV_LOG_INFO
, "Depth mismatch. Container %i bpp, Frame data: %i bpp\n", c
->bpp
, depth
);
385 c
->bigendian
= *src
++;
386 if(c
->bigendian
& (~1)) {
387 av_log(avctx
, AV_LOG_INFO
, "Invalid header: bigendian flag = %i\n", c
->bigendian
);
390 //skip the rest of pixel format data
393 case MAGIC_WMVj
: // unknown
396 case 0x00000000: // raw rectangle data
397 if((dx
+ w
> c
->width
) || (dy
+ h
> c
->height
)) {
398 av_log(avctx
, AV_LOG_ERROR
, "Incorrect frame size: %ix%i+%ix%i of %ix%i\n", w
, h
, dx
, dy
, c
->width
, c
->height
);
401 if(size_left
< w
* h
* c
->bpp2
) {
402 av_log(avctx
, AV_LOG_ERROR
, "Premature end of data! (need %i got %i)\n", w
* h
* c
->bpp2
, size_left
);
405 paint_raw(outptr
, w
, h
, src
, c
->bpp2
, c
->bigendian
, c
->pic
.linesize
[0]);
406 src
+= w
* h
* c
->bpp2
;
408 case 0x00000005: // HexTile encoded rectangle
409 if((dx
+ w
> c
->width
) || (dy
+ h
> c
->height
)) {
410 av_log(avctx
, AV_LOG_ERROR
, "Incorrect frame size: %ix%i+%ix%i of %ix%i\n", w
, h
, dx
, dy
, c
->width
, c
->height
);
413 res
= decode_hextile(c
, outptr
, src
, size_left
, w
, h
, c
->pic
.linesize
[0]);
419 av_log(avctx
, AV_LOG_ERROR
, "Unsupported block type 0x%08X\n", enc
);
420 chunks
= 0; // leave chunks decoding loop
425 //save screen data before painting cursor
427 if(c
->width
< c
->cur_x
+ w
) w
= c
->width
- c
->cur_x
;
429 if(c
->height
< c
->cur_y
+ h
) h
= c
->height
- c
->cur_y
;
440 if((w
> 0) && (h
> 0)) {
441 outptr
= c
->pic
.data
[0] + dx
* c
->bpp2
+ dy
* c
->pic
.linesize
[0];
442 for(i
= 0; i
< h
; i
++) {
443 memcpy(c
->screendta
+ i
* c
->cur_w
* c
->bpp2
, outptr
, w
* c
->bpp2
);
444 outptr
+= c
->pic
.linesize
[0];
446 outptr
= c
->pic
.data
[0];
447 put_cursor(outptr
, c
->pic
.linesize
[0], c
, c
->cur_x
, c
->cur_y
);
451 *(AVFrame
*)data
= c
->pic
;
453 /* always report that the buffer was completely consumed */
464 static av_cold
int decode_init(AVCodecContext
*avctx
)
466 VmncContext
* const c
= avctx
->priv_data
;
470 c
->width
= avctx
->width
;
471 c
->height
= avctx
->height
;
473 c
->bpp
= avctx
->bits_per_coded_sample
;
478 avctx
->pix_fmt
= AV_PIX_FMT_PAL8
;
481 avctx
->pix_fmt
= AV_PIX_FMT_RGB555
;
484 avctx
->pix_fmt
= AV_PIX_FMT_RGB32
;
487 av_log(avctx
, AV_LOG_ERROR
, "Unsupported bitdepth %i\n", c
->bpp
);
488 return AVERROR_INVALIDDATA
;
498 * Uninit VMnc decoder
501 static av_cold
int decode_end(AVCodecContext
*avctx
)
503 VmncContext
* const c
= avctx
->priv_data
;
506 avctx
->release_buffer(avctx
, &c
->pic
);
510 av_free(c
->screendta
);
514 AVCodec ff_vmnc_decoder
= {
516 .type
= AVMEDIA_TYPE_VIDEO
,
517 .id
= AV_CODEC_ID_VMNC
,
518 .priv_data_size
= sizeof(VmncContext
),
521 .decode
= decode_frame
,
522 .capabilities
= CODEC_CAP_DR1
,
523 .long_name
= NULL_IF_CONFIG_SMALL("VMware Screen Codec / VMware Video"),