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