mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-23 10:11:08 +00:00
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:
parent
ef999c5df7
commit
d557572e66
2 changed files with 181 additions and 59 deletions
|
@ -40,6 +40,8 @@
|
|||
GST_DEBUG_CATEGORY (sirendec_debug);
|
||||
#define GST_CAT_DEFAULT (sirendec_debug)
|
||||
|
||||
#define FRAME_DURATION (20 * GST_MSECOND)
|
||||
|
||||
/* elementfactory information */
|
||||
static const GstElementDetails gst_siren_dec_details =
|
||||
GST_ELEMENT_DETAILS ("Siren Decoder element",
|
||||
|
@ -75,11 +77,15 @@ enum
|
|||
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 void gst_siren_dec_dispose (GObject * object);
|
||||
|
||||
static void
|
||||
_do_init (GType type)
|
||||
{
|
||||
|
@ -113,7 +119,9 @@ gst_siren_dec_class_init (GstSirenDecClass * klass)
|
|||
|
||||
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");
|
||||
}
|
||||
|
@ -128,102 +136,215 @@ gst_siren_dec_init (GstSirenDec * dec, GstSirenDecClass * klass)
|
|||
dec->sinkpad = gst_pad_new_from_static_template (&sinktemplate, "sink");
|
||||
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_DEBUG_FUNCPTR (gst_siren_dec_chain));
|
||||
|
||||
gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
|
||||
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");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_siren_dec_dispose (GObject * object)
|
||||
gst_siren_dec_finalize (GObject * object)
|
||||
{
|
||||
GstSirenDec *dec = GST_SIREN_DEC (object);
|
||||
|
||||
GST_DEBUG_OBJECT (dec, "Disposing");
|
||||
GST_DEBUG_OBJECT (dec, "Finalize");
|
||||
|
||||
if (dec->decoder) {
|
||||
Siren7_CloseDecoder (dec->decoder);
|
||||
dec->decoder = NULL;
|
||||
Siren7_CloseDecoder (dec->decoder);
|
||||
g_object_unref (dec->adapter);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (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;
|
||||
}
|
||||
|
||||
if (dec->srccaps) {
|
||||
gst_caps_unref (dec->srccaps);
|
||||
dec->srccaps = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
return res;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
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;
|
||||
GstBuffer *decoded = NULL;
|
||||
guint in_offset = 0;
|
||||
guint out_offset = 0;
|
||||
gint decode_ret = 0;
|
||||
guint size = 0;
|
||||
GstBuffer *out_buf;
|
||||
guint8 *in_data, *out_data;
|
||||
guint8 *to_free = NULL;
|
||||
guint i, size, num_frames;
|
||||
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;
|
||||
size -= size % 640;
|
||||
|
||||
if (size == 0) {
|
||||
GST_LOG_OBJECT (dec, "Got buffer smaller than framesize: %u < 40",
|
||||
GST_BUFFER_SIZE (buf));
|
||||
return GST_FLOW_OK;
|
||||
discont = GST_BUFFER_IS_DISCONT (buf);
|
||||
if (discont) {
|
||||
GST_DEBUG_OBJECT (dec, "received DISCONT, flush adapter");
|
||||
gst_adapter_clear (dec->adapter);
|
||||
dec->discont = TRUE;
|
||||
}
|
||||
|
||||
if (GST_BUFFER_SIZE (buf) % 40 != 0)
|
||||
GST_LOG_OBJECT (dec, "Got buffer with size not a multiple for frame size,"
|
||||
" ignoring last %u bytes", GST_BUFFER_SIZE (buf) % 40);
|
||||
gst_adapter_push (dec->adapter, buf);
|
||||
|
||||
ret = gst_pad_alloc_buffer_and_set_caps (dec->srcpad,
|
||||
GST_BUFFER_OFFSET (buf) * 16, size, dec->srccaps, &decoded);
|
||||
size = gst_adapter_available (dec->adapter);
|
||||
|
||||
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)
|
||||
return ret;
|
||||
goto alloc_failed;
|
||||
|
||||
GST_BUFFER_TIMESTAMP (decoded) = GST_BUFFER_TIMESTAMP (buf);
|
||||
/* get the timestamp for the output buffer */
|
||||
timestamp = gst_adapter_prev_timestamp (dec->adapter, &distance);
|
||||
|
||||
while ((in_offset + 40 <= GST_BUFFER_SIZE (buf)) && ret == GST_FLOW_OK) {
|
||||
/* 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, "Decoding frame");
|
||||
GST_LOG_OBJECT (dec,
|
||||
"timestamp %" GST_TIME_FORMAT ", distance %" G_GUINT64_FORMAT,
|
||||
GST_TIME_ARGS (timestamp), distance);
|
||||
|
||||
decode_ret = Siren7_DecodeFrame (dec->decoder,
|
||||
GST_BUFFER_DATA (buf) + in_offset,
|
||||
GST_BUFFER_DATA (decoded) + out_offset);
|
||||
if (decode_ret != 0) {
|
||||
GST_ERROR_OBJECT (dec, "Siren7_DecodeFrame returned %d", decode_ret);
|
||||
ret = GST_FLOW_ERROR;
|
||||
}
|
||||
/* 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);
|
||||
|
||||
in_offset += 40;
|
||||
out_offset += 640;
|
||||
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 : %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;
|
||||
GST_LOG_OBJECT (dec, "Finished decoding");
|
||||
|
||||
/* mark discont */
|
||||
if (dec->discont) {
|
||||
GST_BUFFER_FLAG_SET (out_buf, GST_BUFFER_FLAG_DISCONT);
|
||||
dec->discont = FALSE;
|
||||
}
|
||||
|
||||
ret = gst_pad_push (dec->srcpad, decoded);
|
||||
GST_BUFFER_TIMESTAMP (out_buf) = timestamp;
|
||||
GST_BUFFER_DURATION (out_buf) = num_frames * FRAME_DURATION;
|
||||
|
||||
gst_object_unref (dec);
|
||||
ret = gst_pad_push (dec->srcpad, out_buf);
|
||||
|
||||
done:
|
||||
if (to_free)
|
||||
g_free (to_free);
|
||||
|
||||
return ret;
|
||||
|
||||
/* ERRORS */
|
||||
alloc_failed:
|
||||
{
|
||||
GST_DEBUG_OBJECT (dec, "failed to pad_alloc buffer: %d (%d)", ret,
|
||||
gst_flow_get_name (ret));
|
||||
goto done;
|
||||
}
|
||||
decode_error:
|
||||
{
|
||||
GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL),
|
||||
("Error decoding frame: %d", decode_ret));
|
||||
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;
|
||||
}
|
||||
|
||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
gst_adapter_clear (dec->adapter);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
gboolean
|
||||
gst_siren_dec_plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
|
|
|
@ -53,10 +53,11 @@ struct _GstSirenDec
|
|||
/* Protected by stream lock */
|
||||
SirenDecoder decoder;
|
||||
|
||||
GstAdapter *adapter;
|
||||
gboolean discont;
|
||||
|
||||
GstPad *sinkpad;
|
||||
GstPad *srcpad;
|
||||
|
||||
GstCaps *srccaps;
|
||||
};
|
||||
|
||||
struct _GstSirenDecClass
|
||||
|
|
Loading…
Reference in a new issue