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.
This commit is contained in:
Wim Taymans 2013-01-08 10:19:39 +01:00 committed by Tim-Philipp Müller
parent dcb57780ef
commit 91f4b15490
2 changed files with 145 additions and 89 deletions

View file

@ -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;
inoffset = 0;
outbuf = NULL;
channels = rate = 0;
res = gst_pad_alloc_buffer_and_set_caps (dec->srcpad,
GST_BUFFER_OFFSET_NONE, codesize, NULL, &output);
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;
/* 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;
/* 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);
outbuf = NULL;
}
gst_buffer_set_caps (output, dec->outcaps);
if (discont) {
GST_BUFFER_FLAG_SET (output, GST_BUFFER_FLAG_DISCONT);
discont = FALSE;
}
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)
{

View file

@ -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 {