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 libavcodec/vmnc.c 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, const uint8_t *buf, int buf_size) 288{ 289 VmncContext * const c = avctx->priv_data; 290 uint8_t *outptr; 291 const uint8_t *src = buf; 292 int dx, dy, w, h, depth, enc, chunks, res, size_left; 293 294 c->pic.reference = 1; 295 c->pic.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE | FF_BUFFER_HINTS_REUSABLE; 296 if(avctx->reget_buffer(avctx, &c->pic) < 0){ 297 av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); 298 return -1; 299 } 300 301 c->pic.key_frame = 0; 302 c->pic.pict_type = FF_P_TYPE; 303 304 //restore screen after cursor 305 if(c->screendta) { 306 int i; 307 w = c->cur_w; 308 if(c->width < c->cur_x + w) w = c->width - c->cur_x; 309 h = c->cur_h; 310 if(c->height < c->cur_y + h) h = c->height - c->cur_y; 311 dx = c->cur_x; 312 if(dx < 0) { 313 w += dx; 314 dx = 0; 315 } 316 dy = c->cur_y; 317 if(dy < 0) { 318 h += dy; 319 dy = 0; 320 } 321 if((w > 0) && (h > 0)) { 322 outptr = c->pic.data[0] + dx * c->bpp2 + dy * c->pic.linesize[0]; 323 for(i = 0; i < h; i++) { 324 memcpy(outptr, c->screendta + i * c->cur_w * c->bpp2, w * c->bpp2); 325 outptr += c->pic.linesize[0]; 326 } 327 } 328 } 329 src += 2; 330 chunks = AV_RB16(src); src += 2; 331 while(chunks--) { 332 dx = AV_RB16(src); src += 2; 333 dy = AV_RB16(src); src += 2; 334 w = AV_RB16(src); src += 2; 335 h = AV_RB16(src); src += 2; 336 enc = AV_RB32(src); src += 4; 337 outptr = c->pic.data[0] + dx * c->bpp2 + dy * c->pic.linesize[0]; 338 size_left = buf_size - (src - buf); 339 switch(enc) { 340 case MAGIC_WMVd: // cursor 341 if(size_left < 2 + w * h * c->bpp2 * 2) { 342 av_log(avctx, AV_LOG_ERROR, "Premature end of data! (need %i got %i)\n", 2 + w * h * c->bpp2 * 2, size_left); 343 return -1; 344 } 345 src += 2; 346 c->cur_w = w; 347 c->cur_h = h; 348 c->cur_hx = dx; 349 c->cur_hy = dy; 350 if((c->cur_hx > c->cur_w) || (c->cur_hy > c->cur_h)) { 351 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); 352 c->cur_hx = c->cur_hy = 0; 353 } 354 c->curbits = av_realloc(c->curbits, c->cur_w * c->cur_h * c->bpp2); 355 c->curmask = av_realloc(c->curmask, c->cur_w * c->cur_h * c->bpp2); 356 c->screendta = av_realloc(c->screendta, c->cur_w * c->cur_h * c->bpp2); 357 load_cursor(c, src); 358 src += w * h * c->bpp2 * 2; 359 break; 360 case MAGIC_WMVe: // unknown 361 src += 2; 362 break; 363 case MAGIC_WMVf: // update cursor position 364 c->cur_x = dx - c->cur_hx; 365 c->cur_y = dy - c->cur_hy; 366 break; 367 case MAGIC_WMVg: // unknown 368 src += 10; 369 break; 370 case MAGIC_WMVh: // unknown 371 src += 4; 372 break; 373 case MAGIC_WMVi: // ServerInitialization struct 374 c->pic.key_frame = 1; 375 c->pic.pict_type = FF_I_TYPE; 376 depth = *src++; 377 if(depth != c->bpp) { 378 av_log(avctx, AV_LOG_INFO, "Depth mismatch. Container %i bpp, Frame data: %i bpp\n", c->bpp, depth); 379 } 380 src++; 381 c->bigendian = *src++; 382 if(c->bigendian & (~1)) { 383 av_log(avctx, AV_LOG_INFO, "Invalid header: bigendian flag = %i\n", c->bigendian); 384 return -1; 385 } 386 //skip the rest of pixel format data 387 src += 13; 388 break; 389 case MAGIC_WMVj: // unknown 390 src += 2; 391 break; 392 case 0x00000000: // raw rectangle data 393 if((dx + w > c->width) || (dy + h > c->height)) { 394 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); 395 return -1; 396 } 397 if(size_left < w * h * c->bpp2) { 398 av_log(avctx, AV_LOG_ERROR, "Premature end of data! (need %i got %i)\n", w * h * c->bpp2, size_left); 399 return -1; 400 } 401 paint_raw(outptr, w, h, src, c->bpp2, c->bigendian, c->pic.linesize[0]); 402 src += w * h * c->bpp2; 403 break; 404 case 0x00000005: // HexTile encoded rectangle 405 if((dx + w > c->width) || (dy + h > c->height)) { 406 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); 407 return -1; 408 } 409 res = decode_hextile(c, outptr, src, size_left, w, h, c->pic.linesize[0]); 410 if(res < 0) 411 return -1; 412 src += res; 413 break; 414 default: 415 av_log(avctx, AV_LOG_ERROR, "Unsupported block type 0x%08X\n", enc); 416 chunks = 0; // leave chunks decoding loop 417 } 418 } 419 if(c->screendta){ 420 int i; 421 //save screen data before painting cursor 422 w = c->cur_w; 423 if(c->width < c->cur_x + w) w = c->width - c->cur_x; 424 h = c->cur_h; 425 if(c->height < c->cur_y + h) h = c->height - c->cur_y; 426 dx = c->cur_x; 427 if(dx < 0) { 428 w += dx; 429 dx = 0; 430 } 431 dy = c->cur_y; 432 if(dy < 0) { 433 h += dy; 434 dy = 0; 435 } 436 if((w > 0) && (h > 0)) { 437 outptr = c->pic.data[0] + dx * c->bpp2 + dy * c->pic.linesize[0]; 438 for(i = 0; i < h; i++) { 439 memcpy(c->screendta + i * c->cur_w * c->bpp2, outptr, w * c->bpp2); 440 outptr += c->pic.linesize[0]; 441 } 442 outptr = c->pic.data[0]; 443 put_cursor(outptr, c->pic.linesize[0], c, c->cur_x, c->cur_y); 444 } 445 } 446 *data_size = sizeof(AVFrame); 447 *(AVFrame*)data = c->pic; 448 449 /* always report that the buffer was completely consumed */ 450 return buf_size; 451} 452 453 454 455/* 456 * 457 * Init VMnc decoder 458 * 459 */ 460static av_cold int decode_init(AVCodecContext *avctx) 461{ 462 VmncContext * const c = avctx->priv_data; 463 464 c->avctx = avctx; 465 466 c->pic.data[0] = NULL; 467 c->width = avctx->width; 468 c->height = avctx->height; 469 470 if (avcodec_check_dimensions(avctx, avctx->width, avctx->height) < 0) { 471 return 1; 472 } 473 c->bpp = avctx->bits_per_coded_sample; 474 c->bpp2 = c->bpp/8; 475 476 switch(c->bpp){ 477 case 8: 478 avctx->pix_fmt = PIX_FMT_PAL8; 479 break; 480 case 16: 481 avctx->pix_fmt = PIX_FMT_RGB555; 482 break; 483 case 32: 484 avctx->pix_fmt = PIX_FMT_RGB32; 485 break; 486 default: 487 av_log(avctx, AV_LOG_ERROR, "Unsupported bitdepth %i\n", c->bpp); 488 } 489 490 return 0; 491} 492 493 494 495/* 496 * 497 * Uninit VMnc decoder 498 * 499 */ 500static av_cold int decode_end(AVCodecContext *avctx) 501{ 502 VmncContext * const c = avctx->priv_data; 503 504 if (c->pic.data[0]) 505 avctx->release_buffer(avctx, &c->pic); 506 507 av_free(c->curbits); 508 av_free(c->curmask); 509 av_free(c->screendta); 510 return 0; 511} 512 513AVCodec vmnc_decoder = { 514 "vmnc", 515 CODEC_TYPE_VIDEO, 516 CODEC_ID_VMNC, 517 sizeof(VmncContext), 518 decode_init, 519 NULL, 520 decode_end, 521 decode_frame, 522 .long_name = NULL_IF_CONFIG_SMALL("VMware Screen Codec / VMware Video"), 523}; 524 525