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 <stdio.h> 29#include <stdlib.h> 30 31#include "libavutil/intreadwrite.h" 32#include "avcodec.h" 33 34enum EncTypes { 35 MAGIC_WMVd = 0x574D5664, 36 MAGIC_WMVe, 37 MAGIC_WMVf, 38 MAGIC_WMVg, 39 MAGIC_WMVh, 40 MAGIC_WMVi, 41 MAGIC_WMVj 42}; 43 44enum HexTile_Flags { 45 HT_RAW = 1, // tile is raw 46 HT_BKG = 2, // background color is present 47 HT_FG = 4, // foreground color is present 48 HT_SUB = 8, // subrects are present 49 HT_CLR = 16 // each subrect has own color 50}; 51 52/* 53 * Decoder context 54 */ 55typedef struct VmncContext { 56 AVCodecContext *avctx; 57 AVFrame pic; 58 59 int bpp; 60 int bpp2; 61 int bigendian; 62 uint8_t pal[768]; 63 int width, height; 64 65 /* cursor data */ 66 int cur_w, cur_h; 67 int cur_x, cur_y; 68 int cur_hx, cur_hy; 69 uint8_t* curbits, *curmask; 70 uint8_t* screendta; 71} VmncContext; 72 73/* read pixel value from stream */ 74static av_always_inline int vmnc_get_pixel(const uint8_t* buf, int bpp, int be) { 75 switch(bpp * 2 + be) { 76 case 2: 77 case 3: return *buf; 78 case 4: return AV_RL16(buf); 79 case 5: return AV_RB16(buf); 80 case 8: return AV_RL32(buf); 81 case 9: return AV_RB32(buf); 82 default: return 0; 83 } 84} 85 86static void load_cursor(VmncContext *c, const uint8_t *src) 87{ 88 int i, j, p; 89 const int bpp = c->bpp2; 90 uint8_t *dst8 = c->curbits; 91 uint16_t *dst16 = (uint16_t*)c->curbits; 92 uint32_t *dst32 = (uint32_t*)c->curbits; 93 94 for(j = 0; j < c->cur_h; j++) { 95 for(i = 0; i < c->cur_w; i++) { 96 p = vmnc_get_pixel(src, bpp, c->bigendian); 97 src += bpp; 98 if(bpp == 1) *dst8++ = p; 99 if(bpp == 2) *dst16++ = p; 100 if(bpp == 4) *dst32++ = p; 101 } 102 } 103 dst8 = c->curmask; 104 dst16 = (uint16_t*)c->curmask; 105 dst32 = (uint32_t*)c->curmask; 106 for(j = 0; j < c->cur_h; j++) { 107 for(i = 0; i < c->cur_w; i++) { 108 p = vmnc_get_pixel(src, bpp, c->bigendian); 109 src += bpp; 110 if(bpp == 1) *dst8++ = p; 111 if(bpp == 2) *dst16++ = p; 112 if(bpp == 4) *dst32++ = p; 113 } 114 } 115} 116 117static void put_cursor(uint8_t *dst, int stride, VmncContext *c, int dx, int dy) 118{ 119 int i, j; 120 int w, h, x, y; 121 w = c->cur_w; 122 if(c->width < c->cur_x + c->cur_w) w = c->width - c->cur_x; 123 h = c->cur_h; 124 if(c->height < c->cur_y + c->cur_h) h = c->height - c->cur_y; 125 x = c->cur_x; 126 y = c->cur_y; 127 if(x < 0) { 128 w += x; 129 x = 0; 130 } 131 if(y < 0) { 132 h += y; 133 y = 0; 134 } 135 136 if((w < 1) || (h < 1)) return; 137 dst += x * c->bpp2 + y * stride; 138 139 if(c->bpp2 == 1) { 140 uint8_t* cd = c->curbits, *msk = c->curmask; 141 for(j = 0; j < h; j++) { 142 for(i = 0; i < w; i++) 143 dst[i] = (dst[i] & cd[i]) ^ msk[i]; 144 msk += c->cur_w; 145 cd += c->cur_w; 146 dst += stride; 147 } 148 } else if(c->bpp2 == 2) { 149 uint16_t* cd = (uint16_t*)c->curbits, *msk = (uint16_t*)c->curmask; 150 uint16_t* dst2; 151 for(j = 0; j < h; j++) { 152 dst2 = (uint16_t*)dst; 153 for(i = 0; i < w; i++) 154 dst2[i] = (dst2[i] & cd[i]) ^ msk[i]; 155 msk += c->cur_w; 156 cd += c->cur_w; 157 dst += stride; 158 } 159 } else if(c->bpp2 == 4) { 160 uint32_t* cd = (uint32_t*)c->curbits, *msk = (uint32_t*)c->curmask; 161 uint32_t* dst2; 162 for(j = 0; j < h; j++) { 163 dst2 = (uint32_t*)dst; 164 for(i = 0; i < w; i++) 165 dst2[i] = (dst2[i] & cd[i]) ^ msk[i]; 166 msk += c->cur_w; 167 cd += c->cur_w; 168 dst += stride; 169 } 170 } 171} 172 173/* fill rectangle with given color */ 174static av_always_inline void paint_rect(uint8_t *dst, int dx, int dy, int w, int h, int color, int bpp, int stride) 175{ 176 int i, j; 177 dst += dx * bpp + dy * stride; 178 if(bpp == 1){ 179 for(j = 0; j < h; j++) { 180 memset(dst, color, w); 181 dst += stride; 182 } 183 }else if(bpp == 2){ 184 uint16_t* dst2; 185 for(j = 0; j < h; j++) { 186 dst2 = (uint16_t*)dst; 187 for(i = 0; i < w; i++) { 188 *dst2++ = color; 189 } 190 dst += stride; 191 } 192 }else if(bpp == 4){ 193 uint32_t* dst2; 194 for(j = 0; j < h; j++) { 195 dst2 = (uint32_t*)dst; 196 for(i = 0; i < w; i++) { 197 dst2[i] = color; 198 } 199 dst += stride; 200 } 201 } 202} 203 204static av_always_inline void paint_raw(uint8_t *dst, int w, int h, const uint8_t* src, int bpp, int be, int stride) 205{ 206 int i, j, p; 207 for(j = 0; j < h; j++) { 208 for(i = 0; i < w; i++) { 209 p = vmnc_get_pixel(src, bpp, be); 210 src += bpp; 211 switch(bpp){ 212 case 1: 213 dst[i] = p; 214 break; 215 case 2: 216 ((uint16_t*)dst)[i] = p; 217 break; 218 case 4: 219 ((uint32_t*)dst)[i] = p; 220 break; 221 } 222 } 223 dst += stride; 224 } 225} 226 227static int decode_hextile(VmncContext *c, uint8_t* dst, const uint8_t* src, int ssize, int w, int h, int stride) 228{ 229 int i, j, k; 230 int bg = 0, fg = 0, rects, color, flags, xy, wh; 231 const int bpp = c->bpp2; 232 uint8_t *dst2; 233 int bw = 16, bh = 16; 234 const uint8_t *ssrc=src; 235 236 for(j = 0; j < h; j += 16) { 237 dst2 = dst; 238 bw = 16; 239 if(j + 16 > h) bh = h - j; 240 for(i = 0; i < w; i += 16, dst2 += 16 * bpp) { 241 if(src - ssrc >= ssize) { 242 av_log(c->avctx, AV_LOG_ERROR, "Premature end of data!\n"); 243 return -1; 244 } 245 if(i + 16 > w) bw = w - i; 246 flags = *src++; 247 if(flags & HT_RAW) { 248 if(src - ssrc > ssize - bw * bh * bpp) { 249 av_log(c->avctx, AV_LOG_ERROR, "Premature end of data!\n"); 250 return -1; 251 } 252 paint_raw(dst2, bw, bh, src, bpp, c->bigendian, stride); 253 src += bw * bh * bpp; 254 } else { 255 if(flags & HT_BKG) { 256 bg = vmnc_get_pixel(src, bpp, c->bigendian); src += bpp; 257 } 258 if(flags & HT_FG) { 259 fg = vmnc_get_pixel(src, bpp, c->bigendian); src += bpp; 260 } 261 rects = 0; 262 if(flags & HT_SUB) 263 rects = *src++; 264 color = !!(flags & HT_CLR); 265 266 paint_rect(dst2, 0, 0, bw, bh, bg, bpp, stride); 267 268 if(src - ssrc > ssize - rects * (color * bpp + 2)) { 269 av_log(c->avctx, AV_LOG_ERROR, "Premature end of data!\n"); 270 return -1; 271 } 272 for(k = 0; k < rects; k++) { 273 if(color) { 274 fg = vmnc_get_pixel(src, bpp, c->bigendian); src += bpp; 275 } 276 xy = *src++; 277 wh = *src++; 278 paint_rect(dst2, xy >> 4, xy & 0xF, (wh>>4)+1, (wh & 0xF)+1, fg, bpp, stride); 279 } 280 } 281 } 282 dst += stride * 16; 283 } 284 return src - ssrc; 285} 286 287static int decode_frame(AVCodecContext *avctx, void *data, int *data_size, AVPacket *avpkt) 288{ 289 const uint8_t *buf = avpkt->data; 290 int buf_size = avpkt->size; 291 VmncContext * const c = avctx->priv_data; 292 uint8_t *outptr; 293 const uint8_t *src = buf; 294 int dx, dy, w, h, depth, enc, chunks, res, size_left; 295 296 c->pic.reference = 1; 297 c->pic.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE | FF_BUFFER_HINTS_REUSABLE; 298 if(avctx->reget_buffer(avctx, &c->pic) < 0){ 299 av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); 300 return -1; 301 } 302 303 c->pic.key_frame = 0; 304 c->pic.pict_type = FF_P_TYPE; 305 306 //restore screen after cursor 307 if(c->screendta) { 308 int i; 309 w = c->cur_w; 310 if(c->width < c->cur_x + w) w = c->width - c->cur_x; 311 h = c->cur_h; 312 if(c->height < c->cur_y + h) h = c->height - c->cur_y; 313 dx = c->cur_x; 314 if(dx < 0) { 315 w += dx; 316 dx = 0; 317 } 318 dy = c->cur_y; 319 if(dy < 0) { 320 h += dy; 321 dy = 0; 322 } 323 if((w > 0) && (h > 0)) { 324 outptr = c->pic.data[0] + dx * c->bpp2 + dy * c->pic.linesize[0]; 325 for(i = 0; i < h; i++) { 326 memcpy(outptr, c->screendta + i * c->cur_w * c->bpp2, w * c->bpp2); 327 outptr += c->pic.linesize[0]; 328 } 329 } 330 } 331 src += 2; 332 chunks = AV_RB16(src); src += 2; 333 while(chunks--) { 334 dx = AV_RB16(src); src += 2; 335 dy = AV_RB16(src); src += 2; 336 w = AV_RB16(src); src += 2; 337 h = AV_RB16(src); src += 2; 338 enc = AV_RB32(src); src += 4; 339 outptr = c->pic.data[0] + dx * c->bpp2 + dy * c->pic.linesize[0]; 340 size_left = buf_size - (src - buf); 341 switch(enc) { 342 case MAGIC_WMVd: // cursor 343 if(size_left < 2 + w * h * c->bpp2 * 2) { 344 av_log(avctx, AV_LOG_ERROR, "Premature end of data! (need %i got %i)\n", 2 + w * h * c->bpp2 * 2, size_left); 345 return -1; 346 } 347 src += 2; 348 c->cur_w = w; 349 c->cur_h = h; 350 c->cur_hx = dx; 351 c->cur_hy = dy; 352 if((c->cur_hx > c->cur_w) || (c->cur_hy > c->cur_h)) { 353 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); 354 c->cur_hx = c->cur_hy = 0; 355 } 356 c->curbits = av_realloc(c->curbits, c->cur_w * c->cur_h * c->bpp2); 357 c->curmask = av_realloc(c->curmask, c->cur_w * c->cur_h * c->bpp2); 358 c->screendta = av_realloc(c->screendta, c->cur_w * c->cur_h * c->bpp2); 359 load_cursor(c, src); 360 src += w * h * c->bpp2 * 2; 361 break; 362 case MAGIC_WMVe: // unknown 363 src += 2; 364 break; 365 case MAGIC_WMVf: // update cursor position 366 c->cur_x = dx - c->cur_hx; 367 c->cur_y = dy - c->cur_hy; 368 break; 369 case MAGIC_WMVg: // unknown 370 src += 10; 371 break; 372 case MAGIC_WMVh: // unknown 373 src += 4; 374 break; 375 case MAGIC_WMVi: // ServerInitialization struct 376 c->pic.key_frame = 1; 377 c->pic.pict_type = FF_I_TYPE; 378 depth = *src++; 379 if(depth != c->bpp) { 380 av_log(avctx, AV_LOG_INFO, "Depth mismatch. Container %i bpp, Frame data: %i bpp\n", c->bpp, depth); 381 } 382 src++; 383 c->bigendian = *src++; 384 if(c->bigendian & (~1)) { 385 av_log(avctx, AV_LOG_INFO, "Invalid header: bigendian flag = %i\n", c->bigendian); 386 return -1; 387 } 388 //skip the rest of pixel format data 389 src += 13; 390 break; 391 case MAGIC_WMVj: // unknown 392 src += 2; 393 break; 394 case 0x00000000: // raw rectangle data 395 if((dx + w > c->width) || (dy + h > c->height)) { 396 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); 397 return -1; 398 } 399 if(size_left < w * h * c->bpp2) { 400 av_log(avctx, AV_LOG_ERROR, "Premature end of data! (need %i got %i)\n", w * h * c->bpp2, size_left); 401 return -1; 402 } 403 paint_raw(outptr, w, h, src, c->bpp2, c->bigendian, c->pic.linesize[0]); 404 src += w * h * c->bpp2; 405 break; 406 case 0x00000005: // HexTile encoded rectangle 407 if((dx + w > c->width) || (dy + h > c->height)) { 408 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); 409 return -1; 410 } 411 res = decode_hextile(c, outptr, src, size_left, w, h, c->pic.linesize[0]); 412 if(res < 0) 413 return -1; 414 src += res; 415 break; 416 default: 417 av_log(avctx, AV_LOG_ERROR, "Unsupported block type 0x%08X\n", enc); 418 chunks = 0; // leave chunks decoding loop 419 } 420 } 421 if(c->screendta){ 422 int i; 423 //save screen data before painting cursor 424 w = c->cur_w; 425 if(c->width < c->cur_x + w) w = c->width - c->cur_x; 426 h = c->cur_h; 427 if(c->height < c->cur_y + h) h = c->height - c->cur_y; 428 dx = c->cur_x; 429 if(dx < 0) { 430 w += dx; 431 dx = 0; 432 } 433 dy = c->cur_y; 434 if(dy < 0) { 435 h += dy; 436 dy = 0; 437 } 438 if((w > 0) && (h > 0)) { 439 outptr = c->pic.data[0] + dx * c->bpp2 + dy * c->pic.linesize[0]; 440 for(i = 0; i < h; i++) { 441 memcpy(c->screendta + i * c->cur_w * c->bpp2, outptr, w * c->bpp2); 442 outptr += c->pic.linesize[0]; 443 } 444 outptr = c->pic.data[0]; 445 put_cursor(outptr, c->pic.linesize[0], c, c->cur_x, c->cur_y); 446 } 447 } 448 *data_size = sizeof(AVFrame); 449 *(AVFrame*)data = c->pic; 450 451 /* always report that the buffer was completely consumed */ 452 return buf_size; 453} 454 455 456 457/* 458 * 459 * Init VMnc decoder 460 * 461 */ 462static av_cold int decode_init(AVCodecContext *avctx) 463{ 464 VmncContext * const c = avctx->priv_data; 465 466 c->avctx = avctx; 467 468 c->width = avctx->width; 469 c->height = avctx->height; 470 471 c->bpp = avctx->bits_per_coded_sample; 472 c->bpp2 = c->bpp/8; 473 474 switch(c->bpp){ 475 case 8: 476 avctx->pix_fmt = PIX_FMT_PAL8; 477 break; 478 case 16: 479 avctx->pix_fmt = PIX_FMT_RGB555; 480 break; 481 case 32: 482 avctx->pix_fmt = PIX_FMT_RGB32; 483 break; 484 default: 485 av_log(avctx, AV_LOG_ERROR, "Unsupported bitdepth %i\n", c->bpp); 486 } 487 488 return 0; 489} 490 491 492 493/* 494 * 495 * Uninit VMnc decoder 496 * 497 */ 498static av_cold int decode_end(AVCodecContext *avctx) 499{ 500 VmncContext * const c = avctx->priv_data; 501 502 if (c->pic.data[0]) 503 avctx->release_buffer(avctx, &c->pic); 504 505 av_free(c->curbits); 506 av_free(c->curmask); 507 av_free(c->screendta); 508 return 0; 509} 510 511AVCodec vmnc_decoder = { 512 "vmnc", 513 AVMEDIA_TYPE_VIDEO, 514 CODEC_ID_VMNC, 515 sizeof(VmncContext), 516 decode_init, 517 NULL, 518 decode_end, 519 decode_frame, 520 CODEC_CAP_DR1, 521 .long_name = NULL_IF_CONFIG_SMALL("VMware Screen Codec / VMware Video"), 522}; 523 524