mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +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>
|
||||
|
||||
* gst/interleave/deinterleave.c: (gst_deinterleave_add_new_pads),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
plugin_LTLIBRARIES = libgstvmnc.la
|
||||
|
||||
libgstvmnc_la_SOURCES = vmncdec.c
|
||||
libgstvmnc_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS) $(THEORA_CFLAGS)
|
||||
libgstvmnc_la_LIBADD = $(GST_LIBS)
|
||||
libgstvmnc_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(THEORA_CFLAGS)
|
||||
libgstvmnc_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS)
|
||||
libgstvmnc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#endif
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstadapter.h>
|
||||
#include <gst/video/video.h>
|
||||
#include <string.h>
|
||||
|
||||
|
@ -51,6 +52,12 @@ enum
|
|||
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)
|
||||
enum
|
||||
{
|
||||
|
@ -109,6 +116,10 @@ typedef struct
|
|||
GstPad *srcpad;
|
||||
|
||||
GstCaps *caps;
|
||||
gboolean have_format;
|
||||
|
||||
int parsed;
|
||||
GstAdapter *adapter;
|
||||
|
||||
int framerate_num;
|
||||
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 GstStateChangeReturn vmnc_dec_change_state (GstElement * element,
|
||||
GstStateChange transition);
|
||||
static void vmnc_dec_finalize (GObject * object);
|
||||
|
||||
static void
|
||||
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->get_property = vmnc_dec_get_property;
|
||||
|
||||
gobject_class->finalize = vmnc_dec_finalize;
|
||||
|
||||
gstelement_class->change_state = vmnc_dec_change_state;
|
||||
|
||||
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_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
|
||||
|
@ -222,6 +246,10 @@ gst_vmnc_dec_reset (GstVMncDec * dec)
|
|||
/* Use these as defaults if the container doesn't provide anything */
|
||||
dec->framerate_num = 5;
|
||||
dec->framerate_denom = 1;
|
||||
|
||||
dec->have_format = FALSE;
|
||||
|
||||
gst_adapter_clear (dec->adapter);
|
||||
}
|
||||
|
||||
struct RfbRectangle
|
||||
|
@ -238,11 +266,11 @@ struct RfbRectangle
|
|||
* Return number of bytes consumed, or < 0 on error
|
||||
*/
|
||||
typedef int (*rectangle_handler) (GstVMncDec * dec, struct RfbRectangle * rect,
|
||||
guint8 * data, int len);
|
||||
const guint8 * data, int len, gboolean decode);
|
||||
|
||||
static int
|
||||
vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||
guint8 * data, int len)
|
||||
const guint8 * data, int len, gboolean decode)
|
||||
{
|
||||
GstCaps *caps;
|
||||
gint bpp, tc;
|
||||
|
@ -251,12 +279,12 @@ vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
|||
|
||||
/* A WMVi rectangle has a 16byte payload */
|
||||
if (len < 16) {
|
||||
GST_WARNING_OBJECT (dec, "Bad WMVi rect: too short");
|
||||
return -1;
|
||||
GST_DEBUG_OBJECT (dec, "Bad WMVi rect: too short");
|
||||
return ERROR_INSUFFICIENT_DATA;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
return 16;
|
||||
}
|
||||
|
@ -266,7 +294,7 @@ vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
|||
|
||||
if (rect->x != 0 || rect->y != 0) {
|
||||
GST_WARNING_OBJECT (dec, "Bad WMVi rect: wrong coordinates");
|
||||
return -1;
|
||||
return ERROR_INVALID;
|
||||
}
|
||||
|
||||
bpp = data[0];
|
||||
|
@ -277,12 +305,12 @@ vmnc_handle_wmvi_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
|||
|
||||
if (bpp != 8 && bpp != 16 && bpp != 32) {
|
||||
GST_WARNING_OBJECT (dec, "Bad bpp value: %d", bpp);
|
||||
return -1;
|
||||
return ERROR_INVALID;
|
||||
}
|
||||
|
||||
if (!tc) {
|
||||
GST_WARNING_OBJECT (dec, "Paletted video not supported");
|
||||
return -1;
|
||||
return ERROR_INVALID;
|
||||
}
|
||||
|
||||
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",
|
||||
"framerate", GST_TYPE_FRACTION, dec->framerate_num, dec->framerate_denom,
|
||||
"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);
|
||||
dec->imagedata = g_malloc (dec->format.width * dec->format.height *
|
||||
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;
|
||||
|
||||
|
@ -452,7 +487,9 @@ vmnc_make_buffer (GstVMncDec * dec, GstBuffer * inbuf)
|
|||
render_cursor (dec, data);
|
||||
}
|
||||
|
||||
gst_buffer_stamp (buf, inbuf);
|
||||
if (inbuf) {
|
||||
gst_buffer_stamp (buf, inbuf);
|
||||
}
|
||||
|
||||
gst_buffer_set_caps (buf, dec->caps);
|
||||
|
||||
|
@ -461,15 +498,15 @@ vmnc_make_buffer (GstVMncDec * dec, GstBuffer * inbuf)
|
|||
|
||||
static int
|
||||
vmnc_handle_wmvd_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||
guint8 * data, int len)
|
||||
const guint8 * data, int len, gboolean decode)
|
||||
{
|
||||
/* Cursor data. */
|
||||
int datalen = 2;
|
||||
int type, size;
|
||||
|
||||
if (len < datalen) {
|
||||
GST_WARNING_OBJECT (dec, "Cursor data too short");
|
||||
return -1;
|
||||
GST_DEBUG_OBJECT (dec, "Cursor data too short");
|
||||
return ERROR_INSUFFICIENT_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;
|
||||
} else {
|
||||
GST_WARNING_OBJECT (dec, "Unknown cursor type: %d", type);
|
||||
return -1;
|
||||
return ERROR_INVALID;
|
||||
}
|
||||
|
||||
if (len < datalen) {
|
||||
GST_WARNING_OBJECT (dec, "Cursor data too short");
|
||||
return -1;
|
||||
}
|
||||
GST_DEBUG_OBJECT (dec, "Cursor data too short");
|
||||
return ERROR_INSUFFICIENT_DATA;
|
||||
} else if (!decode)
|
||||
return datalen;
|
||||
|
||||
dec->cursor.type = type;
|
||||
dec->cursor.width = rect->width;
|
||||
|
@ -516,15 +554,16 @@ vmnc_handle_wmvd_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
|||
|
||||
static int
|
||||
vmnc_handle_wmve_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||
guint8 * data, int len)
|
||||
const guint8 * data, int len, gboolean decode)
|
||||
{
|
||||
guint16 flags;
|
||||
|
||||
/* Cursor state. */
|
||||
if (len < 2) {
|
||||
GST_WARNING_OBJECT (dec, "Cursor data too short");
|
||||
return -1;
|
||||
}
|
||||
GST_DEBUG_OBJECT (dec, "Cursor data too short");
|
||||
return ERROR_INSUFFICIENT_DATA;
|
||||
} else if (!decode)
|
||||
return 2;
|
||||
|
||||
flags = RFB_GET_UINT16 (data);
|
||||
dec->cursor.visible = flags & 0x01;
|
||||
|
@ -534,7 +573,7 @@ vmnc_handle_wmve_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
|||
|
||||
static int
|
||||
vmnc_handle_wmvf_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
||||
guint8 * data, int len)
|
||||
const guint8 * data, int len, gboolean decode)
|
||||
{
|
||||
/* Cursor position. */
|
||||
dec->cursor.x = rect->x;
|
||||
|
@ -544,46 +583,47 @@ vmnc_handle_wmvf_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
|||
|
||||
static int
|
||||
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 */
|
||||
if (len < 10) {
|
||||
GST_WARNING_OBJECT (dec, "Keyboard data too short");
|
||||
return -1;
|
||||
GST_DEBUG_OBJECT (dec, "Keyboard data too short");
|
||||
return ERROR_INSUFFICIENT_DATA;
|
||||
}
|
||||
return 10;
|
||||
}
|
||||
|
||||
static int
|
||||
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 */
|
||||
if (len < 4) {
|
||||
GST_WARNING_OBJECT (dec, "Keyboard data too short");
|
||||
return -1;
|
||||
GST_DEBUG_OBJECT (dec, "Keyboard data too short");
|
||||
return ERROR_INSUFFICIENT_DATA;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
|
||||
static int
|
||||
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 */
|
||||
if (len < 2) {
|
||||
GST_WARNING_OBJECT (dec, "VM state data too short");
|
||||
return -1;
|
||||
GST_DEBUG_OBJECT (dec, "VM state data too short");
|
||||
return ERROR_INSUFFICIENT_DATA;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
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 i;
|
||||
guint8 *dst, *src;
|
||||
guint8 *dst;
|
||||
const guint8 *src;
|
||||
int line;
|
||||
|
||||
src = data;
|
||||
|
@ -620,40 +660,93 @@ render_subrect (GstVMncDec * dec, int x, int y, int width,
|
|||
|
||||
static int
|
||||
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;
|
||||
|
||||
if (len < datalen) {
|
||||
GST_WARNING_OBJECT (dec, "Raw data too short");
|
||||
return -1;
|
||||
GST_DEBUG_OBJECT (dec, "Raw data too short");
|
||||
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;
|
||||
}
|
||||
|
||||
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) \
|
||||
if (dec->format.bytes_per_pixel == 1) { \
|
||||
if (off >= len) \
|
||||
return -1; \
|
||||
return ERROR_INSUFFICIENT_DATA; \
|
||||
pixel = data[off++]; \
|
||||
} else if (dec->format.bytes_per_pixel == 2) { \
|
||||
if (off+2 > len) \
|
||||
return -1; \
|
||||
return ERROR_INSUFFICIENT_DATA; \
|
||||
pixel = (*(guint16 *)(data + off)); \
|
||||
off += 2; \
|
||||
} else { \
|
||||
if (off+4 > len) \
|
||||
return -1; \
|
||||
return ERROR_INSUFFICIENT_DATA; \
|
||||
pixel = (*(guint32 *)(data + off)); \
|
||||
off += 4; \
|
||||
}
|
||||
|
||||
static int
|
||||
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 tilesy = GST_ROUND_UP_16 (rect->height) / 16;
|
||||
|
@ -678,16 +771,17 @@ vmnc_handle_hextile_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
|||
width = 16;
|
||||
|
||||
if (off >= len) {
|
||||
return -1;
|
||||
return ERROR_INSUFFICIENT_DATA;
|
||||
}
|
||||
flags = data[off++];
|
||||
|
||||
if (flags & 0x1) {
|
||||
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,
|
||||
width, height);
|
||||
if (decode)
|
||||
render_raw_tile (dec, data + off, rect->x + x * 16, rect->y + y * 16,
|
||||
width, height);
|
||||
off += width * height * dec->format.bytes_per_pixel;
|
||||
} else {
|
||||
if (flags & 0x2) {
|
||||
|
@ -700,23 +794,24 @@ vmnc_handle_hextile_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
|||
subrects = 0;
|
||||
if (flags & 0x8) {
|
||||
if (off >= len) {
|
||||
return -1;
|
||||
return ERROR_INSUFFICIENT_DATA;
|
||||
}
|
||||
subrects = data[off++];
|
||||
}
|
||||
|
||||
/* Paint background colour on entire tile */
|
||||
render_subrect (dec, rect->x + x * 16, rect->y + y * 16, width, height,
|
||||
bg);
|
||||
if (decode)
|
||||
render_subrect (dec, rect->x + x * 16, rect->y + y * 16,
|
||||
width, height, bg);
|
||||
|
||||
coloured = flags & 0x10;
|
||||
for (z = 0; z < subrects; z++) {
|
||||
if (off + (coloured ? 3 : 2) > len)
|
||||
return -1;
|
||||
if (coloured) {
|
||||
READ_PIXEL (colour, data, off, len);
|
||||
} else
|
||||
colour = fg;
|
||||
if (off + 2 > len)
|
||||
return ERROR_INSUFFICIENT_DATA;
|
||||
|
||||
{
|
||||
int off_x = (data[off] & 0xf0) >> 4;
|
||||
|
@ -727,11 +822,15 @@ vmnc_handle_hextile_rectangle (GstVMncDec * dec, struct RfbRectangle *rect,
|
|||
off += 2;
|
||||
|
||||
/* Ensure we don't have out of bounds coordinates */
|
||||
if (off_x + w > width || off_y + h > height)
|
||||
return -1;
|
||||
if (off_x + w > width || off_y + h > height) {
|
||||
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,
|
||||
rect->y + y * 16 + off_y, w, h, colour);
|
||||
if (decode)
|
||||
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;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
vmnc_handle_packet (GstVMncDec * dec, GstBuffer * buf)
|
||||
/* Handle a packet in one of two modes: decode or parse.
|
||||
* 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 offset = 0;
|
||||
|
||||
if (len < 4) {
|
||||
GST_WARNING_OBJECT (dec, "Packet too short");
|
||||
return GST_FLOW_ERROR;
|
||||
GST_DEBUG_OBJECT (dec, "Packet too short");
|
||||
return ERROR_INSUFFICIENT_DATA;
|
||||
}
|
||||
|
||||
type = data[0];
|
||||
|
@ -760,17 +866,21 @@ vmnc_handle_packet (GstVMncDec * dec, GstBuffer * buf)
|
|||
{
|
||||
int numrect = RFB_GET_UINT16 (data + 2);
|
||||
int i;
|
||||
int offset = 4;
|
||||
int read;
|
||||
|
||||
offset = 4;
|
||||
|
||||
for (i = 0; i < numrect; i++) {
|
||||
struct RfbRectangle r;
|
||||
rectangle_handler handler;
|
||||
|
||||
if (len < offset + 12) {
|
||||
GST_WARNING_OBJECT (dec, "Packet too short for rectangle header");
|
||||
return GST_FLOW_ERROR;
|
||||
GST_DEBUG_OBJECT (dec,
|
||||
"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.y = RFB_GET_UINT16 (data + offset + 2);
|
||||
r.width = RFB_GET_UINT16 (data + offset + 4);
|
||||
|
@ -780,14 +890,15 @@ vmnc_handle_packet (GstVMncDec * dec, GstBuffer * buf)
|
|||
if (r.type != TYPE_WMVi) {
|
||||
/* We must have a WMVi packet to initialise things before we can
|
||||
* continue */
|
||||
if (!dec->caps) {
|
||||
GST_WARNING_OBJECT (dec, "Received packet without WMVi");
|
||||
return GST_FLOW_ERROR;
|
||||
if (!dec->have_format) {
|
||||
GST_WARNING_OBJECT (dec, "Received packet without WMVi: %d",
|
||||
r.type);
|
||||
return ERROR_INVALID;
|
||||
}
|
||||
if (r.x + r.width > dec->format.width ||
|
||||
r.y + r.height > dec->format.height) {
|
||||
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:
|
||||
handler = vmnc_handle_raw_rectangle;
|
||||
break;
|
||||
case TYPE_COPY:
|
||||
handler = vmnc_handle_copy_rectangle;
|
||||
break;
|
||||
case TYPE_HEXTILE:
|
||||
handler = vmnc_handle_hextile_rectangle;
|
||||
break;
|
||||
default:
|
||||
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) {
|
||||
GST_WARNING_OBJECT (dec, "Error calling rectangle handler\n");
|
||||
return GST_FLOW_ERROR;
|
||||
GST_DEBUG_OBJECT (dec, "Error calling rectangle handler\n");
|
||||
return read;
|
||||
}
|
||||
offset += 12 + read;
|
||||
}
|
||||
|
@ -835,10 +949,10 @@ vmnc_handle_packet (GstVMncDec * dec, GstBuffer * buf)
|
|||
}
|
||||
default:
|
||||
GST_WARNING_OBJECT (dec, "Packet type unknown: %d", type);
|
||||
return GST_FLOW_ERROR;
|
||||
return ERROR_INVALID;
|
||||
}
|
||||
|
||||
return GST_FLOW_OK;
|
||||
return offset;
|
||||
}
|
||||
|
||||
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
|
||||
* container here. We just use the framerate */
|
||||
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
|
||||
* here */
|
||||
gst_structure_get_fraction (structure, "framerate",
|
||||
&dec->framerate_num, &dec->framerate_denom);
|
||||
if (gst_caps_get_size (caps) > 0) {
|
||||
GstStructure *structure = gst_caps_get_structure (caps, 0);
|
||||
|
||||
/* 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);
|
||||
|
||||
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
|
||||
vmnc_dec_chain (GstPad * pad, GstBuffer * buf)
|
||||
{
|
||||
GstVMncDec *dec;
|
||||
GstFlowReturn res;
|
||||
GstBuffer *outbuf;
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
|
||||
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) {
|
||||
outbuf = vmnc_make_buffer (dec, buf);
|
||||
res = gst_pad_push (dec->srcpad, outbuf);
|
||||
gst_adapter_push (dec->adapter, buf);
|
||||
|
||||
|
||||
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);
|
||||
|
||||
return res;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
|
|
Loading…
Reference in a new issue