mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-03 05:59:10 +00:00
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:
parent
dcb57780ef
commit
91f4b15490
2 changed files with 145 additions and 89 deletions
|
@ -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 =
|
||||
|
@ -55,97 +59,16 @@ GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
|
|||
"signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16"));
|
||||
|
||||
static GstFlowReturn
|
||||
sbc_dec_chain (GstPad * pad, GstBuffer * buffer)
|
||||
gst_sbc_dec_flush (GstSbcDec * dec, GstBuffer * outbuf,
|
||||
gint outoffset, gint channels, gint rate)
|
||||
{
|
||||
GstSbcDec *dec = GST_SBC_DEC (gst_pad_get_parent (pad));
|
||||
GstFlowReturn res = GST_FLOW_OK;
|
||||
guint size, codesize, offset = 0;
|
||||
guint8 *data;
|
||||
GstClockTime timestamp;
|
||||
gboolean discont;
|
||||
|
||||
codesize = sbc_get_codesize (&dec->sbc);
|
||||
|
||||
discont = GST_BUFFER_IS_DISCONT (buffer);
|
||||
if (discont) {
|
||||
/* reset previous buffer */
|
||||
gst_buffer_unref (dec->buffer);
|
||||
dec->buffer = NULL;
|
||||
/* 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);
|
||||
|
||||
timestamp = GST_BUFFER_TIMESTAMP (buffer);
|
||||
|
||||
|
||||
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);
|
||||
|
||||
if (res != GST_FLOW_OK)
|
||||
goto done;
|
||||
|
||||
consumed = sbc_decode (&dec->sbc, data + offset, size - offset,
|
||||
GST_BUFFER_DATA (output), codesize, NULL);
|
||||
GST_INFO_OBJECT (dec, "consumed %d bytes", consumed);
|
||||
|
||||
if (consumed <= 0) {
|
||||
offset += sbc_get_frame_length (&dec->sbc);
|
||||
continue;
|
||||
}
|
||||
|
||||
rate = gst_sbc_parse_rate_from_sbc (dec->sbc.frequency);
|
||||
channels = gst_sbc_get_channel_number (dec->sbc.mode);
|
||||
|
||||
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);
|
||||
}
|
||||
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;
|
||||
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);
|
||||
|
||||
|
@ -158,32 +81,155 @@ sbc_dec_chain (GstPad * pad, GstBuffer * buffer)
|
|||
gst_object_unref (template);
|
||||
}
|
||||
|
||||
gst_buffer_set_caps (output, dec->outcaps);
|
||||
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);
|
||||
|
||||
if (discont) {
|
||||
GST_BUFFER_FLAG_SET (output, GST_BUFFER_FLAG_DISCONT);
|
||||
discont = FALSE;
|
||||
}
|
||||
|
||||
res = gst_pad_push (dec->srcpad, output);
|
||||
static GstFlowReturn
|
||||
sbc_dec_chain (GstPad * pad, GstBuffer * buffer)
|
||||
{
|
||||
GstSbcDec *dec = GST_SBC_DEC (gst_pad_get_parent (pad));
|
||||
GstFlowReturn res = GST_FLOW_OK;
|
||||
const guint8 *indata;
|
||||
guint insize;
|
||||
GstClockTime timestamp;
|
||||
gboolean discont;
|
||||
GstBuffer *outbuf;
|
||||
guint8 *outdata;
|
||||
guint inoffset, outoffset;
|
||||
gint rate, channels;
|
||||
|
||||
discont = GST_BUFFER_IS_DISCONT (buffer);
|
||||
if (discont) {
|
||||
/* reset previous buffer */
|
||||
gst_adapter_clear (dec->adapter);
|
||||
/* we need a new timestamp to lock onto */
|
||||
dec->next_sample = -1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
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, BUF_SIZE, NULL, &outbuf);
|
||||
|
||||
if (res != GST_FLOW_OK)
|
||||
goto done;
|
||||
|
||||
offset += consumed;
|
||||
if (discont) {
|
||||
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
|
||||
discont = FALSE;
|
||||
}
|
||||
|
||||
if (offset < size)
|
||||
dec->buffer = gst_buffer_create_sub (buffer, offset, size - offset);
|
||||
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) {
|
||||
/* calculate the next sample */
|
||||
dec->next_sample += outconsumed / (2 * channels);
|
||||
dec->next_timestamp = gst_util_uint64_scale_int (dec->next_sample,
|
||||
GST_SECOND, rate);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
outbuf = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue