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