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., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, 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
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,
47 #define GST_CAT_DEFAULT vmnc_debug
48 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
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)
61 ERROR_INVALID = -1, /* Invalid data in bitstream */
62 ERROR_INSUFFICIENT_DATA = -2 /* Haven't received enough data yet */
65 static GstStaticPadTemplate vmnc_dec_src_factory =
66 GST_STATIC_PAD_TEMPLATE ("src",
69 GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ RGBx, BGRx, xRGB, xBGR,"
70 " RGB15, BGR15, RGB16, BGR16, GRAY8 }")));
72 static GstStaticPadTemplate vmnc_dec_sink_factory =
73 GST_STATIC_PAD_TEMPLATE ("sink",
76 GST_STATIC_CAPS ("video/x-vmnc, version=(int)1, "
77 "framerate=(fraction)[0, max], "
78 "width=(int)[0, max], " "height=(int)[0, max]")
81 G_DEFINE_TYPE (GstVMncDec, gst_vmnc_dec, GST_TYPE_VIDEO_DECODER);
84 gst_vmnc_dec_class_init (GstVMncDecClass * klass)
86 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
87 GstVideoDecoderClass *decoder_class = GST_VIDEO_DECODER_CLASS (klass);
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;
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>");
104 GST_DEBUG_CATEGORY_INIT (vmnc_debug, "vmncdec", 0, "VMnc decoder");
108 gst_vmnc_dec_init (GstVMncDec * dec)
110 gst_video_decoder_set_use_default_pad_acceptcaps (GST_VIDEO_DECODER_CAST
112 GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_DECODER_SINK_PAD (dec));
116 gst_vmnc_dec_reset (GstVideoDecoder * decoder)
118 GstVMncDec *dec = GST_VMNC_DEC (decoder);
120 g_free (dec->imagedata);
121 dec->imagedata = NULL;
123 g_free (dec->cursor.cursordata);
124 dec->cursor.cursordata = NULL;
126 g_free (dec->cursor.cursormask);
127 dec->cursor.cursormask = NULL;
129 dec->cursor.visible = 0;
131 dec->have_format = FALSE;
133 if (dec->input_state)
134 gst_video_codec_state_unref (dec->input_state);
135 dec->input_state = NULL;
150 /* Rectangle handling functions.
151 * Return number of bytes consumed, or < 0 on error
153 typedef int (*rectangle_handler) (GstVMncDec * dec, struct RfbRectangle * rect,
154 const guint8 * data, int len, gboolean decode);
157 vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
158 const guint8 * data, int len, gboolean decode)
160 GstVideoFormat format;
162 guint32 redmask, greenmask, bluemask;
163 guint32 endianness, dataendianness;
164 GstVideoCodecState *state;
166 /* A WMVi rectangle has a 16byte payload */
168 GST_DEBUG_OBJECT (dec, "Bad WMVi rect: too short");
169 return ERROR_INSUFFICIENT_DATA;
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 */
178 /* Store the whole block for simple comparison later */
179 memcpy (dec->format.descriptor, data, 16);
181 if (rect->x != 0 || rect->y != 0) {
182 GST_WARNING_OBJECT (dec, "Bad WMVi rect: wrong coordinates");
183 return ERROR_INVALID;
187 dec->format.depth = data[1];
188 dec->format.big_endian = data[2];
189 dataendianness = data[2] ? G_BIG_ENDIAN : G_LITTLE_ENDIAN;
192 if (bpp != 8 && bpp != 16 && bpp != 32) {
193 GST_WARNING_OBJECT (dec, "Bad bpp value: %d", bpp);
194 return ERROR_INVALID;
198 GST_WARNING_OBJECT (dec, "Paletted video not supported");
199 return ERROR_INVALID;
202 dec->format.bytes_per_pixel = bpp / 8;
203 dec->format.width = rect->width;
204 dec->format.height = rect->height;
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];
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");
219 /* GStreamer's RGB caps are a bit weird. */
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);
235 format = gst_video_format_from_masks (dec->format.depth, bpp, endianness,
236 redmask, greenmask, bluemask, 0);
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));
246 if (format == GST_VIDEO_FORMAT_UNKNOWN) {
247 GST_WARNING_OBJECT (dec, "Video format unknown to GStreamer");
248 return ERROR_INVALID;
251 dec->have_format = TRUE;
253 GST_LOG_OBJECT (dec, "Parsing, not setting caps");
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);
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);
267 dec->format.stride = dec->format.width * dec->format.bytes_per_pixel;
273 render_colour_cursor (GstVMncDec * dec, guint8 * data, int x, int y,
274 int off_x, int off_y, int width, int height)
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;
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;
290 for (i = 0; i < height; i++) {
291 for (j = 0; j < width; j++) {
292 dst[j] = (dst[j] & src[j]) ^ mask[j];
294 dst += dec->format.width;
295 src += dec->cursor.width;
296 mask += dec->cursor.width;
298 } else if (dec->format.bytes_per_pixel == 2) {
299 guint16 *dst = (guint16 *) dstraw;
300 guint16 *src = (guint16 *) srcraw;
301 guint16 *mask = (guint16 *) maskraw;
303 for (i = 0; i < height; i++) {
304 for (j = 0; j < width; j++) {
305 dst[j] = (dst[j] & src[j]) ^ mask[j];
307 dst += dec->format.width;
308 src += dec->cursor.width;
309 mask += dec->cursor.width;
312 guint32 *dst = (guint32 *) dstraw;
313 guint32 *src = (guint32 *) srcraw;
314 guint32 *mask = (guint32 *) maskraw;
316 for (i = 0; i < height; i++) {
317 for (j = 0; j < width; j++) {
318 dst[j] = (dst[j] & src[j]) ^ mask[j];
320 dst += dec->format.width;
321 src += dec->cursor.width;
322 mask += dec->cursor.width;
328 render_cursor (GstVMncDec * dec, guint8 * data)
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;
335 /* Width, height of rendered portion of cursor */
336 int width = dec->cursor.width;
337 int height = dec->cursor.height;
339 /* X,Y offset of rendered portion of cursor */
348 if (x + width > dec->format.width)
349 width = dec->format.width - x;
355 if (y + height > dec->format.height)
356 height = dec->format.height - y;
358 if (dec->cursor.type == CURSOR_COLOUR) {
359 render_colour_cursor (dec, data, x, y, off_x, off_y, width, height);
362 /* TODO: Implement me! */
363 GST_WARNING_OBJECT (dec, "Alpha composited cursors not yet implemented");
368 vmnc_fill_buffer (GstVMncDec * dec, GstVideoCodecFrame * frame)
374 gst_video_decoder_allocate_output_frame (GST_VIDEO_DECODER (dec), frame);
375 if (ret != GST_FLOW_OK)
378 gst_buffer_map (frame->output_buffer, &map, GST_MAP_READWRITE);
380 memcpy (map.data, dec->imagedata, map.size);
382 if (dec->cursor.visible)
383 render_cursor (dec, map.data);
385 gst_buffer_unmap (frame->output_buffer, &map);
391 vmnc_handle_wmvd_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
392 const guint8 * data, int len, gboolean decode)
399 GST_LOG_OBJECT (dec, "Cursor data too short");
400 return ERROR_INSUFFICIENT_DATA;
403 type = RFB_GET_UINT8 (data);
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;
410 GST_WARNING_OBJECT (dec, "Unknown cursor type: %d", type);
411 return ERROR_INVALID;
415 GST_LOG_OBJECT (dec, "Cursor data too short");
416 return ERROR_INSUFFICIENT_DATA;
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;
427 g_free (dec->cursor.cursordata);
428 g_free (dec->cursor.cursormask);
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);
437 dec->cursor.cursordata = g_malloc (rect->width * rect->height * 4);
438 memcpy (dec->cursor.cursordata, data + 2, rect->width * rect->height * 4);
445 vmnc_handle_wmve_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
446 const guint8 * data, int len, gboolean decode)
452 GST_LOG_OBJECT (dec, "Cursor data too short");
453 return ERROR_INSUFFICIENT_DATA;
457 flags = RFB_GET_UINT16 (data);
458 dec->cursor.visible = flags & 0x01;
464 vmnc_handle_wmvf_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
465 const guint8 * data, int len, gboolean decode)
467 /* Cursor position. */
468 dec->cursor.x = rect->x;
469 dec->cursor.y = rect->y;
474 vmnc_handle_wmvg_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
475 const guint8 * data, int len, gboolean decode)
477 /* Keyboard stuff; not interesting for playback */
479 GST_LOG_OBJECT (dec, "Keyboard data too short");
480 return ERROR_INSUFFICIENT_DATA;
486 vmnc_handle_wmvh_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
487 const guint8 * data, int len, gboolean decode)
489 /* More keyboard stuff; not interesting for playback */
491 GST_LOG_OBJECT (dec, "Keyboard data too short");
492 return ERROR_INSUFFICIENT_DATA;
498 vmnc_handle_wmvj_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
499 const guint8 * data, int len, gboolean decode)
501 /* VM state info, not interesting for playback */
503 GST_LOG_OBJECT (dec, "VM state data too short");
504 return ERROR_INSUFFICIENT_DATA;
510 render_raw_tile (GstVMncDec * dec, const guint8 * data, int x, int y,
511 int width, int height)
519 dst = dec->imagedata + dec->format.stride * y +
520 dec->format.bytes_per_pixel * x;
521 line = width * dec->format.bytes_per_pixel;
523 for (i = 0; i < height; i++) {
524 /* This is wrong-endian currently */
525 memcpy (dst, src, line);
527 dst += dec->format.stride;
533 render_subrect (GstVMncDec * dec, int x, int y, int width,
534 int height, guint32 colour)
536 /* Crazy inefficient! */
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;
551 vmnc_handle_raw_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
552 const guint8 * data, int len, gboolean decode)
554 int datalen = rect->width * rect->height * dec->format.bytes_per_pixel;
557 GST_LOG_OBJECT (dec, "Raw data too short");
558 return ERROR_INSUFFICIENT_DATA;
562 render_raw_tile (dec, data, rect->x, rect->y, rect->width, rect->height);
568 vmnc_handle_copy_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
569 const guint8 * data, int len, gboolean decode)
576 GST_LOG_OBJECT (dec, "Copy data too short");
577 return ERROR_INSUFFICIENT_DATA;
581 src_x = RFB_GET_UINT16 (data);
582 src_y = RFB_GET_UINT16 (data + 2);
584 /* Our destination rectangle is guaranteed in-frame; check this for the source
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;
592 if (src_y > rect->y || src_x > rect->x) {
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;
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;
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) { \
623 return ERROR_INSUFFICIENT_DATA; \
624 pixel = data[off++]; \
625 } else if (dec->format.bytes_per_pixel == 2) { \
627 return ERROR_INSUFFICIENT_DATA; \
628 pixel = (*(guint16 *)(data + off)); \
632 return ERROR_INSUFFICIENT_DATA; \
633 pixel = (*(guint32 *)(data + off)); \
638 vmnc_handle_hextile_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
639 const guint8 * data, int len, gboolean decode)
641 int tilesx = GST_ROUND_UP_16 (rect->width) / 16;
642 int tilesy = GST_ROUND_UP_16 (rect->height) / 16;
648 guint32 fg = 0, bg = 0, colour;
651 for (y = 0; y < tilesy; y++) {
653 height = rect->height - (tilesy - 1) * 16;
657 for (x = 0; x < tilesx; x++) {
659 width = rect->width - (tilesx - 1) * 16;
664 return ERROR_INSUFFICIENT_DATA;
669 if (off + width * height * dec->format.bytes_per_pixel > len) {
670 return ERROR_INSUFFICIENT_DATA;
673 render_raw_tile (dec, data + off, rect->x + x * 16, rect->y + y * 16,
675 off += width * height * dec->format.bytes_per_pixel;
678 READ_PIXEL (bg, data, off, len)
681 READ_PIXEL (fg, data, off, len)
687 return ERROR_INSUFFICIENT_DATA;
689 subrects = data[off++];
692 /* Paint background colour on entire tile */
694 render_subrect (dec, rect->x + x * 16, rect->y + y * 16,
697 coloured = flags & 0x10;
698 for (z = 0; z < subrects; z++) {
700 READ_PIXEL (colour, data, off, len);
704 return ERROR_INSUFFICIENT_DATA;
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;
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;
722 render_subrect (dec, rect->x + x * 16 + off_x,
723 rect->y + y * 16 + off_y, w, h, colour);
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
737 * Returns: >= 0, the number of bytes consumed
738 * < 0, packet too short to decode, or error
741 vmnc_handle_packet (GstVMncDec * dec, const guint8 * data, int len,
748 GST_LOG_OBJECT (dec, "Packet too short");
749 return ERROR_INSUFFICIENT_DATA;
757 int numrect = RFB_GET_UINT16 (data + 2);
763 for (i = 0; i < numrect; i++) {
764 struct RfbRectangle r;
765 rectangle_handler handler;
767 if (len < offset + 12) {
769 "Packet too short for rectangle header: %d < %d",
771 return ERROR_INSUFFICIENT_DATA;
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);
780 if (r.type != TYPE_WMVi) {
781 /* We must have a WMVi packet to initialise things before we can
783 if (!dec->have_format) {
784 GST_WARNING_OBJECT (dec, "Received packet without WMVi: %d",
786 return ERROR_INVALID;
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;
794 } else if (r.width > 16384 || r.height > 16384) {
795 GST_WARNING_OBJECT (dec, "Width or height too high: %ux%u", r.width,
797 return ERROR_INVALID;
802 handler = vmnc_handle_wmvd_rectangle;
805 handler = vmnc_handle_wmve_rectangle;
808 handler = vmnc_handle_wmvf_rectangle;
811 handler = vmnc_handle_wmvg_rectangle;
814 handler = vmnc_handle_wmvh_rectangle;
817 handler = vmnc_handle_wmvi_rectangle;
820 handler = vmnc_handle_wmvj_rectangle;
823 handler = vmnc_handle_raw_rectangle;
826 handler = vmnc_handle_copy_rectangle;
829 handler = vmnc_handle_hextile_rectangle;
832 GST_WARNING_OBJECT (dec, "Unknown rectangle type");
833 return ERROR_INVALID;
836 read = handler (dec, &r, data + offset + 12, len - offset - 12, decode);
838 GST_DEBUG_OBJECT (dec, "Error calling rectangle handler\n");
846 GST_WARNING_OBJECT (dec, "Packet type unknown: %d", type);
847 return ERROR_INVALID;
854 gst_vmnc_dec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state)
856 GstVMncDec *dec = GST_VMNC_DEC (decoder);
858 /* We require a format descriptor in-stream, so we ignore the info from the
859 * container here. We just use the framerate */
861 if (dec->input_state)
862 gst_video_codec_state_unref (dec->input_state);
863 dec->input_state = gst_video_codec_state_ref (state);
869 gst_vmnc_dec_sink_event (GstVideoDecoder * bdec, GstEvent * event)
871 const GstSegment *segment;
873 if (GST_EVENT_TYPE (event) != GST_EVENT_SEGMENT)
876 gst_event_parse_segment (event, &segment);
878 if (segment->format == GST_FORMAT_TIME)
879 gst_video_decoder_set_packetized (bdec, TRUE);
881 gst_video_decoder_set_packetized (bdec, FALSE);
884 return GST_VIDEO_DECODER_CLASS (gst_vmnc_dec_parent_class)->sink_event (bdec,
889 gst_vmnc_dec_handle_frame (GstVideoDecoder * decoder,
890 GstVideoCodecFrame * frame)
892 GstVMncDec *dec = GST_VMNC_DEC (decoder);
894 GstFlowReturn ret = GST_FLOW_OK;
897 if (!gst_buffer_map (frame->input_buffer, &map, GST_MAP_READ))
898 return GST_FLOW_ERROR;
900 res = vmnc_handle_packet (dec, map.data, map.size, TRUE);
902 gst_buffer_unmap (frame->input_buffer, &map);
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);
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);
921 gst_video_decoder_drop_frame (decoder, frame);
929 gst_vmnc_dec_parse (GstVideoDecoder * decoder, GstVideoCodecFrame * frame,
930 GstAdapter * adapter, gboolean at_eos)
932 GstVMncDec *dec = GST_VMNC_DEC (decoder);
937 avail = gst_adapter_available (adapter);
938 data = gst_adapter_map (adapter, avail);
940 GST_LOG_OBJECT (dec, "Parsing %d bytes", avail);
942 len = vmnc_handle_packet (dec, data, avail, FALSE);
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;
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);
958 plugin_init (GstPlugin * plugin)
960 if (!gst_element_register (plugin, "vmncdec", GST_RANK_PRIMARY,
966 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
969 "VmWare Video Codec plugins",
970 plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)