• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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