1 /* GStreamer
2 * Copyright (C) 2007 Michael Smith <msmith@xiph.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 /*
21 * This is a decoder for the VMWare VMnc video codec, which VMWare uses for
22 * recording * of virtual machine instances.
23 * It's essentially a serialisation of RFB (the VNC protocol)
24 * 'FramebufferUpdate' messages, with some special encoding-types for VMnc
25 * extensions. There's some documentation (with fixes from VMWare employees) at:
26 * http://wiki.multimedia.cx/index.php?title=VMware_Video
27 */
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include "vmncdec.h"
34
35 #include <string.h>
36
37 static gboolean gst_vmnc_dec_reset (GstVideoDecoder * decoder);
38 static gboolean gst_vmnc_dec_set_format (GstVideoDecoder * decoder,
39 GstVideoCodecState * state);
40 static GstFlowReturn gst_vmnc_dec_handle_frame (GstVideoDecoder * decoder,
41 GstVideoCodecFrame * frame);
42 static GstFlowReturn gst_vmnc_dec_parse (GstVideoDecoder * decoder,
43 GstVideoCodecFrame * frame, GstAdapter * adapter, gboolean at_eos);
44 static gboolean gst_vmnc_dec_sink_event (GstVideoDecoder * bdec,
45 GstEvent * event);
46
47 #define GST_CAT_DEFAULT vmnc_debug
48 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
49
50 #define RFB_GET_UINT32(ptr) GST_READ_UINT32_BE(ptr)
51 #define RFB_GET_UINT16(ptr) GST_READ_UINT16_BE(ptr)
52 #define RFB_GET_UINT8(ptr) GST_READ_UINT8(ptr)
53
54 enum
55 {
56 PROP_0,
57 };
58
59 enum
60 {
61 ERROR_INVALID = -1, /* Invalid data in bitstream */
62 ERROR_INSUFFICIENT_DATA = -2 /* Haven't received enough data yet */
63 };
64
65 static GstStaticPadTemplate vmnc_dec_src_factory =
66 GST_STATIC_PAD_TEMPLATE ("src",
67 GST_PAD_SRC,
68 GST_PAD_ALWAYS,
69 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ RGBx, BGRx, xRGB, xBGR,"
70 " RGB15, BGR15, RGB16, BGR16, GRAY8 }")));
71
72 static GstStaticPadTemplate vmnc_dec_sink_factory =
73 GST_STATIC_PAD_TEMPLATE ("sink",
74 GST_PAD_SINK,
75 GST_PAD_ALWAYS,
76 GST_STATIC_CAPS ("video/x-vmnc, version=(int)1, "
77 "framerate=(fraction)[0, max], "
78 "width=(int)[0, max], " "height=(int)[0, max]")
79 );
80
81 G_DEFINE_TYPE (GstVMncDec, gst_vmnc_dec, GST_TYPE_VIDEO_DECODER);
82 GST_ELEMENT_REGISTER_DEFINE (vmncdec, "vmncdec", GST_RANK_PRIMARY,
83 GST_TYPE_VMNC_DEC);
84
85 static void
gst_vmnc_dec_class_init(GstVMncDecClass * klass)86 gst_vmnc_dec_class_init (GstVMncDecClass * klass)
87 {
88 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
89 GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass);
90
91 decoder_class->start = gst_vmnc_dec_reset;
92 decoder_class->stop = gst_vmnc_dec_reset;
93 decoder_class->parse = gst_vmnc_dec_parse;
94 decoder_class->handle_frame = gst_vmnc_dec_handle_frame;
95 decoder_class->set_format = gst_vmnc_dec_set_format;
96 decoder_class->sink_event = gst_vmnc_dec_sink_event;
97
98 gst_element_class_add_static_pad_template (gstelement_class,
99 &vmnc_dec_src_factory);
100 gst_element_class_add_static_pad_template (gstelement_class,
101 &vmnc_dec_sink_factory);
102 gst_element_class_set_static_metadata (gstelement_class, "VMnc video decoder",
103 "Codec/Decoder/Video", "Decode VmWare video to raw (RGB) video",
104 "Michael Smith <msmith@xiph.org>");
105
106 GST_DEBUG_CATEGORY_INIT (vmnc_debug, "vmncdec", 0, "VMnc decoder");
107 }
108
109 static void
gst_vmnc_dec_init(GstVMncDec * dec)110 gst_vmnc_dec_init (GstVMncDec * dec)
111 {
112 gst_video_decoder_set_use_default_pad_acceptcaps (GST_VIDEO_DECODER_CAST
113 (dec), TRUE);
114 GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_DECODER_SINK_PAD (dec));
115 }
116
117 static gboolean
gst_vmnc_dec_reset(GstVideoDecoder * decoder)118 gst_vmnc_dec_reset (GstVideoDecoder * decoder)
119 {
120 GstVMncDec *dec = GST_VMNC_DEC (decoder);
121
122 g_free (dec->imagedata);
123 dec->imagedata = NULL;
124
125 g_free (dec->cursor.cursordata);
126 dec->cursor.cursordata = NULL;
127
128 g_free (dec->cursor.cursormask);
129 dec->cursor.cursormask = NULL;
130
131 dec->cursor.visible = 0;
132
133 dec->have_format = FALSE;
134
135 if (dec->input_state)
136 gst_video_codec_state_unref (dec->input_state);
137 dec->input_state = NULL;
138
139 return TRUE;
140 }
141
142 struct RfbRectangle
143 {
144 guint16 x;
145 guint16 y;
146 guint16 width;
147 guint16 height;
148
149 gint32 type;
150 };
151
152 /* Rectangle handling functions.
153 * Return number of bytes consumed, or < 0 on error
154 */
155 typedef int (*rectangle_handler) (GstVMncDec * dec, struct RfbRectangle * rect,
156 const guint8 * data, int len, gboolean decode);
157
158 static int
vmnc_handle_wmvi_rectangle(GstVMncDec * dec,struct RfbRectangle * rect,const guint8 * data,int len,gboolean decode)159 vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
160 const guint8 * data, int len, gboolean decode)
161 {
162 GstVideoFormat format;
163 gint bpp, tc;
164 guint32 redmask, greenmask, bluemask;
165 guint32 endianness, dataendianness;
166 GstVideoCodecState *state;
167
168 /* A WMVi rectangle has a 16byte payload */
169 if (len < 16) {
170 GST_DEBUG_OBJECT (dec, "Bad WMVi rect: too short");
171 return ERROR_INSUFFICIENT_DATA;
172 }
173
174 /* We only compare 13 bytes; ignoring the 3 padding bytes at the end */
175 if (dec->have_format && memcmp (data, dec->format.descriptor, 13) == 0) {
176 /* Nothing changed, so just exit */
177 return 16;
178 }
179
180 /* Store the whole block for simple comparison later */
181 memcpy (dec->format.descriptor, data, 16);
182
183 if (rect->x != 0 || rect->y != 0) {
184 GST_WARNING_OBJECT (dec, "Bad WMVi rect: wrong coordinates");
185 return ERROR_INVALID;
186 }
187
188 bpp = data[0];
189 dec->format.depth = data[1];
190 dec->format.big_endian = data[2];
191 dataendianness = data[2] ? G_BIG_ENDIAN : G_LITTLE_ENDIAN;
192 tc = data[3];
193
194 if (bpp != 8 && bpp != 16 && bpp != 32) {
195 GST_WARNING_OBJECT (dec, "Bad bpp value: %d", bpp);
196 return ERROR_INVALID;
197 }
198
199 if (!tc) {
200 GST_WARNING_OBJECT (dec, "Paletted video not supported");
201 return ERROR_INVALID;
202 }
203
204 dec->format.bytes_per_pixel = bpp / 8;
205 dec->format.width = rect->width;
206 dec->format.height = rect->height;
207
208 redmask = (guint32) (RFB_GET_UINT16 (data + 4)) << data[10];
209 greenmask = (guint32) (RFB_GET_UINT16 (data + 6)) << data[11];
210 bluemask = (guint32) (RFB_GET_UINT16 (data + 8)) << data[12];
211
212 GST_DEBUG_OBJECT (dec, "Red: mask %d, shift %d",
213 RFB_GET_UINT16 (data + 4), data[10]);
214 GST_DEBUG_OBJECT (dec, "Green: mask %d, shift %d",
215 RFB_GET_UINT16 (data + 6), data[11]);
216 GST_DEBUG_OBJECT (dec, "Blue: mask %d, shift %d",
217 RFB_GET_UINT16 (data + 8), data[12]);
218 GST_DEBUG_OBJECT (dec, "BPP: %d. endianness: %s", bpp,
219 data[2] ? "big" : "little");
220
221 /* GStreamer's RGB caps are a bit weird. */
222 if (bpp == 8) {
223 endianness = G_BYTE_ORDER; /* Doesn't matter */
224 } else if (bpp == 16) {
225 /* We require host-endian. */
226 endianness = G_BYTE_ORDER;
227 } else { /* bpp == 32 */
228 /* We require big endian */
229 endianness = G_BIG_ENDIAN;
230 if (endianness != dataendianness) {
231 redmask = GUINT32_SWAP_LE_BE (redmask);
232 greenmask = GUINT32_SWAP_LE_BE (greenmask);
233 bluemask = GUINT32_SWAP_LE_BE (bluemask);
234 }
235 }
236
237 format = gst_video_format_from_masks (dec->format.depth, bpp, endianness,
238 redmask, greenmask, bluemask, 0);
239
240 GST_DEBUG_OBJECT (dec, "From depth: %d bpp: %u endianness: %s redmask: %X "
241 "greenmask: %X bluemask: %X got format %s",
242 dec->format.depth, bpp, endianness == G_BIG_ENDIAN ? "BE" : "LE",
243 GUINT32_FROM_BE (redmask), GUINT32_FROM_BE (greenmask),
244 GUINT32_FROM_BE (bluemask),
245 format == GST_VIDEO_FORMAT_UNKNOWN ? "UNKNOWN" :
246 gst_video_format_to_string (format));
247
248 if (format == GST_VIDEO_FORMAT_UNKNOWN) {
249 GST_WARNING_OBJECT (dec, "Video format unknown to GStreamer");
250 return ERROR_INVALID;
251 }
252
253 dec->have_format = TRUE;
254 if (!decode) {
255 GST_LOG_OBJECT (dec, "Parsing, not setting caps");
256 return 16;
257 }
258
259
260 state = gst_video_decoder_set_output_state (GST_VIDEO_DECODER (dec), format,
261 rect->width, rect->height, dec->input_state);
262 gst_video_codec_state_unref (state);
263
264 g_free (dec->imagedata);
265 dec->imagedata = g_malloc0 (dec->format.width * dec->format.height *
266 dec->format.bytes_per_pixel);
267 GST_DEBUG_OBJECT (dec, "Allocated image data at %p", dec->imagedata);
268
269 dec->format.stride = dec->format.width * dec->format.bytes_per_pixel;
270
271 return 16;
272 }
273
274 static void
render_colour_cursor(GstVMncDec * dec,guint8 * data,int x,int y,int off_x,int off_y,int width,int height)275 render_colour_cursor (GstVMncDec * dec, guint8 * data, int x, int y,
276 int off_x, int off_y, int width, int height)
277 {
278 int i, j;
279 guint8 *dstraw = data + dec->format.stride * y +
280 dec->format.bytes_per_pixel * x;
281 guint8 *srcraw = dec->cursor.cursordata +
282 dec->cursor.width * dec->format.bytes_per_pixel * off_y;
283 guint8 *maskraw = dec->cursor.cursormask +
284 dec->cursor.width * dec->format.bytes_per_pixel * off_y;
285
286 /* Boundchecking done by caller; this is just the renderer inner loop */
287 if (dec->format.bytes_per_pixel == 1) {
288 guint8 *dst = dstraw;
289 guint8 *src = srcraw;
290 guint8 *mask = maskraw;
291
292 for (i = 0; i < height; i++) {
293 for (j = 0; j < width; j++) {
294 dst[j] = (dst[j] & src[j]) ^ mask[j];
295 }
296 dst += dec->format.width;
297 src += dec->cursor.width;
298 mask += dec->cursor.width;
299 }
300 } else if (dec->format.bytes_per_pixel == 2) {
301 guint16 *dst = (guint16 *) dstraw;
302 guint16 *src = (guint16 *) srcraw;
303 guint16 *mask = (guint16 *) maskraw;
304
305 for (i = 0; i < height; i++) {
306 for (j = 0; j < width; j++) {
307 dst[j] = (dst[j] & src[j]) ^ mask[j];
308 }
309 dst += dec->format.width;
310 src += dec->cursor.width;
311 mask += dec->cursor.width;
312 }
313 } else {
314 guint32 *dst = (guint32 *) dstraw;
315 guint32 *src = (guint32 *) srcraw;
316 guint32 *mask = (guint32 *) maskraw;
317
318 for (i = 0; i < height; i++) {
319 for (j = 0; j < width; j++) {
320 dst[j] = (dst[j] & src[j]) ^ mask[j];
321 }
322 dst += dec->format.width;
323 src += dec->cursor.width;
324 mask += dec->cursor.width;
325 }
326 }
327 }
328
329 static void
render_cursor(GstVMncDec * dec,guint8 * data)330 render_cursor (GstVMncDec * dec, guint8 * data)
331 {
332 /* First, figure out the portion of the cursor that's on-screen */
333 /* X,Y of top-left of cursor */
334 int x = dec->cursor.x - dec->cursor.hot_x;
335 int y = dec->cursor.y - dec->cursor.hot_y;
336
337 /* Width, height of rendered portion of cursor */
338 int width = dec->cursor.width;
339 int height = dec->cursor.height;
340
341 /* X,Y offset of rendered portion of cursor */
342 int off_x = 0;
343 int off_y = 0;
344
345 if (x < 0) {
346 off_x = -x;
347 width += x;
348 x = 0;
349 }
350 if (x + width > dec->format.width)
351 width = dec->format.width - x;
352 if (y < 0) {
353 off_y = -y;
354 height += y;
355 y = 0;
356 }
357 if (y + height > dec->format.height)
358 height = dec->format.height - y;
359
360 if (dec->cursor.type == CURSOR_COLOUR) {
361 render_colour_cursor (dec, data, x, y, off_x, off_y, width, height);
362 } else {
363 /* Alpha cursor. */
364 /* TODO: Implement me! */
365 GST_WARNING_OBJECT (dec, "Alpha composited cursors not yet implemented");
366 }
367 }
368
369 static GstFlowReturn
vmnc_fill_buffer(GstVMncDec * dec,GstVideoCodecFrame * frame)370 vmnc_fill_buffer (GstVMncDec * dec, GstVideoCodecFrame * frame)
371 {
372 GstFlowReturn ret;
373 GstMapInfo map;
374
375 ret =
376 gst_video_decoder_allocate_output_frame (GST_VIDEO_DECODER (dec), frame);
377 if (ret != GST_FLOW_OK)
378 return ret;
379
380 gst_buffer_map (frame->output_buffer, &map, GST_MAP_READWRITE);
381
382 memcpy (map.data, dec->imagedata, map.size);
383
384 if (dec->cursor.visible)
385 render_cursor (dec, map.data);
386
387 gst_buffer_unmap (frame->output_buffer, &map);
388
389 return GST_FLOW_OK;
390 }
391
392 static int
vmnc_handle_wmvd_rectangle(GstVMncDec * dec,struct RfbRectangle * rect,const guint8 * data,int len,gboolean decode)393 vmnc_handle_wmvd_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
394 const guint8 * data, int len, gboolean decode)
395 {
396 /* Cursor data. */
397 int datalen = 2;
398 int type, size;
399
400 if (len < datalen) {
401 GST_LOG_OBJECT (dec, "Cursor data too short");
402 return ERROR_INSUFFICIENT_DATA;
403 }
404
405 type = RFB_GET_UINT8 (data);
406
407 if (type == CURSOR_COLOUR) {
408 datalen += rect->width * rect->height * dec->format.bytes_per_pixel * 2;
409 } else if (type == CURSOR_ALPHA) {
410 datalen += rect->width * rect->height * 4;
411 } else {
412 GST_WARNING_OBJECT (dec, "Unknown cursor type: %d", type);
413 return ERROR_INVALID;
414 }
415
416 if (len < datalen) {
417 GST_LOG_OBJECT (dec, "Cursor data too short");
418 return ERROR_INSUFFICIENT_DATA;
419 } else if (!decode)
420 return datalen;
421
422 dec->cursor.type = type;
423 dec->cursor.width = rect->width;
424 dec->cursor.height = rect->height;
425 dec->cursor.type = type;
426 dec->cursor.hot_x = rect->x;
427 dec->cursor.hot_y = rect->y;
428
429 g_free (dec->cursor.cursordata);
430 g_free (dec->cursor.cursormask);
431
432 if (type == 0) {
433 size = rect->width * rect->height * dec->format.bytes_per_pixel;
434 dec->cursor.cursordata = g_malloc (size);
435 dec->cursor.cursormask = g_malloc (size);
436 memcpy (dec->cursor.cursordata, data + 2, size);
437 memcpy (dec->cursor.cursormask, data + 2 + size, size);
438 } else {
439 dec->cursor.cursordata = g_malloc (rect->width * rect->height * 4);
440 memcpy (dec->cursor.cursordata, data + 2, rect->width * rect->height * 4);
441 }
442
443 return datalen;
444 }
445
446 static int
vmnc_handle_wmve_rectangle(GstVMncDec * dec,struct RfbRectangle * rect,const guint8 * data,int len,gboolean decode)447 vmnc_handle_wmve_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
448 const guint8 * data, int len, gboolean decode)
449 {
450 guint16 flags;
451
452 /* Cursor state. */
453 if (len < 2) {
454 GST_LOG_OBJECT (dec, "Cursor data too short");
455 return ERROR_INSUFFICIENT_DATA;
456 } else if (!decode)
457 return 2;
458
459 flags = RFB_GET_UINT16 (data);
460 dec->cursor.visible = flags & 0x01;
461
462 return 2;
463 }
464
465 static int
vmnc_handle_wmvf_rectangle(GstVMncDec * dec,struct RfbRectangle * rect,const guint8 * data,int len,gboolean decode)466 vmnc_handle_wmvf_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
467 const guint8 * data, int len, gboolean decode)
468 {
469 /* Cursor position. */
470 dec->cursor.x = rect->x;
471 dec->cursor.y = rect->y;
472 return 0;
473 }
474
475 static int
vmnc_handle_wmvg_rectangle(GstVMncDec * dec,struct RfbRectangle * rect,const guint8 * data,int len,gboolean decode)476 vmnc_handle_wmvg_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
477 const guint8 * data, int len, gboolean decode)
478 {
479 /* Keyboard stuff; not interesting for playback */
480 if (len < 10) {
481 GST_LOG_OBJECT (dec, "Keyboard data too short");
482 return ERROR_INSUFFICIENT_DATA;
483 }
484 return 10;
485 }
486
487 static int
vmnc_handle_wmvh_rectangle(GstVMncDec * dec,struct RfbRectangle * rect,const guint8 * data,int len,gboolean decode)488 vmnc_handle_wmvh_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
489 const guint8 * data, int len, gboolean decode)
490 {
491 /* More keyboard stuff; not interesting for playback */
492 if (len < 4) {
493 GST_LOG_OBJECT (dec, "Keyboard data too short");
494 return ERROR_INSUFFICIENT_DATA;
495 }
496 return 4;
497 }
498
499 static int
vmnc_handle_wmvj_rectangle(GstVMncDec * dec,struct RfbRectangle * rect,const guint8 * data,int len,gboolean decode)500 vmnc_handle_wmvj_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
501 const guint8 * data, int len, gboolean decode)
502 {
503 /* VM state info, not interesting for playback */
504 if (len < 2) {
505 GST_LOG_OBJECT (dec, "VM state data too short");
506 return ERROR_INSUFFICIENT_DATA;
507 }
508 return 2;
509 }
510
511 static void
render_raw_tile(GstVMncDec * dec,const guint8 * data,int x,int y,int width,int height)512 render_raw_tile (GstVMncDec * dec, const guint8 * data, int x, int y,
513 int width, int height)
514 {
515 int i;
516 guint8 *dst;
517 const guint8 *src;
518 int line;
519
520 src = data;
521 dst = dec->imagedata + dec->format.stride * y +
522 dec->format.bytes_per_pixel * x;
523 line = width * dec->format.bytes_per_pixel;
524
525 for (i = 0; i < height; i++) {
526 /* This is wrong-endian currently */
527 memcpy (dst, src, line);
528
529 dst += dec->format.stride;
530 src += line;
531 }
532 }
533
534 static void
render_subrect(GstVMncDec * dec,int x,int y,int width,int height,guint32 colour)535 render_subrect (GstVMncDec * dec, int x, int y, int width,
536 int height, guint32 colour)
537 {
538 /* Crazy inefficient! */
539 int i, j;
540 guint8 *dst;
541
542 for (i = 0; i < height; i++) {
543 dst = dec->imagedata + dec->format.stride * (y + i) +
544 dec->format.bytes_per_pixel * x;
545 for (j = 0; j < width; j++) {
546 memcpy (dst, &colour, dec->format.bytes_per_pixel);
547 dst += dec->format.bytes_per_pixel;
548 }
549 }
550 }
551
552 static int
vmnc_handle_raw_rectangle(GstVMncDec * dec,struct RfbRectangle * rect,const guint8 * data,int len,gboolean decode)553 vmnc_handle_raw_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
554 const guint8 * data, int len, gboolean decode)
555 {
556 int datalen = rect->width * rect->height * dec->format.bytes_per_pixel;
557
558 if (len < datalen) {
559 GST_LOG_OBJECT (dec, "Raw data too short");
560 return ERROR_INSUFFICIENT_DATA;
561 }
562
563 if (decode)
564 render_raw_tile (dec, data, rect->x, rect->y, rect->width, rect->height);
565
566 return datalen;
567 }
568
569 static int
vmnc_handle_copy_rectangle(GstVMncDec * dec,struct RfbRectangle * rect,const guint8 * data,int len,gboolean decode)570 vmnc_handle_copy_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
571 const guint8 * data, int len, gboolean decode)
572 {
573 int src_x, src_y;
574 guint8 *src, *dst;
575 int i;
576
577 if (len < 4) {
578 GST_LOG_OBJECT (dec, "Copy data too short");
579 return ERROR_INSUFFICIENT_DATA;
580 } else if (!decode)
581 return 4;
582
583 src_x = RFB_GET_UINT16 (data);
584 src_y = RFB_GET_UINT16 (data + 2);
585
586 /* Our destination rectangle is guaranteed in-frame; check this for the source
587 * rectangle. */
588 if (src_x + rect->width > dec->format.width ||
589 src_y + rect->height > dec->format.height) {
590 GST_WARNING_OBJECT (dec, "Source rectangle out of range");
591 return ERROR_INVALID;
592 }
593
594 if (src_y > rect->y || src_x > rect->x) {
595 /* Moving forward */
596 src = dec->imagedata + dec->format.stride * src_y +
597 dec->format.bytes_per_pixel * src_x;
598 dst = dec->imagedata + dec->format.stride * rect->y +
599 dec->format.bytes_per_pixel * rect->x;
600 for (i = 0; i < rect->height; i++) {
601 memmove (dst, src, rect->width * dec->format.bytes_per_pixel);
602 dst += dec->format.stride;
603 src += dec->format.stride;
604 }
605 } else {
606 /* Backwards */
607 src = dec->imagedata + dec->format.stride * (src_y + rect->height - 1) +
608 dec->format.bytes_per_pixel * src_x;
609 dst = dec->imagedata + dec->format.stride * (rect->y + rect->height - 1) +
610 dec->format.bytes_per_pixel * rect->x;
611 for (i = rect->height; i > 0; i--) {
612 memmove (dst, src, rect->width * dec->format.bytes_per_pixel);
613 dst -= dec->format.stride;
614 src -= dec->format.stride;
615 }
616 }
617
618 return 4;
619 }
620
621 /* FIXME: data+off might not be properly aligned */
622 #define READ_PIXEL(pixel, data, off, len) \
623 if (dec->format.bytes_per_pixel == 1) { \
624 if (off >= len) \
625 return ERROR_INSUFFICIENT_DATA; \
626 pixel = data[off++]; \
627 } else if (dec->format.bytes_per_pixel == 2) { \
628 if (off+2 > len) \
629 return ERROR_INSUFFICIENT_DATA; \
630 pixel = (*(guint16 *)(data + off)); \
631 off += 2; \
632 } else { \
633 if (off+4 > len) \
634 return ERROR_INSUFFICIENT_DATA; \
635 pixel = (*(guint32 *)(data + off)); \
636 off += 4; \
637 }
638
639 static int
vmnc_handle_hextile_rectangle(GstVMncDec * dec,struct RfbRectangle * rect,const guint8 * data,int len,gboolean decode)640 vmnc_handle_hextile_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
641 const guint8 * data, int len, gboolean decode)
642 {
643 int tilesx = GST_ROUND_UP_16 (rect->width) / 16;
644 int tilesy = GST_ROUND_UP_16 (rect->height) / 16;
645 int x, y, z;
646 int off = 0;
647 int subrects;
648 int coloured;
649 int width, height;
650 guint32 fg = 0, bg = 0, colour;
651 guint8 flags;
652
653 for (y = 0; y < tilesy; y++) {
654 if (y == tilesy - 1)
655 height = rect->height - (tilesy - 1) * 16;
656 else
657 height = 16;
658
659 for (x = 0; x < tilesx; x++) {
660 if (x == tilesx - 1)
661 width = rect->width - (tilesx - 1) * 16;
662 else
663 width = 16;
664
665 if (off >= len) {
666 return ERROR_INSUFFICIENT_DATA;
667 }
668 flags = data[off++];
669
670 if (flags & 0x1) {
671 if (off + width * height * dec->format.bytes_per_pixel > len) {
672 return ERROR_INSUFFICIENT_DATA;
673 }
674 if (decode)
675 render_raw_tile (dec, data + off, rect->x + x * 16, rect->y + y * 16,
676 width, height);
677 off += width * height * dec->format.bytes_per_pixel;
678 } else {
679 if (flags & 0x2) {
680 READ_PIXEL (bg, data, off, len)
681 }
682 if (flags & 0x4) {
683 READ_PIXEL (fg, data, off, len)
684 }
685
686 subrects = 0;
687 if (flags & 0x8) {
688 if (off >= len) {
689 return ERROR_INSUFFICIENT_DATA;
690 }
691 subrects = data[off++];
692 }
693
694 /* Paint background colour on entire tile */
695 if (decode)
696 render_subrect (dec, rect->x + x * 16, rect->y + y * 16,
697 width, height, bg);
698
699 coloured = flags & 0x10;
700 for (z = 0; z < subrects; z++) {
701 if (coloured) {
702 READ_PIXEL (colour, data, off, len);
703 } else
704 colour = fg;
705 if (off + 2 > len)
706 return ERROR_INSUFFICIENT_DATA;
707
708 {
709 int off_x = (data[off] & 0xf0) >> 4;
710 int off_y = (data[off] & 0x0f);
711 int w = ((data[off + 1] & 0xf0) >> 4) + 1;
712 int h = (data[off + 1] & 0x0f) + 1;
713
714 off += 2;
715
716 /* Ensure we don't have out of bounds coordinates */
717 if (off_x + w > width || off_y + h > height) {
718 GST_WARNING_OBJECT (dec, "Subrect out of bounds: %d-%d x %d-%d "
719 "extends outside %dx%d", off_x, w, off_y, h, width, height);
720 return ERROR_INVALID;
721 }
722
723 if (decode)
724 render_subrect (dec, rect->x + x * 16 + off_x,
725 rect->y + y * 16 + off_y, w, h, colour);
726 }
727 }
728 }
729 }
730 }
731
732 return off;
733 }
734
735 /* Handle a packet in one of two modes: decode or parse.
736 * In parse mode, we don't execute any of the decoding, we just do enough to
737 * figure out how many bytes it contains
738 *
739 * Returns: >= 0, the number of bytes consumed
740 * < 0, packet too short to decode, or error
741 */
742 static int
vmnc_handle_packet(GstVMncDec * dec,const guint8 * data,int len,gboolean decode)743 vmnc_handle_packet (GstVMncDec * dec, const guint8 * data, int len,
744 gboolean decode)
745 {
746 int type;
747 int offset = 0;
748
749 if (len < 4) {
750 GST_LOG_OBJECT (dec, "Packet too short");
751 return ERROR_INSUFFICIENT_DATA;
752 }
753
754 type = data[0];
755
756 switch (type) {
757 case 0:
758 {
759 int numrect = RFB_GET_UINT16 (data + 2);
760 int i;
761 int read;
762
763 offset = 4;
764
765 for (i = 0; i < numrect; i++) {
766 struct RfbRectangle r;
767 rectangle_handler handler;
768
769 if (len < offset + 12) {
770 GST_LOG_OBJECT (dec,
771 "Packet too short for rectangle header: %d < %d",
772 len, offset + 12);
773 return ERROR_INSUFFICIENT_DATA;
774 }
775 GST_LOG_OBJECT (dec, "Reading rectangle %d", i);
776 r.x = RFB_GET_UINT16 (data + offset);
777 r.y = RFB_GET_UINT16 (data + offset + 2);
778 r.width = RFB_GET_UINT16 (data + offset + 4);
779 r.height = RFB_GET_UINT16 (data + offset + 6);
780 r.type = RFB_GET_UINT32 (data + offset + 8);
781
782 if (r.type != TYPE_WMVi) {
783 /* We must have a WMVi packet to initialise things before we can
784 * continue */
785 if (!dec->have_format) {
786 GST_WARNING_OBJECT (dec, "Received packet without WMVi: %d",
787 r.type);
788 return ERROR_INVALID;
789 }
790 if (r.x > dec->format.width || r.y > dec->format.height ||
791 r.x + r.width > dec->format.width ||
792 r.y + r.height > dec->format.height) {
793 GST_WARNING_OBJECT (dec, "Rectangle out of range, type %d", r.type);
794 return ERROR_INVALID;
795 }
796 } else if (r.width > 16384 || r.height > 16384) {
797 GST_WARNING_OBJECT (dec, "Width or height too high: %ux%u", r.width,
798 r.height);
799 return ERROR_INVALID;
800 }
801
802 switch (r.type) {
803 case TYPE_WMVd:
804 handler = vmnc_handle_wmvd_rectangle;
805 break;
806 case TYPE_WMVe:
807 handler = vmnc_handle_wmve_rectangle;
808 break;
809 case TYPE_WMVf:
810 handler = vmnc_handle_wmvf_rectangle;
811 break;
812 case TYPE_WMVg:
813 handler = vmnc_handle_wmvg_rectangle;
814 break;
815 case TYPE_WMVh:
816 handler = vmnc_handle_wmvh_rectangle;
817 break;
818 case TYPE_WMVi:
819 handler = vmnc_handle_wmvi_rectangle;
820 break;
821 case TYPE_WMVj:
822 handler = vmnc_handle_wmvj_rectangle;
823 break;
824 case TYPE_RAW:
825 handler = vmnc_handle_raw_rectangle;
826 break;
827 case TYPE_COPY:
828 handler = vmnc_handle_copy_rectangle;
829 break;
830 case TYPE_HEXTILE:
831 handler = vmnc_handle_hextile_rectangle;
832 break;
833 default:
834 GST_WARNING_OBJECT (dec, "Unknown rectangle type");
835 return ERROR_INVALID;
836 }
837
838 read = handler (dec, &r, data + offset + 12, len - offset - 12, decode);
839 if (read < 0) {
840 GST_DEBUG_OBJECT (dec, "Error calling rectangle handler");
841 return read;
842 }
843 offset += 12 + read;
844 }
845 break;
846 }
847 default:
848 GST_WARNING_OBJECT (dec, "Packet type unknown: %d", type);
849 return ERROR_INVALID;
850 }
851
852 return offset;
853 }
854
855 static gboolean
gst_vmnc_dec_set_format(GstVideoDecoder * decoder,GstVideoCodecState * state)856 gst_vmnc_dec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
857 {
858 GstVMncDec *dec = GST_VMNC_DEC (decoder);
859
860 /* We require a format descriptor in-stream, so we ignore the info from the
861 * container here. We just use the framerate */
862
863 if (dec->input_state)
864 gst_video_codec_state_unref (dec->input_state);
865 dec->input_state = gst_video_codec_state_ref (state);
866
867 return TRUE;
868 }
869
870 static gboolean
gst_vmnc_dec_sink_event(GstVideoDecoder * bdec,GstEvent * event)871 gst_vmnc_dec_sink_event (GstVideoDecoder * bdec, GstEvent * event)
872 {
873 const GstSegment *segment;
874
875 if (GST_EVENT_TYPE (event) != GST_EVENT_SEGMENT)
876 goto done;
877
878 gst_event_parse_segment (event, &segment);
879
880 if (segment->format == GST_FORMAT_TIME)
881 gst_video_decoder_set_packetized (bdec, TRUE);
882 else
883 gst_video_decoder_set_packetized (bdec, FALSE);
884
885 done:
886 return GST_VIDEO_DECODER_CLASS (gst_vmnc_dec_parent_class)->sink_event (bdec,
887 event);
888 }
889
890 static GstFlowReturn
gst_vmnc_dec_handle_frame(GstVideoDecoder * decoder,GstVideoCodecFrame * frame)891 gst_vmnc_dec_handle_frame (GstVideoDecoder * decoder,
892 GstVideoCodecFrame * frame)
893 {
894 GstVMncDec *dec = GST_VMNC_DEC (decoder);
895 int res;
896 GstFlowReturn ret = GST_FLOW_OK;
897 GstMapInfo map;
898
899 if (!gst_buffer_map (frame->input_buffer, &map, GST_MAP_READ))
900 return GST_FLOW_ERROR;
901
902 res = vmnc_handle_packet (dec, map.data, map.size, TRUE);
903
904 gst_buffer_unmap (frame->input_buffer, &map);
905
906 if (!dec->have_format) {
907 GST_VIDEO_DECODER_ERROR (decoder, 2, STREAM, DECODE, (NULL),
908 ("Data found before header"), ret);
909 gst_video_decoder_drop_frame (decoder, frame);
910 } else if (res < 0) {
911 ret = GST_FLOW_ERROR;
912 gst_video_decoder_drop_frame (decoder, frame);
913 GST_VIDEO_DECODER_ERROR (decoder, 1, STREAM, DECODE, (NULL),
914 ("Couldn't decode packet"), ret);
915 } else {
916 GST_LOG_OBJECT (dec, "read %d bytes of %" G_GSIZE_FORMAT, res,
917 gst_buffer_get_size (frame->input_buffer));
918 /* inbuf may be NULL; that's ok */
919 ret = vmnc_fill_buffer (dec, frame);
920 if (ret == GST_FLOW_OK)
921 gst_video_decoder_finish_frame (decoder, frame);
922 else
923 gst_video_decoder_drop_frame (decoder, frame);
924 }
925
926 return ret;
927 }
928
929
930 static GstFlowReturn
gst_vmnc_dec_parse(GstVideoDecoder * decoder,GstVideoCodecFrame * frame,GstAdapter * adapter,gboolean at_eos)931 gst_vmnc_dec_parse (GstVideoDecoder * decoder, GstVideoCodecFrame * frame,
932 GstAdapter * adapter, gboolean at_eos)
933 {
934 GstVMncDec *dec = GST_VMNC_DEC (decoder);
935 const guint8 *data;
936 int avail;
937 int len;
938
939 avail = gst_adapter_available (adapter);
940 data = gst_adapter_map (adapter, avail);
941
942 GST_LOG_OBJECT (dec, "Parsing %d bytes", avail);
943
944 len = vmnc_handle_packet (dec, data, avail, FALSE);
945
946 if (len == ERROR_INSUFFICIENT_DATA) {
947 GST_LOG_OBJECT (dec, "Not enough data yet");
948 return GST_VIDEO_DECODER_FLOW_NEED_DATA;
949 } else if (len < 0) {
950 GST_ERROR_OBJECT (dec, "Fatal error in bitstream");
951 return GST_FLOW_ERROR;
952 } else {
953 GST_LOG_OBJECT (dec, "Parsed packet: %d bytes", len);
954 gst_video_decoder_add_to_frame (decoder, len);
955 return gst_video_decoder_have_frame (decoder);
956 }
957 }
958
959 static gboolean
plugin_init(GstPlugin * plugin)960 plugin_init (GstPlugin * plugin)
961 {
962 return GST_ELEMENT_REGISTER (vmncdec, plugin);
963 }
964
965 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
966 GST_VERSION_MINOR,
967 vmnc,
968 "VmWare Video Codec plugins",
969 plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
970