adpcmenc: port to audioencoder
This commit is contained in:
parent
bc1c77395e
commit
1a73bf0b79
@ -5,8 +5,9 @@ libgstadpcmenc_la_SOURCES = adpcmenc.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
|
||||||
libgstadpcmenc_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS)
|
libgstadpcmenc_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_CFLAGS)
|
||||||
libgstadpcmenc_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS)
|
libgstadpcmenc_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) -lgstaudio-@GST_MAJORMINOR@ \
|
||||||
|
$(GST_LIBS)
|
||||||
libgstadpcmenc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
libgstadpcmenc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||||
libgstadpcmenc_la_LIBTOOLFLAGS = --tag=disable-static
|
libgstadpcmenc_la_LIBTOOLFLAGS = --tag=disable-static
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/base/gstadapter.h>
|
#include <gst/audio/gstaudioencoder.h>
|
||||||
|
|
||||||
#define GST_TYPE_ADPCM_ENC \
|
#define GST_TYPE_ADPCM_ENC \
|
||||||
(adpcmenc_get_type ())
|
(adpcmenc_get_type ())
|
||||||
@ -113,17 +113,12 @@ adpcmenc_layout_get_type (void)
|
|||||||
|
|
||||||
typedef struct _ADPCMEncClass
|
typedef struct _ADPCMEncClass
|
||||||
{
|
{
|
||||||
GstElementClass parent_class;
|
GstAudioEncoderClass parent_class;
|
||||||
} ADPCMEncClass;
|
} ADPCMEncClass;
|
||||||
|
|
||||||
typedef struct _ADPCMEnc
|
typedef struct _ADPCMEnc
|
||||||
{
|
{
|
||||||
GstElement parent;
|
GstAudioEncoder parent;
|
||||||
|
|
||||||
GstPad *sinkpad;
|
|
||||||
GstPad *srcpad;
|
|
||||||
|
|
||||||
GstCaps *output_caps;
|
|
||||||
|
|
||||||
enum adpcm_layout layout;
|
enum adpcm_layout layout;
|
||||||
int rate;
|
int rate;
|
||||||
@ -133,19 +128,11 @@ typedef struct _ADPCMEnc
|
|||||||
|
|
||||||
guint8 step_index[2];
|
guint8 step_index[2];
|
||||||
|
|
||||||
gboolean is_setup;
|
|
||||||
|
|
||||||
GstClockTime timestamp;
|
|
||||||
GstClockTime base_timestamp;
|
|
||||||
|
|
||||||
guint64 out_samples;
|
|
||||||
|
|
||||||
GstAdapter *adapter;
|
|
||||||
|
|
||||||
} ADPCMEnc;
|
} ADPCMEnc;
|
||||||
|
|
||||||
GType adpcmenc_get_type (void);
|
GType adpcmenc_get_type (void);
|
||||||
GST_BOILERPLATE (ADPCMEnc, adpcmenc, GstElement, GST_TYPE_ELEMENT);
|
GST_BOILERPLATE (ADPCMEnc, adpcmenc, GstAudioEncoder, GST_TYPE_AUDIO_ENCODER);
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
adpcmenc_setup (ADPCMEnc * enc)
|
adpcmenc_setup (ADPCMEnc * enc)
|
||||||
{
|
{
|
||||||
@ -153,6 +140,7 @@ adpcmenc_setup (ADPCMEnc * enc)
|
|||||||
const int ADPCM_SAMPLES_PER_BYTE = 2;
|
const int ADPCM_SAMPLES_PER_BYTE = 2;
|
||||||
guint64 sample_bytes;
|
guint64 sample_bytes;
|
||||||
const char *layout;
|
const char *layout;
|
||||||
|
GstCaps *caps;
|
||||||
|
|
||||||
switch (enc->layout) {
|
switch (enc->layout) {
|
||||||
case LAYOUT_ADPCM_DVI:
|
case LAYOUT_ADPCM_DVI:
|
||||||
@ -168,21 +156,14 @@ adpcmenc_setup (ADPCMEnc * enc)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
enc->output_caps = gst_caps_new_simple ("audio/x-adpcm",
|
caps = gst_caps_new_simple ("audio/x-adpcm",
|
||||||
"rate", G_TYPE_INT, enc->rate,
|
"rate", G_TYPE_INT, enc->rate,
|
||||||
"channels", G_TYPE_INT, enc->channels,
|
"channels", G_TYPE_INT, enc->channels,
|
||||||
"layout", G_TYPE_STRING, layout,
|
"layout", G_TYPE_STRING, layout,
|
||||||
"block_align", G_TYPE_INT, enc->blocksize, NULL);
|
"block_align", G_TYPE_INT, enc->blocksize, NULL);
|
||||||
|
|
||||||
if (enc->output_caps) {
|
gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), caps);
|
||||||
gst_pad_set_caps (enc->srcpad, enc->output_caps);
|
gst_caps_unref (caps);
|
||||||
}
|
|
||||||
|
|
||||||
enc->is_setup = TRUE;
|
|
||||||
enc->timestamp = GST_CLOCK_TIME_NONE;
|
|
||||||
enc->base_timestamp = GST_CLOCK_TIME_NONE;
|
|
||||||
enc->adapter = gst_adapter_new ();
|
|
||||||
enc->out_samples = 0;
|
|
||||||
|
|
||||||
/* Step index state is carried between blocks. */
|
/* Step index state is carried between blocks. */
|
||||||
enc->step_index[0] = 0;
|
enc->step_index[0] = 0;
|
||||||
@ -191,37 +172,21 @@ adpcmenc_setup (ADPCMEnc * enc)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
adpcmenc_teardown (ADPCMEnc * enc)
|
|
||||||
{
|
|
||||||
if (enc->output_caps) {
|
|
||||||
gst_caps_unref (enc->output_caps);
|
|
||||||
enc->output_caps = NULL;
|
|
||||||
}
|
|
||||||
if (enc->adapter) {
|
|
||||||
g_object_unref (enc->adapter);
|
|
||||||
enc->adapter = NULL;
|
|
||||||
}
|
|
||||||
enc->is_setup = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
adpcmenc_sink_setcaps (GstPad * pad, GstCaps * caps)
|
adpcmenc_set_format (GstAudioEncoder * benc, GstAudioInfo * info)
|
||||||
{
|
{
|
||||||
ADPCMEnc *enc = (ADPCMEnc *) gst_pad_get_parent (pad);
|
ADPCMEnc *enc = (ADPCMEnc *) (benc);
|
||||||
GstStructure *structure = gst_caps_get_structure (caps, 0);
|
|
||||||
|
|
||||||
if (!gst_structure_get_int (structure, "rate", &enc->rate))
|
enc->rate = GST_AUDIO_INFO_RATE (info);
|
||||||
return FALSE;
|
enc->channels = GST_AUDIO_INFO_CHANNELS (info);
|
||||||
if (!gst_structure_get_int (structure, "channels", &enc->channels))
|
|
||||||
|
if (!adpcmenc_setup (enc))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (enc->is_setup) {
|
/* report needs to base class */
|
||||||
adpcmenc_teardown (enc);
|
gst_audio_encoder_set_frame_samples_min (benc, enc->samples_per_block);
|
||||||
}
|
gst_audio_encoder_set_frame_samples_max (benc, enc->samples_per_block);
|
||||||
adpcmenc_setup (enc);
|
gst_audio_encoder_set_frame_max (benc, 1);
|
||||||
|
|
||||||
gst_object_unref (enc);
|
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -368,148 +333,86 @@ adpcmenc_encode_ima_block (ADPCMEnc * enc, const gint16 * samples,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstBuffer *
|
||||||
adpcmenc_encode_block (ADPCMEnc * enc, const gint16 * samples, int blocksize)
|
adpcmenc_encode_block (ADPCMEnc * enc, const gint16 * samples, int blocksize)
|
||||||
{
|
{
|
||||||
gboolean res;
|
gboolean res = FALSE;
|
||||||
GstBuffer *outbuf = NULL;
|
GstBuffer *outbuf = NULL;
|
||||||
|
|
||||||
if (enc->layout == LAYOUT_ADPCM_DVI) {
|
if (enc->layout == LAYOUT_ADPCM_DVI) {
|
||||||
outbuf = gst_buffer_new_and_alloc (enc->blocksize);
|
outbuf = gst_buffer_new_and_alloc (enc->blocksize);
|
||||||
res = adpcmenc_encode_ima_block (enc, samples, GST_BUFFER_DATA (outbuf));
|
res = adpcmenc_encode_ima_block (enc, samples, GST_BUFFER_DATA (outbuf));
|
||||||
} else {
|
} else {
|
||||||
|
/* should not happen afaics */
|
||||||
|
g_assert_not_reached ();
|
||||||
GST_WARNING_OBJECT (enc, "Unknown layout");
|
GST_WARNING_OBJECT (enc, "Unknown layout");
|
||||||
return GST_FLOW_ERROR;
|
res = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
gst_buffer_unref (outbuf);
|
if (outbuf)
|
||||||
|
gst_buffer_unref (outbuf);
|
||||||
|
outbuf = NULL;
|
||||||
GST_WARNING_OBJECT (enc, "Encode of block failed");
|
GST_WARNING_OBJECT (enc, "Encode of block failed");
|
||||||
return GST_FLOW_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_buffer_set_caps (outbuf, enc->output_caps);
|
return outbuf;
|
||||||
GST_BUFFER_TIMESTAMP (outbuf) = enc->timestamp;
|
|
||||||
|
|
||||||
enc->out_samples += enc->samples_per_block;
|
|
||||||
enc->timestamp = enc->base_timestamp +
|
|
||||||
gst_util_uint64_scale_int (enc->out_samples, GST_SECOND, enc->rate);
|
|
||||||
GST_BUFFER_DURATION (outbuf) = enc->timestamp - GST_BUFFER_TIMESTAMP (outbuf);
|
|
||||||
|
|
||||||
return gst_pad_push (enc->srcpad, outbuf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstFlowReturn
|
static GstFlowReturn
|
||||||
adpcmenc_chain (GstPad * pad, GstBuffer * buf)
|
adpcmenc_handle_frame (GstAudioEncoder * benc, GstBuffer * buffer)
|
||||||
{
|
{
|
||||||
ADPCMEnc *enc = (ADPCMEnc *) gst_pad_get_parent (pad);
|
ADPCMEnc *enc = (ADPCMEnc *) (benc);
|
||||||
GstFlowReturn ret = GST_FLOW_OK;
|
GstFlowReturn ret = GST_FLOW_OK;
|
||||||
gint16 *samples;
|
gint16 *samples;
|
||||||
GstBuffer *databuf = NULL;
|
GstBuffer *outbuf;
|
||||||
int input_bytes_per_block;
|
int input_bytes_per_block;
|
||||||
const int BYTES_PER_SAMPLE = 2;
|
const int BYTES_PER_SAMPLE = 2;
|
||||||
|
|
||||||
if (enc->base_timestamp == GST_CLOCK_TIME_NONE) {
|
/* we don't deal with squeezing remnants, so simply discard those */
|
||||||
enc->base_timestamp = GST_BUFFER_TIMESTAMP (buf);
|
if (G_UNLIKELY (buffer == NULL)) {
|
||||||
if (enc->base_timestamp == GST_CLOCK_TIME_NONE)
|
GST_DEBUG_OBJECT (benc, "no data");
|
||||||
enc->base_timestamp = 0;
|
goto done;
|
||||||
enc->timestamp = enc->base_timestamp;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gst_adapter_push (enc->adapter, buf);
|
|
||||||
|
|
||||||
input_bytes_per_block =
|
input_bytes_per_block =
|
||||||
enc->samples_per_block * BYTES_PER_SAMPLE * enc->channels;
|
enc->samples_per_block * BYTES_PER_SAMPLE * enc->channels;
|
||||||
|
|
||||||
while (gst_adapter_available (enc->adapter) >= input_bytes_per_block) {
|
if (G_UNLIKELY (GST_BUFFER_SIZE (buffer) < input_bytes_per_block)) {
|
||||||
databuf = gst_adapter_take_buffer (enc->adapter, input_bytes_per_block);
|
GST_DEBUG_OBJECT (enc, "discarding trailing data %d",
|
||||||
samples = (gint16 *) GST_BUFFER_DATA (databuf);
|
GST_BUFFER_SIZE (buffer));
|
||||||
ret = adpcmenc_encode_block (enc, samples, enc->blocksize);
|
ret = gst_audio_encoder_finish_frame (benc, NULL, -1);
|
||||||
gst_buffer_unref (databuf);
|
goto done;
|
||||||
if (ret != GST_FLOW_OK)
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
samples = (gint16 *) GST_BUFFER_DATA (buffer);
|
||||||
|
outbuf = adpcmenc_encode_block (enc, samples, enc->blocksize);
|
||||||
|
|
||||||
|
ret = gst_audio_encoder_finish_frame (benc, outbuf, enc->samples_per_block);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
gst_object_unref (enc);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
adpcmenc_sink_event (GstPad * pad, GstEvent * event)
|
adpcmenc_start (GstAudioEncoder * enc)
|
||||||
{
|
{
|
||||||
ADPCMEnc *enc = (ADPCMEnc *) gst_pad_get_parent (pad);
|
GST_DEBUG_OBJECT (enc, "start");
|
||||||
gboolean res;
|
|
||||||
switch (GST_EVENT_TYPE (event)) {
|
return TRUE;
|
||||||
case GST_EVENT_FLUSH_STOP:
|
|
||||||
enc->out_samples = 0;
|
|
||||||
enc->timestamp = GST_CLOCK_TIME_NONE;
|
|
||||||
enc->base_timestamp = GST_CLOCK_TIME_NONE;
|
|
||||||
gst_adapter_clear (enc->adapter);
|
|
||||||
/* Fall through */
|
|
||||||
default:
|
|
||||||
res = gst_pad_push_event (enc->srcpad, event);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
gst_object_unref (enc);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstStateChangeReturn
|
static gboolean
|
||||||
adpcmenc_change_state (GstElement * element, GstStateChange transition)
|
adpcmenc_stop (GstAudioEncoder * enc)
|
||||||
{
|
{
|
||||||
GstStateChangeReturn ret;
|
GST_DEBUG_OBJECT (enc, "stop");
|
||||||
ADPCMEnc *enc = (ADPCMEnc *) 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:
|
|
||||||
adpcmenc_teardown (enc);
|
|
||||||
break;
|
|
||||||
case GST_STATE_CHANGE_READY_TO_NULL:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
adpcmenc_dispose (GObject * obj)
|
|
||||||
{
|
|
||||||
G_OBJECT_CLASS (parent_class)->dispose (obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
adpcmenc_init (ADPCMEnc * enc, ADPCMEncClass * klass)
|
adpcmenc_init (ADPCMEnc * enc, ADPCMEncClass * klass)
|
||||||
{
|
{
|
||||||
enc->sinkpad =
|
|
||||||
gst_pad_new_from_static_template (&adpcmenc_sink_template, "sink");
|
|
||||||
gst_pad_set_setcaps_function (enc->sinkpad,
|
|
||||||
GST_DEBUG_FUNCPTR (adpcmenc_sink_setcaps));
|
|
||||||
gst_pad_set_chain_function (enc->sinkpad, GST_DEBUG_FUNCPTR (adpcmenc_chain));
|
|
||||||
gst_pad_set_event_function (enc->sinkpad,
|
|
||||||
GST_DEBUG_FUNCPTR (adpcmenc_sink_event));
|
|
||||||
gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad);
|
|
||||||
|
|
||||||
enc->srcpad =
|
|
||||||
gst_pad_new_from_static_template (&adpcmenc_src_template, "src");
|
|
||||||
gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad);
|
|
||||||
|
|
||||||
/* Set defaults. */
|
/* Set defaults. */
|
||||||
enc->blocksize = DEFAULT_ADPCM_BLOCK_SIZE;
|
enc->blocksize = DEFAULT_ADPCM_BLOCK_SIZE;
|
||||||
enc->layout = DEFAULT_ADPCM_LAYOUT;
|
enc->layout = DEFAULT_ADPCM_LAYOUT;
|
||||||
@ -519,11 +422,16 @@ static void
|
|||||||
adpcmenc_class_init (ADPCMEncClass * klass)
|
adpcmenc_class_init (ADPCMEncClass * klass)
|
||||||
{
|
{
|
||||||
GObjectClass *gobjectclass = (GObjectClass *) klass;
|
GObjectClass *gobjectclass = (GObjectClass *) klass;
|
||||||
GstElementClass *gstelement_class = (GstElementClass *) klass;
|
GstAudioEncoderClass *base_class = (GstAudioEncoderClass *) klass;
|
||||||
|
|
||||||
gobjectclass->set_property = adpcmenc_set_property;
|
gobjectclass->set_property = adpcmenc_set_property;
|
||||||
gobjectclass->get_property = adpcmenc_get_property;
|
gobjectclass->get_property = adpcmenc_get_property;
|
||||||
|
|
||||||
|
base_class->start = GST_DEBUG_FUNCPTR (adpcmenc_start);
|
||||||
|
base_class->stop = GST_DEBUG_FUNCPTR (adpcmenc_stop);
|
||||||
|
base_class->set_format = GST_DEBUG_FUNCPTR (adpcmenc_set_format);
|
||||||
|
base_class->handle_frame = GST_DEBUG_FUNCPTR (adpcmenc_handle_frame);
|
||||||
|
|
||||||
g_object_class_install_property (gobjectclass, ARG_LAYOUT,
|
g_object_class_install_property (gobjectclass, ARG_LAYOUT,
|
||||||
g_param_spec_enum ("layout", "Layout",
|
g_param_spec_enum ("layout", "Layout",
|
||||||
"Layout for output stream",
|
"Layout for output stream",
|
||||||
@ -537,10 +445,9 @@ adpcmenc_class_init (ADPCMEncClass * klass)
|
|||||||
DEFAULT_ADPCM_BLOCK_SIZE,
|
DEFAULT_ADPCM_BLOCK_SIZE,
|
||||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
gobjectclass->dispose = adpcmenc_dispose;
|
}
|
||||||
gstelement_class->change_state = adpcmenc_change_state;
|
|
||||||
} static void
|
|
||||||
|
|
||||||
|
static void
|
||||||
adpcmenc_base_init (gpointer klass)
|
adpcmenc_base_init (gpointer klass)
|
||||||
{
|
{
|
||||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user