summaryrefslogtreecommitdiffstats
path: root/gst
diff options
context:
space:
mode:
authorMichael Smith <msmith@xiph.org>2007-03-23 18:41:52 +0000
committerMichael Smith <msmith@xiph.org>2007-03-23 18:41:52 +0000
commit628d19135f4c559c51d396ace9bbb899624d6bd9 (patch)
tree49978b06e7417676f6b07e6709b762192f6a9f9b /gst
parent63881aeec07deca048db446e532d990a11d87f84 (diff)
downloadgst-plugins-bad-628d19135f4c559c51d396ace9bbb899624d6bd9.tar.gz
gst-plugins-bad-628d19135f4c559c51d396ace9bbb899624d6bd9.tar.bz2
gst-plugins-bad-628d19135f4c559c51d396ace9bbb899624d6bd9.zip
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).
Diffstat (limited to 'gst')
-rw-r--r--gst/vmnc/Makefile.am4
-rw-r--r--gst/vmnc/vmncdec.c357
2 files changed, 273 insertions, 88 deletions
diff --git a/gst/vmnc/Makefile.am b/gst/vmnc/Makefile.am
index 34ff95ac..dc71466e 100644
--- a/gst/vmnc/Makefile.am
+++ b/gst/vmnc/Makefile.am
@@ -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)
diff --git a/gst/vmnc/vmncdec.c b/gst/vmnc/vmncdec.c
index 7dd0b1c4..2a9a7a67 100644
--- a/gst/vmnc/vmncdec.c
+++ b/gst/vmnc/vmncdec.c
@@ -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;
-
- render_subrect (dec, rect->x + x * 16 + off_x,
- rect->y + y * 16 + off_y, w, h, colour);
+ 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;
+ }
+
+ 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,12 +961,20 @@ 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);
@@ -860,25 +982,88 @@ vmnc_dec_setcaps (GstPad * pad, GstCaps * caps)
}
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;
+
+ 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 (res == GST_FLOW_OK) {
- outbuf = vmnc_make_buffer (dec, buf);
- res = gst_pad_push (dec->srcpad, outbuf);
+ 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