diff options
author | Wim Taymans <wim.taymans@collabora.co.uk> | 2013-01-08 10:19:39 +0100 |
---|---|---|
committer | Wim Taymans <wim.taymans@collabora.co.uk> | 2013-01-08 10:19:39 +0100 |
commit | 4c4eadcb83e26d77e9097b3f254781218ebc260b (patch) | |
tree | af128ccc26276f358ce7ae3fcde1c3263e7b1281 | |
parent | 23e1bd0085634406a6aab9a8f3658d9f57aa9bf1 (diff) |
sbcdec: make decoder more performantarun-4101
Use an adapter to accumulate input buffers.
Decode all input in one output buffer when possible to reduce the amount of push
operations.
-rw-r--r-- | audio/gstsbcdec.c | 243 | ||||
-rw-r--r-- | audio/gstsbcdec.h | 4 |
2 files changed, 151 insertions, 96 deletions
diff --git a/audio/gstsbcdec.c b/audio/gstsbcdec.c index 1aa7a15d..88887e09 100644 --- a/audio/gstsbcdec.c +++ b/audio/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 = @@ -56,138 +60,180 @@ static GstStaticPadTemplate sbc_dec_src_factory = "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, + inoffset = 0; + outbuf = NULL; + channels = rate = 0; + + while (insize > 0) { + gint inconsumed, outlen; + gint outsize; + size_t outconsumed; + + if (outbuf == NULL) { + res = gst_pad_alloc_buffer_and_set_caps(dec->srcpad, GST_BUFFER_OFFSET_NONE, - codesize, NULL, &output); + BUF_SIZE, NULL, &outbuf); + + if (res != GST_FLOW_OK) + goto done; - if (res != GST_FLOW_OK) - goto done; + if (discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + discont = FALSE; + } - consumed = sbc_decode(&dec->sbc, data + offset, size - offset, - GST_BUFFER_DATA(output), codesize, - NULL); - GST_INFO_OBJECT (dec, "consumed %d bytes", consumed); + GST_BUFFER_TIMESTAMP (outbuf) = dec->next_timestamp; + outdata = GST_BUFFER_DATA (outbuf); + outsize = GST_BUFFER_SIZE (outbuf); + outoffset = 0; + } - if (consumed <= 0) { - offset += sbc_get_frame_length (&dec->sbc); + 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); - } if (dec->next_sample != (guint64) -1) { - /* reconstruct timestamp from our sample counter otherwise */ - timestamp = gst_util_uint64_scale_int (dec->next_sample, - GST_SECOND, rate); + /* lock onto timestamp when we have one */ + dec->next_sample = gst_util_uint64_scale_int (timestamp, + rate, GST_SECOND); + timestamp = GST_CLOCK_TIME_NONE; } - 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; + /* 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_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, +static GstStateChangeReturn gst_sbc_dec_change_state(GstElement *element, GstStateChange transition) { GstStateChangeReturn result; @@ -196,10 +242,6 @@ static GstStateChangeReturn sbc_dec_change_state(GstElement *element, 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; @@ -213,10 +255,7 @@ static GstStateChangeReturn sbc_dec_change_state(GstElement *element, 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); @@ -246,11 +285,14 @@ static void 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"); @@ -268,9 +310,20 @@ static void gst_sbc_dec_init(GstSbcDec *self, GstSbcDecClass *klass) &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) { return gst_element_register(plugin, "sbcdec", GST_RANK_PRIMARY, diff --git a/audio/gstsbcdec.h b/audio/gstsbcdec.h index a62e61b2..f5b9416a 100644 --- a/audio/gstsbcdec.h +++ b/audio/gstsbcdec.h @@ -22,6 +22,7 @@ */ #include <gst/gst.h> +#include <gst/base/gstadapter.h> #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 { |