Imported Upstream version 0.10.23
[profile/ivi/gst-plugins-bad.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., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, 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 <gst/gst.h>
34 #include <gst/base/gstadapter.h>
35 #include <gst/video/video.h>
36 #include <string.h>
37
38 #define GST_CAT_DEFAULT vmnc_debug
39 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
40
41 #define GST_TYPE_VMNC_DEC \
42   (gst_vmnc_dec_get_type())
43 #define GST_VMNC_DEC(obj) \
44   (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_VMNC_DEC,GstVMncDec))
45
46 #define RFB_GET_UINT32(ptr) GST_READ_UINT32_BE(ptr)
47 #define RFB_GET_UINT16(ptr) GST_READ_UINT16_BE(ptr)
48 #define RFB_GET_UINT8(ptr) GST_READ_UINT8(ptr)
49
50 enum
51 {
52   ARG_0,
53 };
54
55 enum
56 {
57   ERROR_INVALID = -1,           /* Invalid data in bitstream */
58   ERROR_INSUFFICIENT_DATA = -2  /* Haven't received enough data yet */
59 };
60
61 #define MAKE_TYPE(a,b,c,d) ((a<<24)|(b<<16)|(c<<8)|d)
62 enum
63 {
64   TYPE_RAW = 0,
65   TYPE_COPY = 1,
66   TYPE_RRE = 2,
67   TYPE_CoRRE = 4,
68   TYPE_HEXTILE = 5,
69
70   TYPE_WMVd = MAKE_TYPE ('W', 'M', 'V', 'd'),
71   TYPE_WMVe = MAKE_TYPE ('W', 'M', 'V', 'e'),
72   TYPE_WMVf = MAKE_TYPE ('W', 'M', 'V', 'f'),
73   TYPE_WMVg = MAKE_TYPE ('W', 'M', 'V', 'g'),
74   TYPE_WMVh = MAKE_TYPE ('W', 'M', 'V', 'h'),
75   TYPE_WMVi = MAKE_TYPE ('W', 'M', 'V', 'i'),
76   TYPE_WMVj = MAKE_TYPE ('W', 'M', 'V', 'j')
77 };
78
79 struct RFBFormat
80 {
81   int width;
82   int height;
83   int stride;
84   int bytes_per_pixel;
85   int depth;
86   int big_endian;
87
88   guint8 descriptor[16];        /* The raw format descriptor block */
89 };
90
91 enum CursorType
92 {
93   CURSOR_COLOUR = 0,
94   CURSOR_ALPHA = 1
95 };
96
97 struct Cursor
98 {
99   enum CursorType type;
100   int visible;
101   int x;
102   int y;
103   int width;
104   int height;
105   int hot_x;
106   int hot_y;
107   guint8 *cursordata;
108   guint8 *cursormask;
109 };
110
111 typedef struct
112 {
113   GstElement element;
114
115   GstPad *sinkpad;
116   GstPad *srcpad;
117
118   GstCaps *caps;
119   gboolean have_format;
120
121   int parsed;
122   GstAdapter *adapter;
123
124   int framerate_num;
125   int framerate_denom;
126
127   struct Cursor cursor;
128   struct RFBFormat format;
129   guint8 *imagedata;
130 } GstVMncDec;
131
132 typedef struct
133 {
134   GstElementClass parent_class;
135 } GstVMncDecClass;
136
137 static GstStaticPadTemplate vmnc_dec_src_factory =
138 GST_STATIC_PAD_TEMPLATE ("src",
139     GST_PAD_SRC,
140     GST_PAD_ALWAYS,
141     GST_STATIC_CAPS ("video/x-raw-rgb"));
142
143 static GstStaticPadTemplate vmnc_dec_sink_factory =
144 GST_STATIC_PAD_TEMPLATE ("sink",
145     GST_PAD_SINK,
146     GST_PAD_ALWAYS,
147     GST_STATIC_CAPS ("video/x-vmnc, version=(int)1, "
148         "framerate=(fraction)[0, max], "
149         "width=(int)[0, max], " "height=(int)[0, max]")
150     );
151
152 GType gst_vmnc_dec_get_type (void);
153 GST_BOILERPLATE (GstVMncDec, gst_vmnc_dec, GstElement, GST_TYPE_ELEMENT);
154
155 static void vmnc_dec_get_property (GObject * object, guint prop_id,
156     GValue * value, GParamSpec * pspec);
157 static void vmnc_dec_set_property (GObject * object, guint prop_id,
158     const GValue * value, GParamSpec * pspec);
159
160 static GstFlowReturn vmnc_dec_chain (GstPad * pad, GstBuffer * buffer);
161 static gboolean vmnc_dec_setcaps (GstPad * pad, GstCaps * caps);
162 static GstStateChangeReturn vmnc_dec_change_state (GstElement * element,
163     GstStateChange transition);
164 static void vmnc_dec_finalize (GObject * object);
165
166 static void
167 gst_vmnc_dec_base_init (gpointer g_class)
168 {
169   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
170
171   gst_element_class_add_static_pad_template (element_class,
172       &vmnc_dec_src_factory);
173   gst_element_class_add_static_pad_template (element_class,
174       &vmnc_dec_sink_factory);
175   gst_element_class_set_details_simple (element_class, "VMnc video decoder",
176       "Codec/Decoder/Video",
177       "Decode VmWare video to raw (RGB) video",
178       "Michael Smith <msmith@xiph.org>");
179 }
180
181 static void
182 gst_vmnc_dec_class_init (GstVMncDecClass * klass)
183 {
184   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
185   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
186
187   gobject_class->set_property = vmnc_dec_set_property;
188   gobject_class->get_property = vmnc_dec_get_property;
189
190   gobject_class->finalize = vmnc_dec_finalize;
191
192   gstelement_class->change_state = vmnc_dec_change_state;
193
194   GST_DEBUG_CATEGORY_INIT (vmnc_debug, "vmncdec", 0, "VMnc decoder");
195 }
196
197 static void
198 gst_vmnc_dec_init (GstVMncDec * dec, GstVMncDecClass * g_class)
199 {
200   dec->sinkpad =
201       gst_pad_new_from_static_template (&vmnc_dec_sink_factory, "sink");
202   gst_pad_set_chain_function (dec->sinkpad, vmnc_dec_chain);
203   gst_pad_set_setcaps_function (dec->sinkpad, vmnc_dec_setcaps);
204   gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
205
206   dec->srcpad = gst_pad_new_from_static_template (&vmnc_dec_src_factory, "src");
207   gst_pad_use_fixed_caps (dec->srcpad);
208
209   gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
210
211   dec->adapter = gst_adapter_new ();
212 }
213
214 static void
215 vmnc_dec_finalize (GObject * object)
216 {
217   GstVMncDec *dec = GST_VMNC_DEC (object);
218
219   g_object_unref (dec->adapter);
220
221   G_OBJECT_CLASS (parent_class)->finalize (object);
222 }
223
224 static void
225 gst_vmnc_dec_reset (GstVMncDec * dec)
226 {
227   if (dec->caps) {
228     gst_caps_unref (dec->caps);
229     dec->caps = NULL;
230   }
231   if (dec->imagedata) {
232     g_free (dec->imagedata);
233     dec->imagedata = NULL;
234   }
235
236   if (dec->cursor.cursordata) {
237     g_free (dec->cursor.cursordata);
238     dec->cursor.cursordata = NULL;
239   }
240   if (dec->cursor.cursormask) {
241     g_free (dec->cursor.cursormask);
242     dec->cursor.cursormask = NULL;
243   }
244   dec->cursor.visible = 0;
245
246   /* Use these as defaults if the container doesn't provide anything */
247   dec->framerate_num = 5;
248   dec->framerate_denom = 1;
249
250   dec->have_format = FALSE;
251
252   gst_adapter_clear (dec->adapter);
253 }
254
255 struct RfbRectangle
256 {
257   guint16 x;
258   guint16 y;
259   guint16 width;
260   guint16 height;
261
262   gint32 type;
263 };
264
265 /* Rectangle handling functions.
266  * Return number of bytes consumed, or < 0 on error
267  */
268 typedef int (*rectangle_handler) (GstVMncDec * dec, struct RfbRectangle * rect,
269     const guint8 * data, int len, gboolean decode);
270
271 static int
272 vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
273     const guint8 * data, int len, gboolean decode)
274 {
275   GstCaps *caps;
276   gint bpp, tc;
277   guint32 redmask, greenmask, bluemask;
278   guint32 endianness, dataendianness;
279
280   /* A WMVi rectangle has a 16byte payload */
281   if (len < 16) {
282     GST_DEBUG_OBJECT (dec, "Bad WMVi rect: too short");
283     return ERROR_INSUFFICIENT_DATA;
284   }
285
286   /* We only compare 13 bytes; ignoring the 3 padding bytes at the end */
287   if (dec->caps && memcmp (data, dec->format.descriptor, 13) == 0) {
288     /* Nothing changed, so just exit */
289     return 16;
290   }
291
292   /* Store the whole block for simple comparison later */
293   memcpy (dec->format.descriptor, data, 16);
294
295   if (rect->x != 0 || rect->y != 0) {
296     GST_WARNING_OBJECT (dec, "Bad WMVi rect: wrong coordinates");
297     return ERROR_INVALID;
298   }
299
300   bpp = data[0];
301   dec->format.depth = data[1];
302   dec->format.big_endian = data[2];
303   dataendianness = data[2] ? G_BIG_ENDIAN : G_LITTLE_ENDIAN;
304   tc = data[3];
305
306   if (bpp != 8 && bpp != 16 && bpp != 32) {
307     GST_WARNING_OBJECT (dec, "Bad bpp value: %d", bpp);
308     return ERROR_INVALID;
309   }
310
311   if (!tc) {
312     GST_WARNING_OBJECT (dec, "Paletted video not supported");
313     return ERROR_INVALID;
314   }
315
316   dec->format.bytes_per_pixel = bpp / 8;
317   dec->format.width = rect->width;
318   dec->format.height = rect->height;
319
320   redmask = (guint32) (RFB_GET_UINT16 (data + 4)) << data[10];
321   greenmask = (guint32) (RFB_GET_UINT16 (data + 6)) << data[11];
322   bluemask = (guint32) (RFB_GET_UINT16 (data + 8)) << data[12];
323
324   GST_DEBUG_OBJECT (dec, "Red: mask %d, shift %d",
325       RFB_GET_UINT16 (data + 4), data[10]);
326   GST_DEBUG_OBJECT (dec, "Green: mask %d, shift %d",
327       RFB_GET_UINT16 (data + 6), data[11]);
328   GST_DEBUG_OBJECT (dec, "Blue: mask %d, shift %d",
329       RFB_GET_UINT16 (data + 8), data[12]);
330   GST_DEBUG_OBJECT (dec, "BPP: %d. endianness: %s", bpp,
331       data[2] ? "big" : "little");
332
333   /* GStreamer's RGB caps are a bit weird. */
334   if (bpp == 8) {
335     endianness = G_BYTE_ORDER;  /* Doesn't matter */
336   } else if (bpp == 16) {
337     /* We require host-endian. */
338     endianness = G_BYTE_ORDER;
339   } else {                      /* bpp == 32 */
340     /* We require big endian */
341     endianness = G_BIG_ENDIAN;
342     if (endianness != dataendianness) {
343       redmask = GUINT32_SWAP_LE_BE (redmask);
344       greenmask = GUINT32_SWAP_LE_BE (greenmask);
345       bluemask = GUINT32_SWAP_LE_BE (bluemask);
346     }
347   }
348
349   dec->have_format = TRUE;
350   if (!decode) {
351     GST_DEBUG_OBJECT (dec, "Parsing, not setting caps");
352     return 16;
353   }
354
355   caps = gst_caps_new_simple ("video/x-raw-rgb",
356       "framerate", GST_TYPE_FRACTION, dec->framerate_num, dec->framerate_denom,
357       "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
358       "width", G_TYPE_INT, rect->width, "height", G_TYPE_INT, rect->height,
359       "bpp", G_TYPE_INT, bpp,
360       "depth", G_TYPE_INT, dec->format.depth,
361       "endianness", G_TYPE_INT, endianness,
362       "red_mask", G_TYPE_INT, redmask,
363       "green_mask", G_TYPE_INT, greenmask,
364       "blue_mask", G_TYPE_INT, bluemask, NULL);
365   gst_pad_set_caps (dec->srcpad, caps);
366
367   if (dec->caps)
368     gst_caps_unref (dec->caps);
369   dec->caps = caps;
370
371   if (dec->imagedata)
372     g_free (dec->imagedata);
373   dec->imagedata = g_malloc (dec->format.width * dec->format.height *
374       dec->format.bytes_per_pixel);
375   GST_DEBUG_OBJECT (dec, "Allocated image data at %p", dec->imagedata);
376
377   dec->format.stride = dec->format.width * dec->format.bytes_per_pixel;
378
379   return 16;
380 }
381
382 static void
383 render_colour_cursor (GstVMncDec * dec, guint8 * data, int x, int y,
384     int off_x, int off_y, int width, int height)
385 {
386   int i, j;
387   guint8 *dstraw = data + dec->format.stride * y +
388       dec->format.bytes_per_pixel * x;
389   guint8 *srcraw = dec->cursor.cursordata +
390       dec->cursor.width * dec->format.bytes_per_pixel * off_y;
391   guint8 *maskraw = dec->cursor.cursormask +
392       dec->cursor.width * dec->format.bytes_per_pixel * off_y;
393
394   /* Boundchecking done by caller; this is just the renderer inner loop */
395   if (dec->format.bytes_per_pixel == 1) {
396     guint8 *dst = dstraw;
397     guint8 *src = srcraw;
398     guint8 *mask = maskraw;
399
400     for (i = 0; i < height; i++) {
401       for (j = 0; j < width; j++) {
402         dst[j] = (dst[j] & src[j]) ^ mask[j];
403       }
404       dst += dec->format.width;
405       src += dec->cursor.width;
406       mask += dec->cursor.width;
407     }
408   } else if (dec->format.bytes_per_pixel == 2) {
409     guint16 *dst = (guint16 *) dstraw;
410     guint16 *src = (guint16 *) srcraw;
411     guint16 *mask = (guint16 *) maskraw;
412
413     for (i = 0; i < height; i++) {
414       for (j = 0; j < width; j++) {
415         dst[j] = (dst[j] & src[j]) ^ mask[j];
416       }
417       dst += dec->format.width;
418       src += dec->cursor.width;
419       mask += dec->cursor.width;
420     }
421   } else {
422     guint32 *dst = (guint32 *) dstraw;
423     guint32 *src = (guint32 *) srcraw;
424     guint32 *mask = (guint32 *) maskraw;
425
426     for (i = 0; i < height; i++) {
427       for (j = 0; j < width; j++) {
428         dst[j] = (dst[j] & src[j]) ^ mask[j];
429       }
430       dst += dec->format.width;
431       src += dec->cursor.width;
432       mask += dec->cursor.width;
433     }
434   }
435 }
436
437 static void
438 render_cursor (GstVMncDec * dec, guint8 * data)
439 {
440   /* First, figure out the portion of the cursor that's on-screen */
441   /* X,Y of top-left of cursor */
442   int x = dec->cursor.x - dec->cursor.hot_x;
443   int y = dec->cursor.y - dec->cursor.hot_y;
444
445   /* Width, height of rendered portion of cursor */
446   int width = dec->cursor.width;
447   int height = dec->cursor.height;
448
449   /* X,Y offset of rendered portion of cursor */
450   int off_x = 0;
451   int off_y = 0;
452
453   if (x < 0) {
454     off_x = -x;
455     width += x;
456     x = 0;
457   }
458   if (x + width > dec->format.width)
459     width = dec->format.width - x;
460   if (y < 0) {
461     off_y = -y;
462     height += y;
463     y = 0;
464   }
465   if (y + height > dec->format.height)
466     height = dec->format.height - y;
467
468   if (dec->cursor.type == CURSOR_COLOUR) {
469     render_colour_cursor (dec, data, x, y, off_x, off_y, width, height);
470   } else {
471     /* Alpha cursor. */
472     /* TODO: Implement me! */
473     GST_WARNING_OBJECT (dec, "Alpha composited cursors not yet implemented");
474   }
475 }
476
477 static GstBuffer *
478 vmnc_make_buffer (GstVMncDec * dec, GstBuffer * inbuf)
479 {
480   int size = dec->format.stride * dec->format.height;
481   GstBuffer *buf = gst_buffer_new_and_alloc (size);
482   guint8 *data = GST_BUFFER_DATA (buf);
483
484   memcpy (data, dec->imagedata, size);
485
486   if (dec->cursor.visible) {
487     render_cursor (dec, data);
488   }
489
490   if (inbuf) {
491     gst_buffer_copy_metadata (buf, inbuf, GST_BUFFER_COPY_TIMESTAMPS);
492   }
493
494   gst_buffer_set_caps (buf, dec->caps);
495
496   return buf;
497 }
498
499 static int
500 vmnc_handle_wmvd_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
501     const guint8 * data, int len, gboolean decode)
502 {
503   /* Cursor data. */
504   int datalen = 2;
505   int type, size;
506
507   if (len < datalen) {
508     GST_DEBUG_OBJECT (dec, "Cursor data too short");
509     return ERROR_INSUFFICIENT_DATA;
510   }
511
512   type = RFB_GET_UINT8 (data);
513
514   if (type == CURSOR_COLOUR) {
515     datalen += rect->width * rect->height * dec->format.bytes_per_pixel * 2;
516   } else if (type == CURSOR_ALPHA) {
517     datalen += rect->width * rect->height * 4;
518   } else {
519     GST_WARNING_OBJECT (dec, "Unknown cursor type: %d", type);
520     return ERROR_INVALID;
521   }
522
523   if (len < datalen) {
524     GST_DEBUG_OBJECT (dec, "Cursor data too short");
525     return ERROR_INSUFFICIENT_DATA;
526   } else if (!decode)
527     return datalen;
528
529   dec->cursor.type = type;
530   dec->cursor.width = rect->width;
531   dec->cursor.height = rect->height;
532   dec->cursor.type = type;
533   dec->cursor.hot_x = rect->x;
534   dec->cursor.hot_y = rect->y;
535
536   if (dec->cursor.cursordata)
537     g_free (dec->cursor.cursordata);
538   if (dec->cursor.cursormask)
539     g_free (dec->cursor.cursormask);
540
541   if (type == 0) {
542     size = rect->width * rect->height * dec->format.bytes_per_pixel;
543     dec->cursor.cursordata = g_malloc (size);
544     dec->cursor.cursormask = g_malloc (size);
545     memcpy (dec->cursor.cursordata, data + 2, size);
546     memcpy (dec->cursor.cursormask, data + 2 + size, size);
547   } else {
548     dec->cursor.cursordata = g_malloc (rect->width * rect->height * 4);
549     memcpy (dec->cursor.cursordata, data + 2, rect->width * rect->height * 4);
550   }
551
552   return datalen;
553 }
554
555 static int
556 vmnc_handle_wmve_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
557     const guint8 * data, int len, gboolean decode)
558 {
559   guint16 flags;
560
561   /* Cursor state. */
562   if (len < 2) {
563     GST_DEBUG_OBJECT (dec, "Cursor data too short");
564     return ERROR_INSUFFICIENT_DATA;
565   } else if (!decode)
566     return 2;
567
568   flags = RFB_GET_UINT16 (data);
569   dec->cursor.visible = flags & 0x01;
570
571   return 2;
572 }
573
574 static int
575 vmnc_handle_wmvf_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
576     const guint8 * data, int len, gboolean decode)
577 {
578   /* Cursor position. */
579   dec->cursor.x = rect->x;
580   dec->cursor.y = rect->y;
581   return 0;
582 }
583
584 static int
585 vmnc_handle_wmvg_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
586     const guint8 * data, int len, gboolean decode)
587 {
588   /* Keyboard stuff; not interesting for playback */
589   if (len < 10) {
590     GST_DEBUG_OBJECT (dec, "Keyboard data too short");
591     return ERROR_INSUFFICIENT_DATA;
592   }
593   return 10;
594 }
595
596 static int
597 vmnc_handle_wmvh_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
598     const guint8 * data, int len, gboolean decode)
599 {
600   /* More keyboard stuff; not interesting for playback */
601   if (len < 4) {
602     GST_DEBUG_OBJECT (dec, "Keyboard data too short");
603     return ERROR_INSUFFICIENT_DATA;
604   }
605   return 4;
606 }
607
608 static int
609 vmnc_handle_wmvj_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
610     const guint8 * data, int len, gboolean decode)
611 {
612   /* VM state info, not interesting for playback */
613   if (len < 2) {
614     GST_DEBUG_OBJECT (dec, "VM state data too short");
615     return ERROR_INSUFFICIENT_DATA;
616   }
617   return 2;
618 }
619
620 static void
621 render_raw_tile (GstVMncDec * dec, const guint8 * data, int x, int y,
622     int width, int height)
623 {
624   int i;
625   guint8 *dst;
626   const guint8 *src;
627   int line;
628
629   src = data;
630   dst = dec->imagedata + dec->format.stride * y +
631       dec->format.bytes_per_pixel * x;
632   line = width * dec->format.bytes_per_pixel;
633
634   for (i = 0; i < height; i++) {
635     /* This is wrong-endian currently */
636     memcpy (dst, src, line);
637
638     dst += dec->format.stride;
639     src += line;
640   }
641 }
642
643 static void
644 render_subrect (GstVMncDec * dec, int x, int y, int width,
645     int height, guint32 colour)
646 {
647   /* Crazy inefficient! */
648   int i, j;
649   guint8 *dst;
650
651   for (i = 0; i < height; i++) {
652     dst = dec->imagedata + dec->format.stride * (y + i) +
653         dec->format.bytes_per_pixel * x;
654     for (j = 0; j < width; j++) {
655       memcpy (dst, &colour, dec->format.bytes_per_pixel);
656       dst += dec->format.bytes_per_pixel;
657     }
658   }
659 }
660
661 static int
662 vmnc_handle_raw_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
663     const guint8 * data, int len, gboolean decode)
664 {
665   int datalen = rect->width * rect->height * dec->format.bytes_per_pixel;
666
667   if (len < datalen) {
668     GST_DEBUG_OBJECT (dec, "Raw data too short");
669     return ERROR_INSUFFICIENT_DATA;
670   }
671
672   if (decode)
673     render_raw_tile (dec, data, rect->x, rect->y, rect->width, rect->height);
674
675   return datalen;
676 }
677
678 static int
679 vmnc_handle_copy_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
680     const guint8 * data, int len, gboolean decode)
681 {
682   int src_x, src_y;
683   guint8 *src, *dst;
684   int i;
685
686   if (len < 4) {
687     GST_DEBUG_OBJECT (dec, "Copy data too short");
688     return ERROR_INSUFFICIENT_DATA;
689   } else if (!decode)
690     return 4;
691
692   src_x = RFB_GET_UINT16 (data);
693   src_y = RFB_GET_UINT16 (data + 2);
694
695   /* Our destination rectangle is guaranteed in-frame; check this for the source
696    * rectangle. */
697   if (src_x + rect->width > dec->format.width ||
698       src_y + rect->height > dec->format.height) {
699     GST_WARNING_OBJECT (dec, "Source rectangle out of range");
700     return ERROR_INVALID;
701   }
702
703   if (src_y > rect->y || src_x > rect->x) {
704     /* Moving forward */
705     src = dec->imagedata + dec->format.stride * src_y +
706         dec->format.bytes_per_pixel * src_x;
707     dst = dec->imagedata + dec->format.stride * rect->y +
708         dec->format.bytes_per_pixel * rect->x;
709     for (i = 0; i < rect->height; i++) {
710       memmove (dst, src, rect->width * dec->format.bytes_per_pixel);
711       dst += dec->format.stride;
712       src += dec->format.stride;
713     }
714   } else {
715     /* Backwards */
716     src = dec->imagedata + dec->format.stride * (src_y + rect->height - 1) +
717         dec->format.bytes_per_pixel * src_x;
718     dst = dec->imagedata + dec->format.stride * (rect->y + rect->height - 1) +
719         dec->format.bytes_per_pixel * rect->x;
720     for (i = rect->height; i > 0; i--) {
721       memmove (dst, src, rect->width * dec->format.bytes_per_pixel);
722       dst -= dec->format.stride;
723       src -= dec->format.stride;
724     }
725   }
726
727   return 4;
728 }
729
730 /* FIXME: data+off might not be properly aligned */
731 #define READ_PIXEL(pixel, data, off, len)         \
732   if (dec->format.bytes_per_pixel == 1) {         \
733      if (off >= len)                              \
734        return ERROR_INSUFFICIENT_DATA;            \
735      pixel = data[off++];                         \
736   } else if (dec->format.bytes_per_pixel == 2) {  \
737      if (off+2 > len)                             \
738        return ERROR_INSUFFICIENT_DATA;            \
739      pixel = (*(guint16 *)(data + off));          \
740      off += 2;                                    \
741   } else {                                        \
742      if (off+4 > len)                             \
743        return ERROR_INSUFFICIENT_DATA;            \
744      pixel = (*(guint32 *)(data + off));          \
745      off += 4;                                    \
746   }
747
748 static int
749 vmnc_handle_hextile_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
750     const guint8 * data, int len, gboolean decode)
751 {
752   int tilesx = GST_ROUND_UP_16 (rect->width) / 16;
753   int tilesy = GST_ROUND_UP_16 (rect->height) / 16;
754   int x, y, z;
755   int off = 0;
756   int subrects;
757   int coloured;
758   int width, height;
759   guint32 fg = 0, bg = 0, colour;
760   guint8 flags;
761
762   for (y = 0; y < tilesy; y++) {
763     if (y == tilesy - 1)
764       height = rect->height - (tilesy - 1) * 16;
765     else
766       height = 16;
767
768     for (x = 0; x < tilesx; x++) {
769       if (x == tilesx - 1)
770         width = rect->width - (tilesx - 1) * 16;
771       else
772         width = 16;
773
774       if (off >= len) {
775         return ERROR_INSUFFICIENT_DATA;
776       }
777       flags = data[off++];
778
779       if (flags & 0x1) {
780         if (off + width * height * dec->format.bytes_per_pixel > len) {
781           return ERROR_INSUFFICIENT_DATA;
782         }
783         if (decode)
784           render_raw_tile (dec, data + off, rect->x + x * 16, rect->y + y * 16,
785               width, height);
786         off += width * height * dec->format.bytes_per_pixel;
787       } else {
788         if (flags & 0x2) {
789           READ_PIXEL (bg, data, off, len)
790         }
791         if (flags & 0x4) {
792           READ_PIXEL (fg, data, off, len)
793         }
794
795         subrects = 0;
796         if (flags & 0x8) {
797           if (off >= len) {
798             return ERROR_INSUFFICIENT_DATA;
799           }
800           subrects = data[off++];
801         }
802
803         /* Paint background colour on entire tile */
804         if (decode)
805           render_subrect (dec, rect->x + x * 16, rect->y + y * 16,
806               width, height, bg);
807
808         coloured = flags & 0x10;
809         for (z = 0; z < subrects; z++) {
810           if (coloured) {
811             READ_PIXEL (colour, data, off, len);
812           } else
813             colour = fg;
814           if (off + 2 > len)
815             return ERROR_INSUFFICIENT_DATA;
816
817           {
818             int off_x = (data[off] & 0xf0) >> 4;
819             int off_y = (data[off] & 0x0f);
820             int w = ((data[off + 1] & 0xf0) >> 4) + 1;
821             int h = (data[off + 1] & 0x0f) + 1;
822
823             off += 2;
824
825             /* Ensure we don't have out of bounds coordinates */
826             if (off_x + w > width || off_y + h > height) {
827               GST_WARNING_OBJECT (dec, "Subrect out of bounds: %d-%d x %d-%d "
828                   "extends outside %dx%d", off_x, w, off_y, h, width, height);
829               return ERROR_INVALID;
830             }
831
832             if (decode)
833               render_subrect (dec, rect->x + x * 16 + off_x,
834                   rect->y + y * 16 + off_y, w, h, colour);
835           }
836         }
837       }
838     }
839   }
840
841   return off;
842 }
843
844 /* Handle a packet in one of two modes: decode or parse.
845  * In parse mode, we don't execute any of the decoding, we just do enough to
846  * figure out how many bytes it contains
847  *
848  * Returns: >= 0, the number of bytes consumed
849  *           < 0, packet too short to decode, or error
850  */
851 static int
852 vmnc_handle_packet (GstVMncDec * dec, const guint8 * data, int len,
853     gboolean decode)
854 {
855   int type;
856   int offset = 0;
857
858   if (len < 4) {
859     GST_DEBUG_OBJECT (dec, "Packet too short");
860     return ERROR_INSUFFICIENT_DATA;
861   }
862
863   type = data[0];
864
865   switch (type) {
866     case 0:
867     {
868       int numrect = RFB_GET_UINT16 (data + 2);
869       int i;
870       int read;
871
872       offset = 4;
873
874       for (i = 0; i < numrect; i++) {
875         struct RfbRectangle r;
876         rectangle_handler handler;
877
878         if (len < offset + 12) {
879           GST_DEBUG_OBJECT (dec,
880               "Packet too short for rectangle header: %d < %d",
881               len, offset + 12);
882           return ERROR_INSUFFICIENT_DATA;
883         }
884         GST_DEBUG_OBJECT (dec, "Reading rectangle %d", i);
885         r.x = RFB_GET_UINT16 (data + offset);
886         r.y = RFB_GET_UINT16 (data + offset + 2);
887         r.width = RFB_GET_UINT16 (data + offset + 4);
888         r.height = RFB_GET_UINT16 (data + offset + 6);
889         r.type = RFB_GET_UINT32 (data + offset + 8);
890
891         if (r.type != TYPE_WMVi) {
892           /* We must have a WMVi packet to initialise things before we can 
893            * continue */
894           if (!dec->have_format) {
895             GST_WARNING_OBJECT (dec, "Received packet without WMVi: %d",
896                 r.type);
897             return ERROR_INVALID;
898           }
899           if (r.x + r.width > dec->format.width ||
900               r.y + r.height > dec->format.height) {
901             GST_WARNING_OBJECT (dec, "Rectangle out of range, type %d", r.type);
902             return ERROR_INVALID;
903           }
904         }
905
906         switch (r.type) {
907           case TYPE_WMVd:
908             handler = vmnc_handle_wmvd_rectangle;
909             break;
910           case TYPE_WMVe:
911             handler = vmnc_handle_wmve_rectangle;
912             break;
913           case TYPE_WMVf:
914             handler = vmnc_handle_wmvf_rectangle;
915             break;
916           case TYPE_WMVg:
917             handler = vmnc_handle_wmvg_rectangle;
918             break;
919           case TYPE_WMVh:
920             handler = vmnc_handle_wmvh_rectangle;
921             break;
922           case TYPE_WMVi:
923             handler = vmnc_handle_wmvi_rectangle;
924             break;
925           case TYPE_WMVj:
926             handler = vmnc_handle_wmvj_rectangle;
927             break;
928           case TYPE_RAW:
929             handler = vmnc_handle_raw_rectangle;
930             break;
931           case TYPE_COPY:
932             handler = vmnc_handle_copy_rectangle;
933             break;
934           case TYPE_HEXTILE:
935             handler = vmnc_handle_hextile_rectangle;
936             break;
937           default:
938             GST_WARNING_OBJECT (dec, "Unknown rectangle type");
939             return ERROR_INVALID;
940         }
941
942         read = handler (dec, &r, data + offset + 12, len - offset - 12, decode);
943         if (read < 0) {
944           GST_DEBUG_OBJECT (dec, "Error calling rectangle handler\n");
945           return read;
946         }
947         offset += 12 + read;
948       }
949       break;
950     }
951     default:
952       GST_WARNING_OBJECT (dec, "Packet type unknown: %d", type);
953       return ERROR_INVALID;
954   }
955
956   return offset;
957 }
958
959 static gboolean
960 vmnc_dec_setcaps (GstPad * pad, GstCaps * caps)
961 {
962   /* We require a format descriptor in-stream, so we ignore the info from the
963    * container here. We just use the framerate */
964   GstVMncDec *dec = GST_VMNC_DEC (gst_pad_get_parent (pad));
965
966   if (gst_caps_get_size (caps) > 0) {
967     GstStructure *structure = gst_caps_get_structure (caps, 0);
968
969     /* We gave these a default in reset(), so we don't need to check for failure
970      * here */
971     gst_structure_get_fraction (structure, "framerate",
972         &dec->framerate_num, &dec->framerate_denom);
973
974     dec->parsed = TRUE;
975   } else {
976     GST_DEBUG_OBJECT (dec, "Unparsed input");
977     dec->parsed = FALSE;
978   }
979
980   gst_object_unref (dec);
981
982   return TRUE;
983 }
984
985 static GstFlowReturn
986 vmnc_dec_chain_frame (GstVMncDec * dec, GstBuffer * inbuf,
987     const guint8 * data, int len)
988 {
989   int res;
990   GstFlowReturn ret = GST_FLOW_OK;
991   GstBuffer *outbuf;
992
993   res = vmnc_handle_packet (dec, data, len, TRUE);
994
995   if (res < 0) {
996     ret = GST_FLOW_ERROR;
997     GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Couldn't decode packet"));
998   } else {
999     GST_DEBUG_OBJECT (dec, "read %d bytes of %d", res, len);
1000     /* inbuf may be NULL; that's ok */
1001     outbuf = vmnc_make_buffer (dec, inbuf);
1002     ret = gst_pad_push (dec->srcpad, outbuf);
1003   }
1004
1005   return ret;
1006 }
1007
1008 static GstFlowReturn
1009 vmnc_dec_chain (GstPad * pad, GstBuffer * buf)
1010 {
1011   GstVMncDec *dec;
1012   GstFlowReturn ret = GST_FLOW_OK;
1013
1014   dec = GST_VMNC_DEC (gst_pad_get_parent (pad));
1015
1016   if (!dec->parsed) {
1017     /* Submit our input buffer to adapter, then parse. Push each frame found
1018      * through the decoder
1019      */
1020     int avail;
1021     const guint8 *data;
1022     int read = 0;
1023
1024     gst_adapter_push (dec->adapter, buf);
1025
1026
1027     avail = gst_adapter_available (dec->adapter);
1028     data = gst_adapter_peek (dec->adapter, avail);
1029
1030     GST_DEBUG_OBJECT (dec, "Parsing %d bytes", avail);
1031
1032     while (TRUE) {
1033       int len = vmnc_handle_packet (dec, data, avail, FALSE);
1034
1035       if (len == ERROR_INSUFFICIENT_DATA) {
1036         GST_DEBUG_OBJECT (dec, "Not enough data yet");
1037         ret = GST_FLOW_OK;
1038         break;
1039       } else if (len < 0) {
1040         GST_DEBUG_OBJECT (dec, "Fatal error in bitstream");
1041         ret = GST_FLOW_ERROR;
1042         break;
1043       }
1044
1045       GST_DEBUG_OBJECT (dec, "Parsed packet: %d bytes", len);
1046
1047       ret = vmnc_dec_chain_frame (dec, NULL, data, len);
1048       avail -= len;
1049       data += len;
1050
1051       read += len;
1052
1053       if (ret != GST_FLOW_OK)
1054         break;
1055     }
1056     GST_DEBUG_OBJECT (dec, "Flushing %d bytes", read);
1057
1058     gst_adapter_flush (dec->adapter, read);
1059   } else {
1060     ret = vmnc_dec_chain_frame (dec, buf, GST_BUFFER_DATA (buf),
1061         GST_BUFFER_SIZE (buf));
1062     gst_buffer_unref (buf);
1063   }
1064
1065   gst_object_unref (dec);
1066
1067   return ret;
1068 }
1069
1070 static GstStateChangeReturn
1071 vmnc_dec_change_state (GstElement * element, GstStateChange transition)
1072 {
1073   GstVMncDec *dec = GST_VMNC_DEC (element);
1074   GstStateChangeReturn ret;
1075
1076   switch (transition) {
1077     case GST_STATE_CHANGE_NULL_TO_READY:
1078       break;
1079     case GST_STATE_CHANGE_READY_TO_PAUSED:
1080       gst_vmnc_dec_reset (dec);
1081       break;
1082     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1083       break;
1084     default:
1085       break;
1086   }
1087
1088   ret = parent_class->change_state (element, transition);
1089
1090   switch (transition) {
1091     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1092       break;
1093     case GST_STATE_CHANGE_PAUSED_TO_READY:
1094       gst_vmnc_dec_reset (dec);
1095       break;
1096     case GST_STATE_CHANGE_READY_TO_NULL:
1097       break;
1098     default:
1099       break;
1100   }
1101
1102   return ret;
1103 }
1104
1105 static void
1106 vmnc_dec_set_property (GObject * object, guint prop_id,
1107     const GValue * value, GParamSpec * pspec)
1108 {
1109   /*GstVMncDec *dec = GST_VMNC_DEC (object); */
1110
1111   switch (prop_id) {
1112     default:
1113       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1114       break;
1115   }
1116 }
1117
1118 static void
1119 vmnc_dec_get_property (GObject * object, guint prop_id,
1120     GValue * value, GParamSpec * pspec)
1121 {
1122   /*GstVMncDec *dec = GST_VMNC_DEC (object); */
1123
1124   switch (prop_id) {
1125     default:
1126       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1127       break;
1128   }
1129 }
1130
1131 static gboolean
1132 plugin_init (GstPlugin * plugin)
1133 {
1134   if (!gst_element_register (plugin, "vmncdec", GST_RANK_PRIMARY,
1135           gst_vmnc_dec_get_type ()))
1136     return FALSE;
1137   return TRUE;
1138 }
1139
1140 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1141     GST_VERSION_MINOR,
1142     "vmnc",
1143     "VmWare Video Codec plugins",
1144     plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)