From 91f4b15490d3006afaf91482e3af737acdba5e8b Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 8 Jan 2013 10:19:39 +0100 Subject: sbc: sbcdec: make decoder more performant Use an adapter to accumulate input buffers. Decode all input in one output buffer when possible to reduce the amount of push operations. --- ext/sbc/gstsbcdec.c | 230 ++++++++++++++++++++++++++++++++-------------------- ext/sbc/gstsbcdec.h | 4 +- 2 files changed, 145 insertions(+), 89 deletions(-) diff --git a/ext/sbc/gstsbcdec.c b/ext/sbc/gstsbcdec.c index 808344617..12245f9d7 100644 --- a/ext/sbc/gstsbcdec.c +++ b/ext/sbc/gstsbcdec.c @@ -31,9 +31,13 @@ #include "gstsbcutil.h" #include "gstsbcdec.h" +#define BUF_SIZE 8192 + GST_DEBUG_CATEGORY_STATIC (sbc_dec_debug); #define GST_CAT_DEFAULT sbc_dec_debug +static void gst_sbc_dec_finalize (GObject * obj); + GST_BOILERPLATE (GstSbcDec, gst_sbc_dec, GstElement, GST_TYPE_ELEMENT); static const GstElementDetails sbc_dec_details = @@ -54,136 +58,178 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, "endianness = (int) BYTE_ORDER, " "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16")); +static GstFlowReturn +gst_sbc_dec_flush (GstSbcDec * dec, GstBuffer * outbuf, + gint outoffset, gint channels, gint rate) +{ + GstClockTime outtime, duration; + + /* we will reuse the same caps object */ + if (dec->outcaps == NULL) { + GstCaps *caps; + GstPadTemplate *template; + + caps = gst_caps_new_simple ("audio/x-raw-int", + "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL); + + template = gst_static_pad_template_get (&sbc_dec_src_factory); + + dec->outcaps = gst_caps_intersect (caps, + gst_pad_template_get_caps (template)); + + gst_caps_unref (caps); + gst_object_unref (template); + } + + gst_buffer_set_caps (outbuf, dec->outcaps); + + /* calculate duration */ + outtime = GST_BUFFER_TIMESTAMP (outbuf); + if (dec->next_timestamp != (guint64) - 1 && outtime != (guint64) - 1) { + duration = dec->next_timestamp - outtime; + } else if (outtime != (guint64) - 1) { + /* otherwise calculate duration based on outbuf size */ + duration = gst_util_uint64_scale_int (outoffset / (2 * channels), + GST_SECOND, rate) - outtime; + } else { + duration = GST_CLOCK_TIME_NONE; + } + GST_BUFFER_DURATION (outbuf) = duration; + GST_BUFFER_SIZE (outbuf) = outoffset; + + return gst_pad_push (dec->srcpad, outbuf); + +} + static GstFlowReturn sbc_dec_chain (GstPad * pad, GstBuffer * buffer) { GstSbcDec *dec = GST_SBC_DEC (gst_pad_get_parent (pad)); GstFlowReturn res = GST_FLOW_OK; - guint size, codesize, offset = 0; - guint8 *data; + const guint8 *indata; + guint insize; GstClockTime timestamp; gboolean discont; - - codesize = sbc_get_codesize (&dec->sbc); + GstBuffer *outbuf; + guint8 *outdata; + guint inoffset, outoffset; + gint rate, channels; discont = GST_BUFFER_IS_DISCONT (buffer); if (discont) { /* reset previous buffer */ - gst_buffer_unref (dec->buffer); - dec->buffer = NULL; + gst_adapter_clear (dec->adapter); /* we need a new timestamp to lock onto */ dec->next_sample = -1; } - if (dec->buffer) { - GstBuffer *temp = buffer; - buffer = gst_buffer_span (dec->buffer, 0, buffer, - GST_BUFFER_SIZE (dec->buffer) + GST_BUFFER_SIZE (buffer)); - gst_buffer_unref (temp); - gst_buffer_unref (dec->buffer); - dec->buffer = NULL; - } - - data = GST_BUFFER_DATA (buffer); - size = GST_BUFFER_SIZE (buffer); + gst_adapter_push (dec->adapter, buffer); timestamp = GST_BUFFER_TIMESTAMP (buffer); + if (GST_CLOCK_TIME_IS_VALID (timestamp)) + dec->next_timestamp = timestamp; + insize = gst_adapter_available (dec->adapter); + indata = gst_adapter_peek (dec->adapter, insize); - while (offset < size) { - GstBuffer *output; - GstPadTemplate *template; - GstCaps *caps; - int consumed; - GstClockTime duration; - gint rate, channels; - res = gst_pad_alloc_buffer_and_set_caps (dec->srcpad, - GST_BUFFER_OFFSET_NONE, codesize, NULL, &output); + inoffset = 0; + outbuf = NULL; + channels = rate = 0; + + while (insize > 0) { + gint inconsumed, outlen; + gint outsize; + size_t outconsumed; - if (res != GST_FLOW_OK) - goto done; + if (outbuf == NULL) { + res = gst_pad_alloc_buffer_and_set_caps (dec->srcpad, + GST_BUFFER_OFFSET_NONE, BUF_SIZE, NULL, &outbuf); - consumed = sbc_decode (&dec->sbc, data + offset, size - offset, - GST_BUFFER_DATA (output), codesize, NULL); - GST_INFO_OBJECT (dec, "consumed %d bytes", consumed); + if (res != GST_FLOW_OK) + goto done; - if (consumed <= 0) { - offset += sbc_get_frame_length (&dec->sbc); + if (discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + discont = FALSE; + } + + GST_BUFFER_TIMESTAMP (outbuf) = dec->next_timestamp; + outdata = GST_BUFFER_DATA (outbuf); + outsize = GST_BUFFER_SIZE (outbuf); + outoffset = 0; + } + + GST_INFO_OBJECT (dec, "inoffset %d/%d, outoffset %d/%d", inoffset, + insize, outoffset, outsize); + + inconsumed = sbc_decode (&dec->sbc, indata + inoffset, insize, + outdata + outoffset, outsize, &outconsumed); + + GST_INFO_OBJECT (dec, "consumed %d, produced %d", inconsumed, outconsumed); + + if (inconsumed <= 0) { + guint frame_len = sbc_get_frame_length (&dec->sbc); + /* skip a frame */ + if (insize > frame_len) { + insize -= frame_len; + inoffset += frame_len; + } else { + insize = 0; + } continue; } + inoffset += inconsumed; + if ((gint) insize > inconsumed) + insize -= inconsumed; + else + insize = 0; + outoffset += outconsumed; + outsize -= outconsumed; + rate = gst_sbc_parse_rate_from_sbc (dec->sbc.frequency); channels = gst_sbc_get_channel_number (dec->sbc.mode); + /* calculate timestamp either from the incomming buffers or + * from our sample counter */ if (GST_CLOCK_TIME_IS_VALID (timestamp)) { /* lock onto timestamp when we have one */ dec->next_sample = gst_util_uint64_scale_int (timestamp, rate, GST_SECOND); + timestamp = GST_CLOCK_TIME_NONE; } if (dec->next_sample != (guint64) - 1) { - /* reconstruct timestamp from our sample counter otherwise */ - timestamp = gst_util_uint64_scale_int (dec->next_sample, + /* calculate the next sample */ + dec->next_sample += outconsumed / (2 * channels); + dec->next_timestamp = gst_util_uint64_scale_int (dec->next_sample, GST_SECOND, rate); } - GST_BUFFER_TIMESTAMP (output) = timestamp; - - /* calculate the next sample */ - if (dec->next_sample != (guint64) - 1) { - /* we ave a valid sample, counter, increment it. */ - dec->next_sample += codesize / (2 * channels); - duration = gst_util_uint64_scale_int (dec->next_sample, - GST_SECOND, rate) - timestamp; - } else { - /* otherwise calculate duration based on output size */ - duration = gst_util_uint64_scale_int (codesize / (2 * channels), - GST_SECOND, rate) - timestamp; - } - GST_BUFFER_DURATION (output) = duration; - - /* reset timestamp for next round */ - timestamp = GST_CLOCK_TIME_NONE; - - /* we will reuse the same caps object */ - if (dec->outcaps == NULL) { - caps = gst_caps_new_simple ("audio/x-raw-int", - "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, channels, NULL); - - template = gst_static_pad_template_get (&sbc_dec_src_factory); - - dec->outcaps = gst_caps_intersect (caps, - gst_pad_template_get_caps (template)); - - gst_caps_unref (caps); - gst_object_unref (template); - } - gst_buffer_set_caps (output, dec->outcaps); + /* check for space, push outbuf buffer */ + outlen = sbc_get_codesize (&dec->sbc); + if (outsize < outlen) { + res = gst_sbc_dec_flush (dec, outbuf, outoffset, channels, rate); + if (res != GST_FLOW_OK) + goto done; - if (discont) { - GST_BUFFER_FLAG_SET (output, GST_BUFFER_FLAG_DISCONT); - discont = FALSE; + outbuf = NULL; } - res = gst_pad_push (dec->srcpad, output); - if (res != GST_FLOW_OK) - goto done; - - offset += consumed; } - if (offset < size) - dec->buffer = gst_buffer_create_sub (buffer, offset, size - offset); + if (outbuf) + res = gst_sbc_dec_flush (dec, outbuf, outoffset, channels, rate); + gst_adapter_flush (dec->adapter, inoffset); done: - gst_buffer_unref (buffer); gst_object_unref (dec); return res; } static GstStateChangeReturn -sbc_dec_change_state (GstElement * element, GstStateChange transition) +gst_sbc_dec_change_state (GstElement * element, GstStateChange transition) { GstStateChangeReturn result; GstSbcDec *dec = GST_SBC_DEC (element); @@ -191,10 +237,6 @@ sbc_dec_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_READY_TO_PAUSED: GST_DEBUG ("Setup subband codec"); - if (dec->buffer) { - gst_buffer_unref (dec->buffer); - dec->buffer = NULL; - } sbc_init (&dec->sbc, 0); dec->outcaps = NULL; dec->next_sample = -1; @@ -208,10 +250,7 @@ sbc_dec_change_state (GstElement * element, GstStateChange transition) switch (transition) { case GST_STATE_CHANGE_PAUSED_TO_READY: GST_DEBUG ("Finish subband codec"); - if (dec->buffer) { - gst_buffer_unref (dec->buffer); - dec->buffer = NULL; - } + gst_adapter_clear (dec->adapter); sbc_finish (&dec->sbc); if (dec->outcaps) { gst_caps_unref (dec->outcaps); @@ -243,11 +282,14 @@ gst_sbc_dec_base_init (gpointer g_class) static void gst_sbc_dec_class_init (GstSbcDecClass * klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass); parent_class = g_type_class_peek_parent (klass); - element_class->change_state = GST_DEBUG_FUNCPTR (sbc_dec_change_state); + object_class->finalize = GST_DEBUG_FUNCPTR (gst_sbc_dec_finalize); + + element_class->change_state = GST_DEBUG_FUNCPTR (gst_sbc_dec_change_state); GST_DEBUG_CATEGORY_INIT (sbc_dec_debug, "sbcdec", 0, "SBC decoding element"); } @@ -263,9 +305,21 @@ gst_sbc_dec_init (GstSbcDec * self, GstSbcDecClass * klass) self->srcpad = gst_pad_new_from_static_template (&sbc_dec_src_factory, "src"); gst_element_add_pad (GST_ELEMENT (self), self->srcpad); + self->adapter = gst_adapter_new (); self->outcaps = NULL; } +static void +gst_sbc_dec_finalize (GObject * obj) +{ + GstSbcDec *self = GST_SBC_DEC (obj); + + g_object_unref (self->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (obj); + +} + gboolean gst_sbc_dec_plugin_init (GstPlugin * plugin) { diff --git a/ext/sbc/gstsbcdec.h b/ext/sbc/gstsbcdec.h index a62e61b2f..f5b9416a9 100644 --- a/ext/sbc/gstsbcdec.h +++ b/ext/sbc/gstsbcdec.h @@ -22,6 +22,7 @@ */ #include +#include #include "sbc.h" @@ -47,13 +48,14 @@ struct _GstSbcDec { GstPad *sinkpad; GstPad *srcpad; - GstBuffer *buffer; + GstAdapter *adapter; /* caps for outgoing buffers */ GstCaps *outcaps; sbc_t sbc; guint64 next_sample; + guint64 next_timestamp; }; struct _GstSbcDecClass { -- cgit v1.2.1