adpcmdec: port to audiodecoder

This commit is contained in:
Mark Nauwelaerts 2012-01-17 21:36:21 +01:00
parent 1a73bf0b79
commit dbc8bbd755
2 changed files with 96 additions and 180 deletions

View file

@ -5,8 +5,9 @@ libgstadpcmdec_la_SOURCES = adpcmdec.c
# flags used to compile this plugin # flags used to compile this plugin
# add other _CFLAGS and _LIBS as needed # add other _CFLAGS and _LIBS as needed
libgstadpcmdec_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) libgstadpcmdec_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
libgstadpcmdec_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) libgstadpcmdec_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-@GST_MAJORMINOR@ \
$(GST_LIBS)
libgstadpcmdec_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstadpcmdec_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
libgstadpcmdec_la_LIBTOOLFLAGS = --tag=disable-static libgstadpcmdec_la_LIBTOOLFLAGS = --tag=disable-static

View file

@ -28,7 +28,7 @@
#endif #endif
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/base/gstadapter.h> #include <gst/audio/gstaudiodecoder.h>
#define GST_TYPE_ADPCM_DEC \ #define GST_TYPE_ADPCM_DEC \
(adpcmdec_get_type ()) (adpcmdec_get_type ())
@ -69,80 +69,29 @@ enum adpcm_layout
typedef struct _ADPCMDecClass typedef struct _ADPCMDecClass
{ {
GstElementClass parent_class; GstAudioDecoderClass parent_class;
} ADPCMDecClass; } ADPCMDecClass;
typedef struct _ADPCMDec typedef struct _ADPCMDec
{ {
GstElement parent; GstAudioDecoder parent;
GstPad *sinkpad;
GstPad *srcpad;
GstCaps *output_caps;
enum adpcm_layout layout; enum adpcm_layout layout;
int rate; int rate;
int channels; int channels;
int blocksize; int blocksize;
gboolean is_setup;
GstClockTime timestamp;
GstClockTime base_timestamp;
guint64 out_samples;
GstAdapter *adapter;
} ADPCMDec; } ADPCMDec;
GType adpcmdec_get_type (void); GType adpcmdec_get_type (void);
GST_BOILERPLATE (ADPCMDec, adpcmdec, GstElement, GST_TYPE_ELEMENT); GST_BOILERPLATE (ADPCMDec, adpcmdec, GstAudioDecoder, GST_TYPE_AUDIO_DECODER);
static gboolean
adpcmdec_setup (ADPCMDec * dec)
{
dec->output_caps = gst_caps_new_simple ("audio/x-raw-int",
"rate", G_TYPE_INT, dec->rate,
"channels", G_TYPE_INT, dec->channels,
"width", G_TYPE_INT, 16,
"depth", G_TYPE_INT, 16,
"endianness", G_TYPE_INT, G_BYTE_ORDER,
"signed", G_TYPE_BOOLEAN, TRUE, NULL);
if (dec->output_caps) {
gst_pad_set_caps (dec->srcpad, dec->output_caps);
}
dec->is_setup = TRUE;
dec->timestamp = GST_CLOCK_TIME_NONE;
dec->base_timestamp = GST_CLOCK_TIME_NONE;
dec->adapter = gst_adapter_new ();
dec->out_samples = 0;
return TRUE;
}
static void
adpcmdec_teardown (ADPCMDec * dec)
{
if (dec->output_caps) {
gst_caps_unref (dec->output_caps);
dec->output_caps = NULL;
}
if (dec->adapter) {
g_object_unref (dec->adapter);
dec->adapter = NULL;
}
dec->is_setup = FALSE;
}
static gboolean static gboolean
adpcmdec_sink_setcaps (GstPad * pad, GstCaps * caps) adpcmdec_set_format (GstAudioDecoder * bdec, GstCaps * in_caps)
{ {
ADPCMDec *dec = (ADPCMDec *) gst_pad_get_parent (pad); ADPCMDec *dec = (ADPCMDec *) (bdec);
GstStructure *structure = gst_caps_get_structure (caps, 0); GstStructure *structure = gst_caps_get_structure (in_caps, 0);
const gchar *layout; const gchar *layout;
GstCaps *caps;
layout = gst_structure_get_string (structure, "layout"); layout = gst_structure_get_string (structure, "layout");
if (!layout) if (!layout)
@ -163,9 +112,16 @@ adpcmdec_sink_setcaps (GstPad * pad, GstCaps * caps)
if (!gst_structure_get_int (structure, "channels", &dec->channels)) if (!gst_structure_get_int (structure, "channels", &dec->channels))
return FALSE; return FALSE;
if (dec->is_setup) caps = gst_caps_new_simple ("audio/x-raw-int",
adpcmdec_teardown (dec); "rate", G_TYPE_INT, dec->rate,
gst_object_unref (dec); "channels", G_TYPE_INT, dec->channels,
"width", G_TYPE_INT, 16,
"depth", G_TYPE_INT, 16,
"endianness", G_TYPE_INT, G_BYTE_ORDER,
"signed", G_TYPE_BOOLEAN, TRUE, NULL);
gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (bdec), caps);
gst_caps_unref (caps);
return TRUE; return TRUE;
} }
@ -377,10 +333,10 @@ adpcmdec_decode_ima_block (ADPCMDec * dec, int n_samples, const guint8 * data,
return TRUE; return TRUE;
} }
static GstFlowReturn static GstBuffer *
adpcmdec_decode_block (ADPCMDec * dec, const guint8 * data, int blocksize) adpcmdec_decode_block (ADPCMDec * dec, const guint8 * data, int blocksize)
{ {
gboolean res; gboolean res = FALSE;
GstBuffer *outbuf = NULL; GstBuffer *outbuf = NULL;
int outsize; int outsize;
int samples; int samples;
@ -390,7 +346,7 @@ adpcmdec_decode_block (ADPCMDec * dec, const guint8 * data, int blocksize)
give two initial sample values per channel. Then the remainder gives give two initial sample values per channel. Then the remainder gives
two samples per byte */ two samples per byte */
if (blocksize < 7 * dec->channels) if (blocksize < 7 * dec->channels)
return GST_FLOW_ERROR; goto exit;
samples = (blocksize - 7 * dec->channels) * 2 + 2 * dec->channels; samples = (blocksize - 7 * dec->channels) * 2 + 2 * dec->channels;
outsize = 2 * samples; outsize = 2 * samples;
outbuf = gst_buffer_new_and_alloc (outsize); outbuf = gst_buffer_new_and_alloc (outsize);
@ -401,7 +357,7 @@ adpcmdec_decode_block (ADPCMDec * dec, const guint8 * data, int blocksize)
/* Each block has a 4 byte header per channel, include an initial sample. /* Each block has a 4 byte header per channel, include an initial sample.
Then the remainder gives two samples per byte */ Then the remainder gives two samples per byte */
if (blocksize < 4 * dec->channels) if (blocksize < 4 * dec->channels)
return GST_FLOW_ERROR; goto exit;
samples = (blocksize - 4 * dec->channels) * 2 + dec->channels; samples = (blocksize - 4 * dec->channels) * 2 + dec->channels;
outsize = 2 * samples; outsize = 2 * samples;
outbuf = gst_buffer_new_and_alloc (outsize); outbuf = gst_buffer_new_and_alloc (outsize);
@ -410,155 +366,114 @@ adpcmdec_decode_block (ADPCMDec * dec, const guint8 * data, int blocksize)
(gint16 *) (GST_BUFFER_DATA (outbuf))); (gint16 *) (GST_BUFFER_DATA (outbuf)));
} else { } else {
GST_WARNING_OBJECT (dec, "Unknown layout"); GST_WARNING_OBJECT (dec, "Unknown layout");
return GST_FLOW_ERROR;
} }
if (!res) { if (!res) {
gst_buffer_unref (outbuf); if (outbuf)
gst_buffer_unref (outbuf);
outbuf = NULL;
GST_WARNING_OBJECT (dec, "Decode of block failed"); GST_WARNING_OBJECT (dec, "Decode of block failed");
return GST_FLOW_ERROR;
} }
gst_buffer_set_caps (outbuf, dec->output_caps); exit:
GST_BUFFER_TIMESTAMP (outbuf) = dec->timestamp; return outbuf;
dec->out_samples += samples / dec->channels;
dec->timestamp = dec->base_timestamp +
gst_util_uint64_scale_int (dec->out_samples, GST_SECOND, dec->rate);
GST_BUFFER_DURATION (outbuf) = dec->timestamp - GST_BUFFER_TIMESTAMP (outbuf);
return gst_pad_push (dec->srcpad, outbuf);
} }
static GstFlowReturn static GstFlowReturn
adpcmdec_chain (GstPad * pad, GstBuffer * buf) adpcmdec_parse (GstAudioDecoder * bdec, GstAdapter * adapter,
gint * offset, gint * length)
{ {
ADPCMDec *dec = (ADPCMDec *) gst_pad_get_parent (pad); ADPCMDec *dec = (ADPCMDec *) (bdec);
guint size;
size = gst_adapter_available (adapter);
g_return_val_if_fail (size > 0, GST_FLOW_ERROR);
if (dec->blocksize < 0) {
/* No explicit blocksize; we just process one input buffer at a time */
*offset = 0;
*length = size;
} else {
if (size >= dec->blocksize) {
*offset = 0;
*length = dec->blocksize;
} else {
return GST_FLOW_UNEXPECTED;
}
}
return GST_FLOW_OK;
}
static GstFlowReturn
adpcmdec_handle_frame (GstAudioDecoder * bdec, GstBuffer * buffer)
{
ADPCMDec *dec = (ADPCMDec *) (bdec);
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
guint8 *data; guint8 *data;
GstBuffer *databuf = NULL; GstBuffer *outbuf = NULL;
if (!dec->is_setup) /* no fancy draining */
adpcmdec_setup (dec); if (G_UNLIKELY (!buffer))
return GST_FLOW_OK;
if (dec->base_timestamp == GST_CLOCK_TIME_NONE) { if (!dec->blocksize)
dec->base_timestamp = GST_BUFFER_TIMESTAMP (buf); return GST_FLOW_NOT_NEGOTIATED;
if (dec->base_timestamp == GST_CLOCK_TIME_NONE)
dec->base_timestamp = 0; data = GST_BUFFER_DATA (buffer);
dec->timestamp = dec->base_timestamp; outbuf = adpcmdec_decode_block (dec, data, dec->blocksize);
if (outbuf == NULL) {
GST_AUDIO_DECODER_ERROR (bdec, 1, STREAM, DECODE, (NULL),
("frame decode failed"), ret);
} }
if (dec->blocksize > 0) { if (ret == GST_FLOW_OK)
gst_adapter_push (dec->adapter, buf); ret = gst_audio_decoder_finish_frame (bdec, outbuf, 1);
while (gst_adapter_available (dec->adapter) >= dec->blocksize) {
databuf = gst_adapter_take_buffer (dec->adapter, dec->blocksize);
data = GST_BUFFER_DATA (databuf);
ret = adpcmdec_decode_block (dec, data, dec->blocksize);
/* Done with input data, free it */
gst_buffer_unref (databuf);
if (ret != GST_FLOW_OK)
goto done;
}
} else {
/* No explicit blocksize; we just process one input buffer at a time */
ret = adpcmdec_decode_block (dec, GST_BUFFER_DATA (buf),
GST_BUFFER_SIZE (buf));
gst_buffer_unref (buf);
}
done:
gst_object_unref (dec);
return ret; return ret;
} }
static gboolean static gboolean
adpcmdec_sink_event (GstPad * pad, GstEvent * event) adpcmdec_start (GstAudioDecoder * bdec)
{ {
ADPCMDec *dec = (ADPCMDec *) gst_pad_get_parent (pad); ADPCMDec *dec = (ADPCMDec *) bdec;
gboolean res;
switch (GST_EVENT_TYPE (event)) { GST_DEBUG_OBJECT (dec, "start");
case GST_EVENT_FLUSH_STOP:
dec->out_samples = 0; dec->blocksize = 0;
dec->timestamp = GST_CLOCK_TIME_NONE; dec->rate = 0;
dec->base_timestamp = GST_CLOCK_TIME_NONE; dec->channels = 0;
gst_adapter_clear (dec->adapter);
/* Fall through */ return TRUE;
default:
res = gst_pad_push_event (dec->srcpad, event);
break;
}
gst_object_unref (dec);
return res;
} }
static GstStateChangeReturn static gboolean
adpcmdec_change_state (GstElement * element, GstStateChange transition) adpcmdec_stop (GstAudioDecoder * dec)
{ {
GstStateChangeReturn ret; GST_DEBUG_OBJECT (dec, "stop");
ADPCMDec *dec = (ADPCMDec *) element;
switch (transition) { return TRUE;
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
adpcmdec_teardown (dec);
break;
case GST_STATE_CHANGE_READY_TO_NULL:
break;
default:
break;
}
return ret;
}
static void
adpcmdec_dispose (GObject * obj)
{
G_OBJECT_CLASS (parent_class)->dispose (obj);
} }
static void static void
adpcmdec_init (ADPCMDec * dec, ADPCMDecClass * klass) adpcmdec_init (ADPCMDec * dec, ADPCMDecClass * klass)
{ {
dec->sinkpad =
gst_pad_new_from_static_template (&adpcmdec_sink_template, "sink");
gst_pad_set_setcaps_function (dec->sinkpad,
GST_DEBUG_FUNCPTR (adpcmdec_sink_setcaps));
gst_pad_set_chain_function (dec->sinkpad, GST_DEBUG_FUNCPTR (adpcmdec_chain));
gst_pad_set_event_function (dec->sinkpad,
GST_DEBUG_FUNCPTR (adpcmdec_sink_event));
gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
dec->srcpad =
gst_pad_new_from_static_template (&adpcmdec_src_template, "src");
gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
} }
static void static void
adpcmdec_class_init (ADPCMDecClass * klass) adpcmdec_class_init (ADPCMDecClass * klass)
{ {
GObjectClass *gobjectclass = (GObjectClass *) klass; GstAudioDecoderClass *base_class = (GstAudioDecoderClass *) klass;
GstElementClass *gstelement_class = (GstElementClass *) klass;
gobjectclass->dispose = adpcmdec_dispose;
gstelement_class->change_state = adpcmdec_change_state;
} static void
base_class->start = GST_DEBUG_FUNCPTR (adpcmdec_start);
base_class->stop = GST_DEBUG_FUNCPTR (adpcmdec_stop);
base_class->set_format = GST_DEBUG_FUNCPTR (adpcmdec_set_format);
base_class->parse = GST_DEBUG_FUNCPTR (adpcmdec_parse);
base_class->handle_frame = GST_DEBUG_FUNCPTR (adpcmdec_handle_frame);
}
static void
adpcmdec_base_init (gpointer klass) adpcmdec_base_init (gpointer klass)
{ {
GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstElementClass *element_class = GST_ELEMENT_CLASS (klass);