sirendec: rewrite sirendec

Add setcaps and event functions.
Add state change functions to clean variables.
Use adapter.
Add timestamps and duration on outgoing buffers.
Add DISCONT handling.
This commit is contained in:
Wim Taymans 2009-09-04 12:40:40 +02:00 committed by Wim Taymans
parent ef999c5df7
commit d557572e66
2 changed files with 181 additions and 59 deletions

View file

@ -40,6 +40,8 @@
GST_DEBUG_CATEGORY (sirendec_debug); GST_DEBUG_CATEGORY (sirendec_debug);
#define GST_CAT_DEFAULT (sirendec_debug) #define GST_CAT_DEFAULT (sirendec_debug)
#define FRAME_DURATION (20 * GST_MSECOND)
/* elementfactory information */ /* elementfactory information */
static const GstElementDetails gst_siren_dec_details = static const GstElementDetails gst_siren_dec_details =
GST_ELEMENT_DETAILS ("Siren Decoder element", GST_ELEMENT_DETAILS ("Siren Decoder element",
@ -75,11 +77,15 @@ enum
ARG_0, ARG_0,
}; };
static void gst_siren_dec_finalize (GObject * object);
static GstStateChangeReturn
gst_siren_change_state (GstElement * element, GstStateChange transition);
static gboolean gst_siren_dec_sink_setcaps (GstPad * pad, GstCaps * caps);
static gboolean gst_siren_dec_sink_event (GstPad * pad, GstEvent * event);
static GstFlowReturn gst_siren_dec_chain (GstPad * pad, GstBuffer * buf); static GstFlowReturn gst_siren_dec_chain (GstPad * pad, GstBuffer * buf);
static void gst_siren_dec_dispose (GObject * object);
static void static void
_do_init (GType type) _do_init (GType type)
{ {
@ -113,7 +119,9 @@ gst_siren_dec_class_init (GstSirenDecClass * klass)
GST_DEBUG ("Initializing Class"); GST_DEBUG ("Initializing Class");
gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_siren_dec_dispose); gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_siren_dec_finalize);
gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_siren_change_state);
GST_DEBUG ("Class Init done"); GST_DEBUG ("Class Init done");
} }
@ -128,102 +136,215 @@ gst_siren_dec_init (GstSirenDec * dec, GstSirenDecClass * klass)
dec->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink"); dec->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
dec->srcpad = gst_pad_new_from_static_template (&srctemplate, "src"); dec->srcpad = gst_pad_new_from_static_template (&srctemplate, "src");
gst_pad_set_setcaps_function (dec->sinkpad,
GST_DEBUG_FUNCPTR (gst_siren_dec_sink_setcaps));
gst_pad_set_event_function (dec->sinkpad,
GST_DEBUG_FUNCPTR (gst_siren_dec_sink_event));
gst_pad_set_chain_function (dec->sinkpad, gst_pad_set_chain_function (dec->sinkpad,
GST_DEBUG_FUNCPTR (gst_siren_dec_chain)); GST_DEBUG_FUNCPTR (gst_siren_dec_chain));
gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
dec->srccaps = gst_static_pad_template_get_caps (&srctemplate); dec->adapter = gst_adapter_new ();
GST_DEBUG_OBJECT (dec, "Init done"); GST_DEBUG_OBJECT (dec, "Init done");
} }
static void static void
gst_siren_dec_dispose (GObject * object) gst_siren_dec_finalize (GObject * object)
{ {
GstSirenDec *dec = GST_SIREN_DEC (object); GstSirenDec *dec = GST_SIREN_DEC (object);
GST_DEBUG_OBJECT (dec, "Disposing"); GST_DEBUG_OBJECT (dec, "Finalize");
if (dec->decoder) {
Siren7_CloseDecoder (dec->decoder); Siren7_CloseDecoder (dec->decoder);
dec->decoder = NULL; g_object_unref (dec->adapter);
}
if (dec->srccaps) { G_OBJECT_CLASS (parent_class)->finalize (object);
gst_caps_unref (dec->srccaps); }
dec->srccaps = NULL;
}
G_OBJECT_CLASS (parent_class)->dispose (object); static gboolean
gst_siren_dec_sink_setcaps (GstPad * pad, GstCaps * caps)
{
GstSirenDec *dec;
gboolean res;
GstCaps *outcaps;
dec = GST_SIREN_DEC (GST_PAD_PARENT (pad));
outcaps = gst_static_pad_template_get_caps (&srctemplate);
res = gst_pad_set_caps (dec->srcpad, outcaps);
gst_caps_unref (outcaps);
return res;
}
static gboolean
gst_siren_dec_sink_event (GstPad * pad, GstEvent * event)
{
GstSirenDec *dec;
gboolean res;
dec = GST_SIREN_DEC (GST_PAD_PARENT (pad));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_EOS:
gst_adapter_clear (dec->adapter);
res = gst_pad_push_event (dec->srcpad, event);
break;
case GST_EVENT_FLUSH_STOP:
gst_adapter_clear (dec->adapter);
res = gst_pad_push_event (dec->srcpad, event);
break;
default:
res = gst_pad_push_event (dec->srcpad, event);
break;
}
return res;
} }
static GstFlowReturn static GstFlowReturn
gst_siren_dec_chain (GstPad * pad, GstBuffer * buf) gst_siren_dec_chain (GstPad * pad, GstBuffer * buf)
{ {
GstSirenDec *dec = GST_SIREN_DEC (gst_pad_get_parent_element (pad)); GstSirenDec *dec;
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
GstBuffer *decoded = NULL; GstBuffer *out_buf;
guint in_offset = 0; guint8 *in_data, *out_data;
guint out_offset = 0; guint8 *to_free = NULL;
gint decode_ret = 0; guint i, size, num_frames;
guint size = 0; gint out_size, in_size;
gint decode_ret;
gboolean discont;
GstClockTime timestamp;
guint64 distance;
GST_LOG_OBJECT (dec, "Decoding buffer of size %d", GST_BUFFER_SIZE (buf)); dec = GST_SIREN_DEC (GST_PAD_PARENT (pad));
size = GST_BUFFER_SIZE (buf) * 16; discont = GST_BUFFER_IS_DISCONT (buf);
size -= size % 640; if (discont) {
GST_DEBUG_OBJECT (dec, "received DISCONT, flush adapter");
if (size == 0) { gst_adapter_clear (dec->adapter);
GST_LOG_OBJECT (dec, "Got buffer smaller than framesize: %u < 40", dec->discont = TRUE;
GST_BUFFER_SIZE (buf));
return GST_FLOW_OK;
} }
if (GST_BUFFER_SIZE (buf) % 40 != 0) gst_adapter_push (dec->adapter, buf);
GST_LOG_OBJECT (dec, "Got buffer with size not a multiple for frame size,"
" ignoring last %u bytes", GST_BUFFER_SIZE (buf) % 40);
ret = gst_pad_alloc_buffer_and_set_caps (dec->srcpad, size = gst_adapter_available (dec->adapter);
GST_BUFFER_OFFSET (buf) * 16, size, dec->srccaps, &decoded);
GST_LOG_OBJECT (dec, "Received buffer of size %u with adapter of size : %u",
GST_BUFFER_SIZE (buf), size);
/* process 40 input bytes into 640 output bytes */
num_frames = size / 40;
if (num_frames == 0)
goto done;
/* this is the input/output size */
in_size = num_frames * 40;
out_size = num_frames * 640;
GST_LOG_OBJECT (dec, "we have %u frames, %u in, %u out", num_frames, in_size,
out_size);
/* get a buffer */
ret = gst_pad_alloc_buffer_and_set_caps (dec->srcpad, -1,
out_size, GST_PAD_CAPS (dec->srcpad), &out_buf);
if (ret != GST_FLOW_OK) if (ret != GST_FLOW_OK)
goto alloc_failed;
/* get the timestamp for the output buffer */
timestamp = gst_adapter_prev_timestamp (dec->adapter, &distance);
/* add the amount of time taken by the distance, each frame is 20ms */
if (timestamp != -1)
timestamp += (distance / 40) * FRAME_DURATION;
GST_LOG_OBJECT (dec,
"timestamp %" GST_TIME_FORMAT ", distance %" G_GUINT64_FORMAT,
GST_TIME_ARGS (timestamp), distance);
/* get the input data for all the frames */
to_free = in_data = gst_adapter_take (dec->adapter, in_size);
out_data = GST_BUFFER_DATA (out_buf);
for (i = 0; i < num_frames; i++) {
GST_LOG_OBJECT (dec, "Decoding frame %u/%u", i, num_frames);
/* decode 40 input bytes to 640 output bytes */
decode_ret = Siren7_DecodeFrame (dec->decoder, in_data, out_data);
if (decode_ret != 0)
goto decode_error;
/* move to next frame */
out_data += 640;
in_data += 40;
}
GST_LOG_OBJECT (dec, "Finished decoding");
/* mark discont */
if (dec->discont) {
GST_BUFFER_FLAG_SET (out_buf, GST_BUFFER_FLAG_DISCONT);
dec->discont = FALSE;
}
GST_BUFFER_TIMESTAMP (out_buf) = timestamp;
GST_BUFFER_DURATION (out_buf) = num_frames * FRAME_DURATION;
ret = gst_pad_push (dec->srcpad, out_buf);
done:
if (to_free)
g_free (to_free);
return ret; return ret;
GST_BUFFER_TIMESTAMP (decoded) = GST_BUFFER_TIMESTAMP (buf); /* ERRORS */
alloc_failed:
while ((in_offset + 40 <= GST_BUFFER_SIZE (buf)) && ret == GST_FLOW_OK) { {
GST_DEBUG_OBJECT (dec, "failed to pad_alloc buffer: %d (%d)", ret,
GST_LOG_OBJECT (dec, "Decoding frame"); gst_flow_get_name (ret));
goto done;
decode_ret = Siren7_DecodeFrame (dec->decoder, }
GST_BUFFER_DATA (buf) + in_offset, decode_error:
GST_BUFFER_DATA (decoded) + out_offset); {
if (decode_ret != 0) { GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL),
GST_ERROR_OBJECT (dec, "Siren7_DecodeFrame returned %d", decode_ret); ("Error decoding frame: %d", decode_ret));
ret = GST_FLOW_ERROR; ret = GST_FLOW_ERROR;
gst_buffer_unref (out_buf);
goto done;
}
}
static GstStateChangeReturn
gst_siren_change_state (GstElement * element, GstStateChange transition)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstSirenDec *dec = GST_SIREN_DEC (element);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
dec->discont = FALSE;
break;
default:
break;
} }
in_offset += 40; ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
out_offset += 640;
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
gst_adapter_clear (dec->adapter);
break;
default:
break;
} }
GST_LOG_OBJECT (dec, "Finished decoding : %d", out_offset);
if (out_offset != GST_BUFFER_SIZE (decoded)) {
GST_ERROR_OBJECT (dec,
"didn't decode enough : offfset (%d) != BUFFER_SIZE (%d)",
out_offset, GST_BUFFER_SIZE (decoded));
return GST_FLOW_ERROR;
}
ret = gst_pad_push (dec->srcpad, decoded);
gst_object_unref (dec);
return ret; return ret;
} }
gboolean gboolean
gst_siren_dec_plugin_init (GstPlugin * plugin) gst_siren_dec_plugin_init (GstPlugin * plugin)
{ {

View file

@ -53,10 +53,11 @@ struct _GstSirenDec
/* Protected by stream lock */ /* Protected by stream lock */
SirenDecoder decoder; SirenDecoder decoder;
GstAdapter *adapter;
gboolean discont;
GstPad *sinkpad; GstPad *sinkpad;
GstPad *srcpad; GstPad *srcpad;
GstCaps *srccaps;
}; };
struct _GstSirenDecClass struct _GstSirenDecClass