2 * Copyright (C) 2007 Michael Smith <msmith@xiph.org>
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.
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.
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.
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
34 #include <gst/base/gstadapter.h>
35 #include <gst/video/video.h>
38 #define GST_CAT_DEFAULT vmnc_debug
39 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
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))
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)
57 ERROR_INVALID = -1, /* Invalid data in bitstream */
58 ERROR_INSUFFICIENT_DATA = -2 /* Haven't received enough data yet */
61 #define MAKE_TYPE(a,b,c,d) ((a<<24)|(b<<16)|(c<<8)|d)
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')
88 guint8 descriptor[16]; /* The raw format descriptor block */
119 gboolean have_format;
127 struct Cursor cursor;
128 struct RFBFormat format;
134 GstElementClass parent_class;
137 static GstStaticPadTemplate vmnc_dec_src_factory =
138 GST_STATIC_PAD_TEMPLATE ("src",
141 GST_STATIC_CAPS ("video/x-raw-rgb"));
143 static GstStaticPadTemplate vmnc_dec_sink_factory =
144 GST_STATIC_PAD_TEMPLATE ("sink",
147 GST_STATIC_CAPS ("video/x-vmnc, version=(int)1, "
148 "framerate=(fraction)[0, max], "
149 "width=(int)[0, max], " "height=(int)[0, max]")
152 GType gst_vmnc_dec_get_type (void);
153 GST_BOILERPLATE (GstVMncDec, gst_vmnc_dec, GstElement, GST_TYPE_ELEMENT);
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);
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);
167 gst_vmnc_dec_base_init (gpointer g_class)
169 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
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>");
182 gst_vmnc_dec_class_init (GstVMncDecClass * klass)
184 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
185 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
187 gobject_class->set_property = vmnc_dec_set_property;
188 gobject_class->get_property = vmnc_dec_get_property;
190 gobject_class->finalize = vmnc_dec_finalize;
192 gstelement_class->change_state = vmnc_dec_change_state;
194 GST_DEBUG_CATEGORY_INIT (vmnc_debug, "vmncdec", 0, "VMnc decoder");
198 gst_vmnc_dec_init (GstVMncDec * dec, GstVMncDecClass * g_class)
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);
206 dec->srcpad = gst_pad_new_from_static_template (&vmnc_dec_src_factory, "src");
207 gst_pad_use_fixed_caps (dec->srcpad);
209 gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
211 dec->adapter = gst_adapter_new ();
215 vmnc_dec_finalize (GObject * object)
217 GstVMncDec *dec = GST_VMNC_DEC (object);
219 g_object_unref (dec->adapter);
221 G_OBJECT_CLASS (parent_class)->finalize (object);
225 gst_vmnc_dec_reset (GstVMncDec * dec)
228 gst_caps_unref (dec->caps);
231 if (dec->imagedata) {
232 g_free (dec->imagedata);
233 dec->imagedata = NULL;
236 if (dec->cursor.cursordata) {
237 g_free (dec->cursor.cursordata);
238 dec->cursor.cursordata = NULL;
240 if (dec->cursor.cursormask) {
241 g_free (dec->cursor.cursormask);
242 dec->cursor.cursormask = NULL;
244 dec->cursor.visible = 0;
246 /* Use these as defaults if the container doesn't provide anything */
247 dec->framerate_num = 5;
248 dec->framerate_denom = 1;
250 dec->have_format = FALSE;
252 gst_adapter_clear (dec->adapter);
265 /* Rectangle handling functions.
266 * Return number of bytes consumed, or < 0 on error
268 typedef int (*rectangle_handler) (GstVMncDec * dec, struct RfbRectangle * rect,
269 const guint8 * data, int len, gboolean decode);
272 vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
273 const guint8 * data, int len, gboolean decode)
277 guint32 redmask, greenmask, bluemask;
278 guint32 endianness, dataendianness;
280 /* A WMVi rectangle has a 16byte payload */
282 GST_DEBUG_OBJECT (dec, "Bad WMVi rect: too short");
283 return ERROR_INSUFFICIENT_DATA;
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 */
292 /* Store the whole block for simple comparison later */
293 memcpy (dec->format.descriptor, data, 16);
295 if (rect->x != 0 || rect->y != 0) {
296 GST_WARNING_OBJECT (dec, "Bad WMVi rect: wrong coordinates");
297 return ERROR_INVALID;
301 dec->format.depth = data[1];
302 dec->format.big_endian = data[2];
303 dataendianness = data[2] ? G_BIG_ENDIAN : G_LITTLE_ENDIAN;
306 if (bpp != 8 && bpp != 16 && bpp != 32) {
307 GST_WARNING_OBJECT (dec, "Bad bpp value: %d", bpp);
308 return ERROR_INVALID;
312 GST_WARNING_OBJECT (dec, "Paletted video not supported");
313 return ERROR_INVALID;
316 dec->format.bytes_per_pixel = bpp / 8;
317 dec->format.width = rect->width;
318 dec->format.height = rect->height;
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];
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");
333 /* GStreamer's RGB caps are a bit weird. */
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);
349 dec->have_format = TRUE;
351 GST_DEBUG_OBJECT (dec, "Parsing, not setting caps");
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);
368 gst_caps_unref (dec->caps);
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);
377 dec->format.stride = dec->format.width * dec->format.bytes_per_pixel;
383 render_colour_cursor (GstVMncDec * dec, guint8 * data, int x, int y,
384 int off_x, int off_y, int width, int height)
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;
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;
400 for (i = 0; i < height; i++) {
401 for (j = 0; j < width; j++) {
402 dst[j] = (dst[j] & src[j]) ^ mask[j];
404 dst += dec->format.width;
405 src += dec->cursor.width;
406 mask += dec->cursor.width;
408 } else if (dec->format.bytes_per_pixel == 2) {
409 guint16 *dst = (guint16 *) dstraw;
410 guint16 *src = (guint16 *) srcraw;
411 guint16 *mask = (guint16 *) maskraw;
413 for (i = 0; i < height; i++) {
414 for (j = 0; j < width; j++) {
415 dst[j] = (dst[j] & src[j]) ^ mask[j];
417 dst += dec->format.width;
418 src += dec->cursor.width;
419 mask += dec->cursor.width;
422 guint32 *dst = (guint32 *) dstraw;
423 guint32 *src = (guint32 *) srcraw;
424 guint32 *mask = (guint32 *) maskraw;
426 for (i = 0; i < height; i++) {
427 for (j = 0; j < width; j++) {
428 dst[j] = (dst[j] & src[j]) ^ mask[j];
430 dst += dec->format.width;
431 src += dec->cursor.width;
432 mask += dec->cursor.width;
438 render_cursor (GstVMncDec * dec, guint8 * data)
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;
445 /* Width, height of rendered portion of cursor */
446 int width = dec->cursor.width;
447 int height = dec->cursor.height;
449 /* X,Y offset of rendered portion of cursor */
458 if (x + width > dec->format.width)
459 width = dec->format.width - x;
465 if (y + height > dec->format.height)
466 height = dec->format.height - y;
468 if (dec->cursor.type == CURSOR_COLOUR) {
469 render_colour_cursor (dec, data, x, y, off_x, off_y, width, height);
472 /* TODO: Implement me! */
473 GST_WARNING_OBJECT (dec, "Alpha composited cursors not yet implemented");
478 vmnc_make_buffer (GstVMncDec * dec, GstBuffer * inbuf)
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);
484 memcpy (data, dec->imagedata, size);
486 if (dec->cursor.visible) {
487 render_cursor (dec, data);
491 gst_buffer_copy_metadata (buf, inbuf, GST_BUFFER_COPY_TIMESTAMPS);
494 gst_buffer_set_caps (buf, dec->caps);
500 vmnc_handle_wmvd_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
501 const guint8 * data, int len, gboolean decode)
508 GST_DEBUG_OBJECT (dec, "Cursor data too short");
509 return ERROR_INSUFFICIENT_DATA;
512 type = RFB_GET_UINT8 (data);
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;
519 GST_WARNING_OBJECT (dec, "Unknown cursor type: %d", type);
520 return ERROR_INVALID;
524 GST_DEBUG_OBJECT (dec, "Cursor data too short");
525 return ERROR_INSUFFICIENT_DATA;
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;
536 if (dec->cursor.cursordata)
537 g_free (dec->cursor.cursordata);
538 if (dec->cursor.cursormask)
539 g_free (dec->cursor.cursormask);
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);
548 dec->cursor.cursordata = g_malloc (rect->width * rect->height * 4);
549 memcpy (dec->cursor.cursordata, data + 2, rect->width * rect->height * 4);
556 vmnc_handle_wmve_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
557 const guint8 * data, int len, gboolean decode)
563 GST_DEBUG_OBJECT (dec, "Cursor data too short");
564 return ERROR_INSUFFICIENT_DATA;
568 flags = RFB_GET_UINT16 (data);
569 dec->cursor.visible = flags & 0x01;
575 vmnc_handle_wmvf_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
576 const guint8 * data, int len, gboolean decode)
578 /* Cursor position. */
579 dec->cursor.x = rect->x;
580 dec->cursor.y = rect->y;
585 vmnc_handle_wmvg_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
586 const guint8 * data, int len, gboolean decode)
588 /* Keyboard stuff; not interesting for playback */
590 GST_DEBUG_OBJECT (dec, "Keyboard data too short");
591 return ERROR_INSUFFICIENT_DATA;
597 vmnc_handle_wmvh_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
598 const guint8 * data, int len, gboolean decode)
600 /* More keyboard stuff; not interesting for playback */
602 GST_DEBUG_OBJECT (dec, "Keyboard data too short");
603 return ERROR_INSUFFICIENT_DATA;
609 vmnc_handle_wmvj_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
610 const guint8 * data, int len, gboolean decode)
612 /* VM state info, not interesting for playback */
614 GST_DEBUG_OBJECT (dec, "VM state data too short");
615 return ERROR_INSUFFICIENT_DATA;
621 render_raw_tile (GstVMncDec * dec, const guint8 * data, int x, int y,
622 int width, int height)
630 dst = dec->imagedata + dec->format.stride * y +
631 dec->format.bytes_per_pixel * x;
632 line = width * dec->format.bytes_per_pixel;
634 for (i = 0; i < height; i++) {
635 /* This is wrong-endian currently */
636 memcpy (dst, src, line);
638 dst += dec->format.stride;
644 render_subrect (GstVMncDec * dec, int x, int y, int width,
645 int height, guint32 colour)
647 /* Crazy inefficient! */
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;
662 vmnc_handle_raw_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
663 const guint8 * data, int len, gboolean decode)
665 int datalen = rect->width * rect->height * dec->format.bytes_per_pixel;
668 GST_DEBUG_OBJECT (dec, "Raw data too short");
669 return ERROR_INSUFFICIENT_DATA;
673 render_raw_tile (dec, data, rect->x, rect->y, rect->width, rect->height);
679 vmnc_handle_copy_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
680 const guint8 * data, int len, gboolean decode)
687 GST_DEBUG_OBJECT (dec, "Copy data too short");
688 return ERROR_INSUFFICIENT_DATA;
692 src_x = RFB_GET_UINT16 (data);
693 src_y = RFB_GET_UINT16 (data + 2);
695 /* Our destination rectangle is guaranteed in-frame; check this for the source
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;
703 if (src_y > rect->y || src_x > rect->x) {
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;
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;
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) { \
734 return ERROR_INSUFFICIENT_DATA; \
735 pixel = data[off++]; \
736 } else if (dec->format.bytes_per_pixel == 2) { \
738 return ERROR_INSUFFICIENT_DATA; \
739 pixel = (*(guint16 *)(data + off)); \
743 return ERROR_INSUFFICIENT_DATA; \
744 pixel = (*(guint32 *)(data + off)); \
749 vmnc_handle_hextile_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
750 const guint8 * data, int len, gboolean decode)
752 int tilesx = GST_ROUND_UP_16 (rect->width) / 16;
753 int tilesy = GST_ROUND_UP_16 (rect->height) / 16;
759 guint32 fg = 0, bg = 0, colour;
762 for (y = 0; y < tilesy; y++) {
764 height = rect->height - (tilesy - 1) * 16;
768 for (x = 0; x < tilesx; x++) {
770 width = rect->width - (tilesx - 1) * 16;
775 return ERROR_INSUFFICIENT_DATA;
780 if (off + width * height * dec->format.bytes_per_pixel > len) {
781 return ERROR_INSUFFICIENT_DATA;
784 render_raw_tile (dec, data + off, rect->x + x * 16, rect->y + y * 16,
786 off += width * height * dec->format.bytes_per_pixel;
789 READ_PIXEL (bg, data, off, len)
792 READ_PIXEL (fg, data, off, len)
798 return ERROR_INSUFFICIENT_DATA;
800 subrects = data[off++];
803 /* Paint background colour on entire tile */
805 render_subrect (dec, rect->x + x * 16, rect->y + y * 16,
808 coloured = flags & 0x10;
809 for (z = 0; z < subrects; z++) {
811 READ_PIXEL (colour, data, off, len);
815 return ERROR_INSUFFICIENT_DATA;
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;
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;
833 render_subrect (dec, rect->x + x * 16 + off_x,
834 rect->y + y * 16 + off_y, w, h, colour);
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
848 * Returns: >= 0, the number of bytes consumed
849 * < 0, packet too short to decode, or error
852 vmnc_handle_packet (GstVMncDec * dec, const guint8 * data, int len,
859 GST_DEBUG_OBJECT (dec, "Packet too short");
860 return ERROR_INSUFFICIENT_DATA;
868 int numrect = RFB_GET_UINT16 (data + 2);
874 for (i = 0; i < numrect; i++) {
875 struct RfbRectangle r;
876 rectangle_handler handler;
878 if (len < offset + 12) {
879 GST_DEBUG_OBJECT (dec,
880 "Packet too short for rectangle header: %d < %d",
882 return ERROR_INSUFFICIENT_DATA;
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);
891 if (r.type != TYPE_WMVi) {
892 /* We must have a WMVi packet to initialise things before we can
894 if (!dec->have_format) {
895 GST_WARNING_OBJECT (dec, "Received packet without WMVi: %d",
897 return ERROR_INVALID;
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;
908 handler = vmnc_handle_wmvd_rectangle;
911 handler = vmnc_handle_wmve_rectangle;
914 handler = vmnc_handle_wmvf_rectangle;
917 handler = vmnc_handle_wmvg_rectangle;
920 handler = vmnc_handle_wmvh_rectangle;
923 handler = vmnc_handle_wmvi_rectangle;
926 handler = vmnc_handle_wmvj_rectangle;
929 handler = vmnc_handle_raw_rectangle;
932 handler = vmnc_handle_copy_rectangle;
935 handler = vmnc_handle_hextile_rectangle;
938 GST_WARNING_OBJECT (dec, "Unknown rectangle type");
939 return ERROR_INVALID;
942 read = handler (dec, &r, data + offset + 12, len - offset - 12, decode);
944 GST_DEBUG_OBJECT (dec, "Error calling rectangle handler\n");
952 GST_WARNING_OBJECT (dec, "Packet type unknown: %d", type);
953 return ERROR_INVALID;
960 vmnc_dec_setcaps (GstPad * pad, GstCaps * caps)
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));
966 if (gst_caps_get_size (caps) > 0) {
967 GstStructure *structure = gst_caps_get_structure (caps, 0);
969 /* We gave these a default in reset(), so we don't need to check for failure
971 gst_structure_get_fraction (structure, "framerate",
972 &dec->framerate_num, &dec->framerate_denom);
976 GST_DEBUG_OBJECT (dec, "Unparsed input");
980 gst_object_unref (dec);
986 vmnc_dec_chain_frame (GstVMncDec * dec, GstBuffer * inbuf,
987 const guint8 * data, int len)
990 GstFlowReturn ret = GST_FLOW_OK;
993 res = vmnc_handle_packet (dec, data, len, TRUE);
996 ret = GST_FLOW_ERROR;
997 GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Couldn't decode packet"));
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);
1008 static GstFlowReturn
1009 vmnc_dec_chain (GstPad * pad, GstBuffer * buf)
1012 GstFlowReturn ret = GST_FLOW_OK;
1014 dec = GST_VMNC_DEC (gst_pad_get_parent (pad));
1017 /* Submit our input buffer to adapter, then parse. Push each frame found
1018 * through the decoder
1024 gst_adapter_push (dec->adapter, buf);
1027 avail = gst_adapter_available (dec->adapter);
1028 data = gst_adapter_peek (dec->adapter, avail);
1030 GST_DEBUG_OBJECT (dec, "Parsing %d bytes", avail);
1033 int len = vmnc_handle_packet (dec, data, avail, FALSE);
1035 if (len == ERROR_INSUFFICIENT_DATA) {
1036 GST_DEBUG_OBJECT (dec, "Not enough data yet");
1039 } else if (len < 0) {
1040 GST_DEBUG_OBJECT (dec, "Fatal error in bitstream");
1041 ret = GST_FLOW_ERROR;
1045 GST_DEBUG_OBJECT (dec, "Parsed packet: %d bytes", len);
1047 ret = vmnc_dec_chain_frame (dec, NULL, data, len);
1053 if (ret != GST_FLOW_OK)
1056 GST_DEBUG_OBJECT (dec, "Flushing %d bytes", read);
1058 gst_adapter_flush (dec->adapter, read);
1060 ret = vmnc_dec_chain_frame (dec, buf, GST_BUFFER_DATA (buf),
1061 GST_BUFFER_SIZE (buf));
1062 gst_buffer_unref (buf);
1065 gst_object_unref (dec);
1070 static GstStateChangeReturn
1071 vmnc_dec_change_state (GstElement * element, GstStateChange transition)
1073 GstVMncDec *dec = GST_VMNC_DEC (element);
1074 GstStateChangeReturn ret;
1076 switch (transition) {
1077 case GST_STATE_CHANGE_NULL_TO_READY:
1079 case GST_STATE_CHANGE_READY_TO_PAUSED:
1080 gst_vmnc_dec_reset (dec);
1082 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1088 ret = parent_class->change_state (element, transition);
1090 switch (transition) {
1091 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1093 case GST_STATE_CHANGE_PAUSED_TO_READY:
1094 gst_vmnc_dec_reset (dec);
1096 case GST_STATE_CHANGE_READY_TO_NULL:
1106 vmnc_dec_set_property (GObject * object, guint prop_id,
1107 const GValue * value, GParamSpec * pspec)
1109 /*GstVMncDec *dec = GST_VMNC_DEC (object); */
1113 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1119 vmnc_dec_get_property (GObject * object, guint prop_id,
1120 GValue * value, GParamSpec * pspec)
1122 /*GstVMncDec *dec = GST_VMNC_DEC (object); */
1126 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1132 plugin_init (GstPlugin * plugin)
1134 if (!gst_element_register (plugin, "vmncdec", GST_RANK_PRIMARY,
1135 gst_vmnc_dec_get_type ()))
1140 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1143 "VmWare Video Codec plugins",
1144 plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)