mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-04-23 09:04:15 +00:00
gst/vmnc/vmncdec.c: Redesign to include a parser for raw files (no timestamps in that mode yet, though).
Original commit message from CVS: * gst/vmnc/vmncdec.c: (gst_vmnc_dec_class_init), (gst_vmnc_dec_init), (vmnc_dec_finalize), (gst_vmnc_dec_reset), (vmnc_handle_wmvi_rectangle), (render_colour_cursor), (render_cursor), (vmnc_make_buffer), (vmnc_handle_wmvd_rectangle), (vmnc_handle_wmve_rectangle), (vmnc_handle_wmvf_rectangle), (vmnc_handle_wmvg_rectangle), (vmnc_handle_wmvh_rectangle), (vmnc_handle_wmvj_rectangle), (render_raw_tile), (render_subrect), (vmnc_handle_raw_rectangle), (vmnc_handle_copy_rectangle), (vmnc_handle_hextile_rectangle), (vmnc_handle_packet), (vmnc_dec_setcaps), (vmnc_dec_chain_frame), (vmnc_dec_chain), (vmnc_dec_set_property), (vmnc_dec_get_property): Redesign to include a parser for raw files (no timestamps in that mode yet, though).
This commit is contained in:
parent
63881aeec0
commit
628d19135f
3 changed files with 288 additions and 87 deletions
16
ChangeLog
16
ChangeLog
|
@ -1,3 +1,19 @@
|
||||||
|
2007-03-23 Michael Smith <msmith@fluendo.com>
|
||||||
|
|
||||||
|
* gst/vmnc/vmncdec.c: (gst_vmnc_dec_class_init),
|
||||||
|
(gst_vmnc_dec_init), (vmnc_dec_finalize), (gst_vmnc_dec_reset),
|
||||||
|
(vmnc_handle_wmvi_rectangle), (render_colour_cursor),
|
||||||
|
(render_cursor), (vmnc_make_buffer), (vmnc_handle_wmvd_rectangle),
|
||||||
|
(vmnc_handle_wmve_rectangle), (vmnc_handle_wmvf_rectangle),
|
||||||
|
(vmnc_handle_wmvg_rectangle), (vmnc_handle_wmvh_rectangle),
|
||||||
|
(vmnc_handle_wmvj_rectangle), (render_raw_tile), (render_subrect),
|
||||||
|
(vmnc_handle_raw_rectangle), (vmnc_handle_copy_rectangle),
|
||||||
|
(vmnc_handle_hextile_rectangle), (vmnc_handle_packet),
|
||||||
|
(vmnc_dec_setcaps), (vmnc_dec_chain_frame), (vmnc_dec_chain),
|
||||||
|
(vmnc_dec_set_property), (vmnc_dec_get_property):
|
||||||
|
Redesign to include a parser for raw files (no timestamps in that
|
||||||
|
mode yet, though).
|
||||||
|
|
||||||
2007-03-22 Tim-Philipp Müller <tim at centricular dot net>
|
2007-03-22 Tim-Philipp Müller <tim at centricular dot net>
|
||||||
|
|
||||||
* gst/interleave/deinterleave.c: (gst_deinterleave_add_new_pads),
|
* gst/interleave/deinterleave.c: (gst_deinterleave_add_new_pads),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
plugin_LTLIBRARIES = libgstvmnc.la
|
plugin_LTLIBRARIES = libgstvmnc.la
|
||||||
|
|
||||||
libgstvmnc_la_SOURCES = vmncdec.c
|
libgstvmnc_la_SOURCES = vmncdec.c
|
||||||
libgstvmnc_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(THEORA_CFLAGS)
|
libgstvmnc_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(THEORA_CFLAGS)
|
||||||
libgstvmnc_la_LIBADD = $(GST_LIBS)
|
libgstvmnc_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS)
|
||||||
libgstvmnc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
libgstvmnc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
|
#include <gst/base/gstadapter.h>
|
||||||
#include <gst/video/video.h>
|
#include <gst/video/video.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -51,6 +52,12 @@ enum
|
||||||
ARG_0,
|
ARG_0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
ERROR_INVALID = -1, /* Invalid data in bitstream */
|
||||||
|
ERROR_INSUFFICIENT_DATA = -2 /* Haven't received enough data yet */
|
||||||
|
};
|
||||||
|
|
||||||
#define MAKE_TYPE(a,b,c,d) ((a<<24)|(b<<16)|(c<<8)|d)
|
#define MAKE_TYPE(a,b,c,d) ((a<<24)|(b<<16)|(c<<8)|d)
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -109,6 +116,10 @@ typedef struct
|
||||||
GstPad *srcpad;
|
GstPad *srcpad;
|
||||||
|
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
|
gboolean have_format;
|
||||||
|
|
||||||
|
int parsed;
|
||||||
|
GstAdapter *adapter;
|
||||||
|
|
||||||
int framerate_num;
|
int framerate_num;
|
||||||
int framerate_denom;
|
int framerate_denom;
|
||||||
|
@ -155,6 +166,7 @@ static GstFlowReturn vmnc_dec_chain (GstPad * pad, GstBuffer * buffer);
|
||||||
static gboolean vmnc_dec_setcaps (GstPad * pad, GstCaps * caps);
|
static gboolean vmnc_dec_setcaps (GstPad * pad, GstCaps * caps);
|
||||||
static GstStateChangeReturn vmnc_dec_change_state (GstElement * element,
|
static GstStateChangeReturn vmnc_dec_change_state (GstElement * element,
|
||||||
GstStateChange transition);
|
GstStateChange transition);
|
||||||
|
static void vmnc_dec_finalize (GObject * object);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_vmnc_dec_base_init (gpointer g_class)
|
gst_vmnc_dec_base_init (gpointer g_class)
|
||||||
|
@ -177,6 +189,8 @@ gst_vmnc_dec_class_init (GstVMncDecClass * klass)
|
||||||
gobject_class->set_property = vmnc_dec_set_property;
|
gobject_class->set_property = vmnc_dec_set_property;
|
||||||
gobject_class->get_property = vmnc_dec_get_property;
|
gobject_class->get_property = vmnc_dec_get_property;
|
||||||
|
|
||||||
|
gobject_class->finalize = vmnc_dec_finalize;
|
||||||
|
|
||||||
gstelement_class->change_state = vmnc_dec_change_state;
|
gstelement_class->change_state = vmnc_dec_change_state;
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (vmnc_debug, "vmncdec", 0, "VMnc decoder");
|
GST_DEBUG_CATEGORY_INIT (vmnc_debug, "vmncdec", 0, "VMnc decoder");
|
||||||
|
@ -195,6 +209,16 @@ gst_vmnc_dec_init (GstVMncDec * dec, GstVMncDecClass * g_class)
|
||||||
gst_pad_use_fixed_caps (dec->srcpad);
|
gst_pad_use_fixed_caps (dec->srcpad);
|
||||||
|
|
||||||
gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
|
gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
|
||||||
|
|
||||||
|
dec->adapter = gst_adapter_new ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vmnc_dec_finalize (GObject * object)
|
||||||
|
{
|
||||||
|
GstVMncDec *dec = GST_VMNC_DEC (object);
|
||||||
|
|
||||||
|
g_object_unref (dec->adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -222,6 +246,10 @@ gst_vmnc_dec_reset (GstVMncDec * dec)
|
||||||
/* Use these as defaults if the container doesn't provide anything */
|
/* Use these as defaults if the container doesn't provide anything */
|
||||||
dec->framerate_num = 5;
|
dec->framerate_num = 5;
|
||||||
dec->framerate_denom = 1;
|
dec->framerate_denom = 1;
|
||||||
|
|
||||||
|
dec->have_format = FALSE;
|
||||||
|
|
||||||
|
gst_adapter_clear (dec->adapter);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RfbRectangle
|
struct RfbRectangle
|
||||||
|
@ -238,11 +266,11 @@ struct RfbRectangle
|
||||||
* Return number of bytes consumed, or < 0 on error
|
* Return number of bytes consumed, or < 0 on error
|
||||||
*/
|
*/
|
||||||
typedef int (*rectangle_handler) (GstVMncDec * dec, struct RfbRectangle * rect,
|
typedef int (*rectangle_handler) (GstVMncDec * dec, struct RfbRectangle * rect,
|
||||||
guint8 * data, int len);
|
const guint8 * data, int len, gboolean decode);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
guint8 * data, int len)
|
const guint8 * data, int len, gboolean decode)
|
||||||
{
|
{
|
||||||
GstCaps *caps;
|
GstCaps *caps;
|
||||||
gint bpp, tc;
|
gint bpp, tc;
|
||||||
|
@ -251,12 +279,12 @@ vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
|
|
||||||
/* A WMVi rectangle has a 16byte payload */
|
/* A WMVi rectangle has a 16byte payload */
|
||||||
if (len < 16) {
|
if (len < 16) {
|
||||||
GST_WARNING_OBJECT (dec, "Bad WMVi rect: too short");
|
GST_DEBUG_OBJECT (dec, "Bad WMVi rect: too short");
|
||||||
return -1;
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We only compare 13 bytes; ignoring the 3 padding bytes at the end */
|
/* We only compare 13 bytes; ignoring the 3 padding bytes at the end */
|
||||||
if (memcmp (data, dec->format.descriptor, 13) == 0) {
|
if (dec->caps && memcmp (data, dec->format.descriptor, 13) == 0) {
|
||||||
/* Nothing changed, so just exit */
|
/* Nothing changed, so just exit */
|
||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
|
@ -266,7 +294,7 @@ vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
|
|
||||||
if (rect->x != 0 || rect->y != 0) {
|
if (rect->x != 0 || rect->y != 0) {
|
||||||
GST_WARNING_OBJECT (dec, "Bad WMVi rect: wrong coordinates");
|
GST_WARNING_OBJECT (dec, "Bad WMVi rect: wrong coordinates");
|
||||||
return -1;
|
return ERROR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
bpp = data[0];
|
bpp = data[0];
|
||||||
|
@ -277,12 +305,12 @@ vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
|
|
||||||
if (bpp != 8 && bpp != 16 && bpp != 32) {
|
if (bpp != 8 && bpp != 16 && bpp != 32) {
|
||||||
GST_WARNING_OBJECT (dec, "Bad bpp value: %d", bpp);
|
GST_WARNING_OBJECT (dec, "Bad bpp value: %d", bpp);
|
||||||
return -1;
|
return ERROR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tc) {
|
if (!tc) {
|
||||||
GST_WARNING_OBJECT (dec, "Paletted video not supported");
|
GST_WARNING_OBJECT (dec, "Paletted video not supported");
|
||||||
return -1;
|
return ERROR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
dec->format.bytes_per_pixel = bpp / 8;
|
dec->format.bytes_per_pixel = bpp / 8;
|
||||||
|
@ -318,6 +346,12 @@ vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dec->have_format = TRUE;
|
||||||
|
if (!decode) {
|
||||||
|
GST_DEBUG_OBJECT (dec, "Parsing, not setting caps");
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
caps = gst_caps_new_simple ("video/x-raw-rgb",
|
caps = gst_caps_new_simple ("video/x-raw-rgb",
|
||||||
"framerate", GST_TYPE_FRACTION, dec->framerate_num, dec->framerate_denom,
|
"framerate", GST_TYPE_FRACTION, dec->framerate_num, dec->framerate_denom,
|
||||||
"pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
|
"pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1,
|
||||||
|
@ -338,6 +372,7 @@ vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
g_free (dec->imagedata);
|
g_free (dec->imagedata);
|
||||||
dec->imagedata = g_malloc (dec->format.width * dec->format.height *
|
dec->imagedata = g_malloc (dec->format.width * dec->format.height *
|
||||||
dec->format.bytes_per_pixel);
|
dec->format.bytes_per_pixel);
|
||||||
|
GST_DEBUG_OBJECT (dec, "Allocated image data at %p", dec->imagedata);
|
||||||
|
|
||||||
dec->format.stride = dec->format.width * dec->format.bytes_per_pixel;
|
dec->format.stride = dec->format.width * dec->format.bytes_per_pixel;
|
||||||
|
|
||||||
|
@ -452,7 +487,9 @@ vmnc_make_buffer (GstVMncDec * dec, GstBuffer * inbuf)
|
||||||
render_cursor (dec, data);
|
render_cursor (dec, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_buffer_stamp (buf, inbuf);
|
if (inbuf) {
|
||||||
|
gst_buffer_stamp (buf, inbuf);
|
||||||
|
}
|
||||||
|
|
||||||
gst_buffer_set_caps (buf, dec->caps);
|
gst_buffer_set_caps (buf, dec->caps);
|
||||||
|
|
||||||
|
@ -461,15 +498,15 @@ vmnc_make_buffer (GstVMncDec * dec, GstBuffer * inbuf)
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vmnc_handle_wmvd_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
vmnc_handle_wmvd_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
guint8 * data, int len)
|
const guint8 * data, int len, gboolean decode)
|
||||||
{
|
{
|
||||||
/* Cursor data. */
|
/* Cursor data. */
|
||||||
int datalen = 2;
|
int datalen = 2;
|
||||||
int type, size;
|
int type, size;
|
||||||
|
|
||||||
if (len < datalen) {
|
if (len < datalen) {
|
||||||
GST_WARNING_OBJECT (dec, "Cursor data too short");
|
GST_DEBUG_OBJECT (dec, "Cursor data too short");
|
||||||
return -1;
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
type = RFB_GET_UINT8 (data);
|
type = RFB_GET_UINT8 (data);
|
||||||
|
@ -480,13 +517,14 @@ vmnc_handle_wmvd_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
datalen += rect->width * rect->height * 4;
|
datalen += rect->width * rect->height * 4;
|
||||||
} else {
|
} else {
|
||||||
GST_WARNING_OBJECT (dec, "Unknown cursor type: %d", type);
|
GST_WARNING_OBJECT (dec, "Unknown cursor type: %d", type);
|
||||||
return -1;
|
return ERROR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len < datalen) {
|
if (len < datalen) {
|
||||||
GST_WARNING_OBJECT (dec, "Cursor data too short");
|
GST_DEBUG_OBJECT (dec, "Cursor data too short");
|
||||||
return -1;
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
}
|
} else if (!decode)
|
||||||
|
return datalen;
|
||||||
|
|
||||||
dec->cursor.type = type;
|
dec->cursor.type = type;
|
||||||
dec->cursor.width = rect->width;
|
dec->cursor.width = rect->width;
|
||||||
|
@ -516,15 +554,16 @@ vmnc_handle_wmvd_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vmnc_handle_wmve_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
vmnc_handle_wmve_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
guint8 * data, int len)
|
const guint8 * data, int len, gboolean decode)
|
||||||
{
|
{
|
||||||
guint16 flags;
|
guint16 flags;
|
||||||
|
|
||||||
/* Cursor state. */
|
/* Cursor state. */
|
||||||
if (len < 2) {
|
if (len < 2) {
|
||||||
GST_WARNING_OBJECT (dec, "Cursor data too short");
|
GST_DEBUG_OBJECT (dec, "Cursor data too short");
|
||||||
return -1;
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
}
|
} else if (!decode)
|
||||||
|
return 2;
|
||||||
|
|
||||||
flags = RFB_GET_UINT16 (data);
|
flags = RFB_GET_UINT16 (data);
|
||||||
dec->cursor.visible = flags & 0x01;
|
dec->cursor.visible = flags & 0x01;
|
||||||
|
@ -534,7 +573,7 @@ vmnc_handle_wmve_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vmnc_handle_wmvf_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
vmnc_handle_wmvf_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
guint8 * data, int len)
|
const guint8 * data, int len, gboolean decode)
|
||||||
{
|
{
|
||||||
/* Cursor position. */
|
/* Cursor position. */
|
||||||
dec->cursor.x = rect->x;
|
dec->cursor.x = rect->x;
|
||||||
|
@ -544,46 +583,47 @@ vmnc_handle_wmvf_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vmnc_handle_wmvg_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
vmnc_handle_wmvg_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
guint8 * data, int len)
|
const guint8 * data, int len, gboolean decode)
|
||||||
{
|
{
|
||||||
/* Keyboard stuff; not interesting for playback */
|
/* Keyboard stuff; not interesting for playback */
|
||||||
if (len < 10) {
|
if (len < 10) {
|
||||||
GST_WARNING_OBJECT (dec, "Keyboard data too short");
|
GST_DEBUG_OBJECT (dec, "Keyboard data too short");
|
||||||
return -1;
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
}
|
}
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vmnc_handle_wmvh_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
vmnc_handle_wmvh_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
guint8 * data, int len)
|
const guint8 * data, int len, gboolean decode)
|
||||||
{
|
{
|
||||||
/* More keyboard stuff; not interesting for playback */
|
/* More keyboard stuff; not interesting for playback */
|
||||||
if (len < 4) {
|
if (len < 4) {
|
||||||
GST_WARNING_OBJECT (dec, "Keyboard data too short");
|
GST_DEBUG_OBJECT (dec, "Keyboard data too short");
|
||||||
return -1;
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
}
|
}
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vmnc_handle_wmvj_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
vmnc_handle_wmvj_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
guint8 * data, int len)
|
const guint8 * data, int len, gboolean decode)
|
||||||
{
|
{
|
||||||
/* VM state info, not interesting for playback */
|
/* VM state info, not interesting for playback */
|
||||||
if (len < 2) {
|
if (len < 2) {
|
||||||
GST_WARNING_OBJECT (dec, "VM state data too short");
|
GST_DEBUG_OBJECT (dec, "VM state data too short");
|
||||||
return -1;
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
}
|
}
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
render_raw_tile (GstVMncDec * dec, guint8 * data, int x, int y,
|
render_raw_tile (GstVMncDec * dec, const guint8 * data, int x, int y,
|
||||||
int width, int height)
|
int width, int height)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
guint8 *dst, *src;
|
guint8 *dst;
|
||||||
|
const guint8 *src;
|
||||||
int line;
|
int line;
|
||||||
|
|
||||||
src = data;
|
src = data;
|
||||||
|
@ -620,40 +660,93 @@ render_subrect (GstVMncDec * dec, int x, int y, int width,
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vmnc_handle_raw_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
vmnc_handle_raw_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
guint8 * data, int len)
|
const guint8 * data, int len, gboolean decode)
|
||||||
{
|
{
|
||||||
int datalen = rect->width * rect->height * dec->format.bytes_per_pixel;
|
int datalen = rect->width * rect->height * dec->format.bytes_per_pixel;
|
||||||
|
|
||||||
if (len < datalen) {
|
if (len < datalen) {
|
||||||
GST_WARNING_OBJECT (dec, "Raw data too short");
|
GST_DEBUG_OBJECT (dec, "Raw data too short");
|
||||||
return -1;
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
render_raw_tile (dec, data, rect->x, rect->y, rect->width, rect->height);
|
if (decode)
|
||||||
|
render_raw_tile (dec, data, rect->x, rect->y, rect->width, rect->height);
|
||||||
|
|
||||||
return datalen;
|
return datalen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vmnc_handle_copy_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
|
const guint8 * data, int len, gboolean decode)
|
||||||
|
{
|
||||||
|
int src_x, src_y;
|
||||||
|
guint8 *src, *dst;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (len < 4) {
|
||||||
|
GST_DEBUG_OBJECT (dec, "Copy data too short");
|
||||||
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
|
} else if (!decode)
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
src_x = RFB_GET_UINT16 (data);
|
||||||
|
src_y = RFB_GET_UINT16 (data + 2);
|
||||||
|
|
||||||
|
/* Our destination rectangle is guaranteed in-frame; check this for the source
|
||||||
|
* rectangle. */
|
||||||
|
if (src_x + rect->width > dec->format.width ||
|
||||||
|
src_y + rect->height > dec->format.height) {
|
||||||
|
GST_WARNING_OBJECT (dec, "Source rectangle out of range");
|
||||||
|
return ERROR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src_y > rect->y || src_x > rect->x) {
|
||||||
|
/* Moving forward */
|
||||||
|
src = dec->imagedata + dec->format.stride * src_y +
|
||||||
|
dec->format.bytes_per_pixel * src_x;
|
||||||
|
dst = dec->imagedata + dec->format.stride * rect->y +
|
||||||
|
dec->format.bytes_per_pixel * rect->x;
|
||||||
|
for (i = 0; i < rect->height; i++) {
|
||||||
|
memmove (dst, src, rect->width * dec->format.bytes_per_pixel);
|
||||||
|
dst += dec->format.stride;
|
||||||
|
src += dec->format.stride;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Backwards */
|
||||||
|
src = dec->imagedata + dec->format.stride * (src_y + rect->height - 1) +
|
||||||
|
dec->format.bytes_per_pixel * src_x;
|
||||||
|
dst = dec->imagedata + dec->format.stride * (rect->y + rect->height - 1) +
|
||||||
|
dec->format.bytes_per_pixel * rect->x;
|
||||||
|
for (i = rect->height; i > 0; i--) {
|
||||||
|
memmove (dst, src, rect->width * dec->format.bytes_per_pixel);
|
||||||
|
dst -= dec->format.stride;
|
||||||
|
src -= dec->format.stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
#define READ_PIXEL(pixel, data, off, len) \
|
#define READ_PIXEL(pixel, data, off, len) \
|
||||||
if (dec->format.bytes_per_pixel == 1) { \
|
if (dec->format.bytes_per_pixel == 1) { \
|
||||||
if (off >= len) \
|
if (off >= len) \
|
||||||
return -1; \
|
return ERROR_INSUFFICIENT_DATA; \
|
||||||
pixel = data[off++]; \
|
pixel = data[off++]; \
|
||||||
} else if (dec->format.bytes_per_pixel == 2) { \
|
} else if (dec->format.bytes_per_pixel == 2) { \
|
||||||
if (off+2 > len) \
|
if (off+2 > len) \
|
||||||
return -1; \
|
return ERROR_INSUFFICIENT_DATA; \
|
||||||
pixel = (*(guint16 *)(data + off)); \
|
pixel = (*(guint16 *)(data + off)); \
|
||||||
off += 2; \
|
off += 2; \
|
||||||
} else { \
|
} else { \
|
||||||
if (off+4 > len) \
|
if (off+4 > len) \
|
||||||
return -1; \
|
return ERROR_INSUFFICIENT_DATA; \
|
||||||
pixel = (*(guint32 *)(data + off)); \
|
pixel = (*(guint32 *)(data + off)); \
|
||||||
off += 4; \
|
off += 4; \
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vmnc_handle_hextile_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
vmnc_handle_hextile_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
guint8 * data, int len)
|
const guint8 * data, int len, gboolean decode)
|
||||||
{
|
{
|
||||||
int tilesx = GST_ROUND_UP_16 (rect->width) / 16;
|
int tilesx = GST_ROUND_UP_16 (rect->width) / 16;
|
||||||
int tilesy = GST_ROUND_UP_16 (rect->height) / 16;
|
int tilesy = GST_ROUND_UP_16 (rect->height) / 16;
|
||||||
|
@ -678,16 +771,17 @@ vmnc_handle_hextile_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
width = 16;
|
width = 16;
|
||||||
|
|
||||||
if (off >= len) {
|
if (off >= len) {
|
||||||
return -1;
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
}
|
}
|
||||||
flags = data[off++];
|
flags = data[off++];
|
||||||
|
|
||||||
if (flags & 0x1) {
|
if (flags & 0x1) {
|
||||||
if (off + width * height * dec->format.bytes_per_pixel > len) {
|
if (off + width * height * dec->format.bytes_per_pixel > len) {
|
||||||
return -1;
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
}
|
}
|
||||||
render_raw_tile (dec, data + off, rect->x + x * 16, rect->y + y * 16,
|
if (decode)
|
||||||
width, height);
|
render_raw_tile (dec, data + off, rect->x + x * 16, rect->y + y * 16,
|
||||||
|
width, height);
|
||||||
off += width * height * dec->format.bytes_per_pixel;
|
off += width * height * dec->format.bytes_per_pixel;
|
||||||
} else {
|
} else {
|
||||||
if (flags & 0x2) {
|
if (flags & 0x2) {
|
||||||
|
@ -700,23 +794,24 @@ vmnc_handle_hextile_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
subrects = 0;
|
subrects = 0;
|
||||||
if (flags & 0x8) {
|
if (flags & 0x8) {
|
||||||
if (off >= len) {
|
if (off >= len) {
|
||||||
return -1;
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
}
|
}
|
||||||
subrects = data[off++];
|
subrects = data[off++];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Paint background colour on entire tile */
|
/* Paint background colour on entire tile */
|
||||||
render_subrect (dec, rect->x + x * 16, rect->y + y * 16, width, height,
|
if (decode)
|
||||||
bg);
|
render_subrect (dec, rect->x + x * 16, rect->y + y * 16,
|
||||||
|
width, height, bg);
|
||||||
|
|
||||||
coloured = flags & 0x10;
|
coloured = flags & 0x10;
|
||||||
for (z = 0; z < subrects; z++) {
|
for (z = 0; z < subrects; z++) {
|
||||||
if (off + (coloured ? 3 : 2) > len)
|
|
||||||
return -1;
|
|
||||||
if (coloured) {
|
if (coloured) {
|
||||||
READ_PIXEL (colour, data, off, len);
|
READ_PIXEL (colour, data, off, len);
|
||||||
} else
|
} else
|
||||||
colour = fg;
|
colour = fg;
|
||||||
|
if (off + 2 > len)
|
||||||
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
|
|
||||||
{
|
{
|
||||||
int off_x = (data[off] & 0xf0) >> 4;
|
int off_x = (data[off] & 0xf0) >> 4;
|
||||||
|
@ -727,11 +822,15 @@ vmnc_handle_hextile_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
off += 2;
|
off += 2;
|
||||||
|
|
||||||
/* Ensure we don't have out of bounds coordinates */
|
/* Ensure we don't have out of bounds coordinates */
|
||||||
if (off_x + w > width || off_y + h > height)
|
if (off_x + w > width || off_y + h > height) {
|
||||||
return -1;
|
GST_WARNING_OBJECT (dec, "Subrect out of bounds: %d-%d x %d-%d "
|
||||||
|
"extends outside %dx%d", off_x, w, off_y, h, width, height);
|
||||||
|
return ERROR_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
render_subrect (dec, rect->x + x * 16 + off_x,
|
if (decode)
|
||||||
rect->y + y * 16 + off_y, w, h, colour);
|
render_subrect (dec, rect->x + x * 16 + off_x,
|
||||||
|
rect->y + y * 16 + off_y, w, h, colour);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -741,16 +840,23 @@ vmnc_handle_hextile_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||||
return off;
|
return off;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
/* Handle a packet in one of two modes: decode or parse.
|
||||||
vmnc_handle_packet (GstVMncDec * dec, GstBuffer * buf)
|
* In parse mode, we don't execute any of the decoding, we just do enough to
|
||||||
|
* figure out how many bytes it contains
|
||||||
|
*
|
||||||
|
* Returns: >= 0, the number of bytes consumed
|
||||||
|
* < 0, packet too short to decode, or error
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
vmnc_handle_packet (GstVMncDec * dec, const guint8 * data, int len,
|
||||||
|
gboolean decode)
|
||||||
{
|
{
|
||||||
guint8 *data = GST_BUFFER_DATA (buf);
|
|
||||||
int len = GST_BUFFER_SIZE (buf);
|
|
||||||
int type;
|
int type;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
if (len < 4) {
|
if (len < 4) {
|
||||||
GST_WARNING_OBJECT (dec, "Packet too short");
|
GST_DEBUG_OBJECT (dec, "Packet too short");
|
||||||
return GST_FLOW_ERROR;
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
type = data[0];
|
type = data[0];
|
||||||
|
@ -760,17 +866,21 @@ vmnc_handle_packet (GstVMncDec * dec, GstBuffer * buf)
|
||||||
{
|
{
|
||||||
int numrect = RFB_GET_UINT16 (data + 2);
|
int numrect = RFB_GET_UINT16 (data + 2);
|
||||||
int i;
|
int i;
|
||||||
int offset = 4;
|
|
||||||
int read;
|
int read;
|
||||||
|
|
||||||
|
offset = 4;
|
||||||
|
|
||||||
for (i = 0; i < numrect; i++) {
|
for (i = 0; i < numrect; i++) {
|
||||||
struct RfbRectangle r;
|
struct RfbRectangle r;
|
||||||
rectangle_handler handler;
|
rectangle_handler handler;
|
||||||
|
|
||||||
if (len < offset + 12) {
|
if (len < offset + 12) {
|
||||||
GST_WARNING_OBJECT (dec, "Packet too short for rectangle header");
|
GST_DEBUG_OBJECT (dec,
|
||||||
return GST_FLOW_ERROR;
|
"Packet too short for rectangle header: %d < %d",
|
||||||
|
len, offset + 12);
|
||||||
|
return ERROR_INSUFFICIENT_DATA;
|
||||||
}
|
}
|
||||||
|
GST_DEBUG_OBJECT (dec, "Reading rectangle %d", i);
|
||||||
r.x = RFB_GET_UINT16 (data + offset);
|
r.x = RFB_GET_UINT16 (data + offset);
|
||||||
r.y = RFB_GET_UINT16 (data + offset + 2);
|
r.y = RFB_GET_UINT16 (data + offset + 2);
|
||||||
r.width = RFB_GET_UINT16 (data + offset + 4);
|
r.width = RFB_GET_UINT16 (data + offset + 4);
|
||||||
|
@ -780,14 +890,15 @@ vmnc_handle_packet (GstVMncDec * dec, GstBuffer * buf)
|
||||||
if (r.type != TYPE_WMVi) {
|
if (r.type != TYPE_WMVi) {
|
||||||
/* We must have a WMVi packet to initialise things before we can
|
/* We must have a WMVi packet to initialise things before we can
|
||||||
* continue */
|
* continue */
|
||||||
if (!dec->caps) {
|
if (!dec->have_format) {
|
||||||
GST_WARNING_OBJECT (dec, "Received packet without WMVi");
|
GST_WARNING_OBJECT (dec, "Received packet without WMVi: %d",
|
||||||
return GST_FLOW_ERROR;
|
r.type);
|
||||||
|
return ERROR_INVALID;
|
||||||
}
|
}
|
||||||
if (r.x + r.width > dec->format.width ||
|
if (r.x + r.width > dec->format.width ||
|
||||||
r.y + r.height > dec->format.height) {
|
r.y + r.height > dec->format.height) {
|
||||||
GST_WARNING_OBJECT (dec, "Rectangle out of range, type %d", r.type);
|
GST_WARNING_OBJECT (dec, "Rectangle out of range, type %d", r.type);
|
||||||
return GST_FLOW_ERROR;
|
return ERROR_INVALID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -816,18 +927,21 @@ vmnc_handle_packet (GstVMncDec * dec, GstBuffer * buf)
|
||||||
case TYPE_RAW:
|
case TYPE_RAW:
|
||||||
handler = vmnc_handle_raw_rectangle;
|
handler = vmnc_handle_raw_rectangle;
|
||||||
break;
|
break;
|
||||||
|
case TYPE_COPY:
|
||||||
|
handler = vmnc_handle_copy_rectangle;
|
||||||
|
break;
|
||||||
case TYPE_HEXTILE:
|
case TYPE_HEXTILE:
|
||||||
handler = vmnc_handle_hextile_rectangle;
|
handler = vmnc_handle_hextile_rectangle;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
GST_WARNING_OBJECT (dec, "Unknown rectangle type");
|
GST_WARNING_OBJECT (dec, "Unknown rectangle type");
|
||||||
return GST_FLOW_ERROR;
|
return ERROR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
read = handler (dec, &r, data + offset + 12, len - offset - 12);
|
read = handler (dec, &r, data + offset + 12, len - offset - 12, decode);
|
||||||
if (read < 0) {
|
if (read < 0) {
|
||||||
GST_WARNING_OBJECT (dec, "Error calling rectangle handler\n");
|
GST_DEBUG_OBJECT (dec, "Error calling rectangle handler\n");
|
||||||
return GST_FLOW_ERROR;
|
return read;
|
||||||
}
|
}
|
||||||
offset += 12 + read;
|
offset += 12 + read;
|
||||||
}
|
}
|
||||||
|
@ -835,10 +949,10 @@ vmnc_handle_packet (GstVMncDec * dec, GstBuffer * buf)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
GST_WARNING_OBJECT (dec, "Packet type unknown: %d", type);
|
GST_WARNING_OBJECT (dec, "Packet type unknown: %d", type);
|
||||||
return GST_FLOW_ERROR;
|
return ERROR_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GST_FLOW_OK;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -847,38 +961,109 @@ vmnc_dec_setcaps (GstPad * pad, GstCaps * caps)
|
||||||
/* We require a format descriptor in-stream, so we ignore the info from the
|
/* We require a format descriptor in-stream, so we ignore the info from the
|
||||||
* container here. We just use the framerate */
|
* container here. We just use the framerate */
|
||||||
GstVMncDec *dec = GST_VMNC_DEC (gst_pad_get_parent (pad));
|
GstVMncDec *dec = GST_VMNC_DEC (gst_pad_get_parent (pad));
|
||||||
GstStructure *structure = gst_caps_get_structure (caps, 0);
|
|
||||||
|
|
||||||
/* We gave these a default in reset(), so we don't need to check for failure
|
if (gst_caps_get_size (caps) > 0) {
|
||||||
* here */
|
GstStructure *structure = gst_caps_get_structure (caps, 0);
|
||||||
gst_structure_get_fraction (structure, "framerate",
|
|
||||||
&dec->framerate_num, &dec->framerate_denom);
|
/* We gave these a default in reset(), so we don't need to check for failure
|
||||||
|
* here */
|
||||||
|
gst_structure_get_fraction (structure, "framerate",
|
||||||
|
&dec->framerate_num, &dec->framerate_denom);
|
||||||
|
|
||||||
|
dec->parsed = TRUE;
|
||||||
|
} else {
|
||||||
|
GST_DEBUG_OBJECT (dec, "Unparsed input");
|
||||||
|
dec->parsed = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
gst_object_unref (dec);
|
gst_object_unref (dec);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static GstFlowReturn
|
||||||
|
vmnc_dec_chain_frame (GstVMncDec * dec, GstBuffer * inbuf,
|
||||||
|
const guint8 * data, int len)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
|
GstBuffer *outbuf;
|
||||||
|
|
||||||
|
res = vmnc_handle_packet (dec, data, len, TRUE);
|
||||||
|
|
||||||
|
if (res < 0) {
|
||||||
|
ret = GST_FLOW_ERROR;
|
||||||
|
GST_ELEMENT_ERROR (dec, STREAM, DECODE, NULL, ("Couldn't decode packet"));
|
||||||
|
} else {
|
||||||
|
GST_DEBUG_OBJECT (dec, "read %d bytes of %d", res, len);
|
||||||
|
/* inbuf may be NULL; that's ok */
|
||||||
|
outbuf = vmnc_make_buffer (dec, inbuf);
|
||||||
|
ret = gst_pad_push (dec->srcpad, outbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
vmnc_dec_chain (GstPad * pad, GstBuffer * buf)
|
vmnc_dec_chain (GstPad * pad, GstBuffer * buf)
|
||||||
{
|
{
|
||||||
GstVMncDec *dec;
|
GstVMncDec *dec;
|
||||||
GstFlowReturn res;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
GstBuffer *outbuf;
|
|
||||||
|
|
||||||
dec = GST_VMNC_DEC (gst_pad_get_parent (pad));
|
dec = GST_VMNC_DEC (gst_pad_get_parent (pad));
|
||||||
|
|
||||||
res = vmnc_handle_packet (dec, buf);
|
if (!dec->parsed) {
|
||||||
|
/* Submit our input buffer to adapter, then parse. Push each frame found
|
||||||
|
* through the decoder
|
||||||
|
*/
|
||||||
|
int avail;
|
||||||
|
const guint8 *data;
|
||||||
|
int read = 0;
|
||||||
|
|
||||||
if (res == GST_FLOW_OK) {
|
gst_adapter_push (dec->adapter, buf);
|
||||||
outbuf = vmnc_make_buffer (dec, buf);
|
|
||||||
res = gst_pad_push (dec->srcpad, outbuf);
|
|
||||||
|
avail = gst_adapter_available (dec->adapter);
|
||||||
|
data = gst_adapter_peek (dec->adapter, avail);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (dec, "Parsing %d bytes", avail);
|
||||||
|
|
||||||
|
while (TRUE) {
|
||||||
|
int len = vmnc_handle_packet (dec, data, avail, FALSE);
|
||||||
|
|
||||||
|
if (len == ERROR_INSUFFICIENT_DATA) {
|
||||||
|
GST_DEBUG_OBJECT (dec, "Not enough data yet");
|
||||||
|
ret = GST_FLOW_OK;
|
||||||
|
break;
|
||||||
|
} else if (len < 0) {
|
||||||
|
GST_DEBUG_OBJECT (dec, "Fatal error in bitstream");
|
||||||
|
ret = GST_FLOW_ERROR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (dec, "Parsed packet: %d bytes", len);
|
||||||
|
|
||||||
|
ret = vmnc_dec_chain_frame (dec, NULL, data, len);
|
||||||
|
avail -= len;
|
||||||
|
data += len;
|
||||||
|
|
||||||
|
read += len;
|
||||||
|
|
||||||
|
if (ret != GST_FLOW_OK)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
GST_DEBUG_OBJECT (dec, "Flushing %d bytes", read);
|
||||||
|
|
||||||
|
gst_adapter_flush (dec->adapter, read);
|
||||||
|
} else {
|
||||||
|
ret = vmnc_dec_chain_frame (dec, buf, GST_BUFFER_DATA (buf),
|
||||||
|
GST_BUFFER_SIZE (buf));
|
||||||
|
gst_buffer_unref (buf);
|
||||||
}
|
}
|
||||||
gst_buffer_unref (buf);
|
|
||||||
|
|
||||||
gst_object_unref (dec);
|
gst_object_unref (dec);
|
||||||
|
|
||||||
return res;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstStateChangeReturn
|
static GstStateChangeReturn
|
||||||
|
|
Loading…
Reference in a new issue