summaryrefslogtreecommitdiffstats
path: root/ext/speex
diff options
context:
space:
mode:
authorWim Taymans <wim.taymans@gmail.com>2004-09-28 16:44:12 +0000
committerWim Taymans <wim.taymans@gmail.com>2004-09-28 16:44:12 +0000
commit2748ae95482b17103909d2b9f0d65f3fb0948388 (patch)
treeb09f604391722085286899789fb0c3c58d38f88d /ext/speex
parent0089ff9ed3851854071c74e82039b95e38940641 (diff)
ext/speex/: Rewrote speex encoder, make sure it can be embedded in ogg.
Original commit message from CVS: * ext/speex/gstspeex.c: (plugin_init): * ext/speex/gstspeexdec.c: (gst_speex_dec_base_init), (gst_speex_dec_class_init), (speex_dec_get_formats), (speex_get_event_masks), (speex_get_query_types), (gst_speex_dec_init), (speex_dec_convert), (speex_dec_src_query), (speex_dec_src_event), (speex_dec_event), (speex_dec_chain), (gst_speexdec_get_property), (gst_speexdec_set_property), (speex_dec_change_state): * ext/speex/gstspeexdec.h: * ext/speex/gstspeexenc.c: (gst_speexenc_get_formats), (gst_speexenc_get_type), (speex_caps_factory), (raw_caps_factory), (gst_speexenc_base_init), (gst_speexenc_class_init), (gst_speexenc_sinkconnect), (gst_speexenc_convert_src), (gst_speexenc_convert_sink), (gst_speexenc_get_query_types), (gst_speexenc_src_query), (gst_speexenc_init), (gst_speexenc_get_tag_value), (comment_init), (comment_add), (gst_speexenc_metadata_set1), (gst_speexenc_set_metadata), (gst_speexenc_setup), (gst_speexenc_buffer_from_data), (gst_speexenc_push_buffer), (gst_speexenc_set_header_on_caps), (gst_speexenc_chain), (gst_speexenc_get_property), (gst_speexenc_set_property), (gst_speexenc_change_state): * ext/speex/gstspeexenc.h: Rewrote speex encoder, make sure it can be embedded in ogg. Implemented speex decoder.
Diffstat (limited to 'ext/speex')
-rw-r--r--ext/speex/gstspeex.c6
-rw-r--r--ext/speex/gstspeexdec.c628
-rw-r--r--ext/speex/gstspeexdec.h26
-rw-r--r--ext/speex/gstspeexenc.c1068
-rw-r--r--ext/speex/gstspeexenc.h51
5 files changed, 1523 insertions, 256 deletions
diff --git a/ext/speex/gstspeex.c b/ext/speex/gstspeex.c
index cde74576..c80211c4 100644
--- a/ext/speex/gstspeex.c
+++ b/ext/speex/gstspeex.c
@@ -26,6 +26,12 @@
static gboolean
plugin_init (GstPlugin * plugin)
{
+ if (!gst_library_load ("gstbytestream"))
+ return FALSE;
+
+ if (!gst_library_load ("gsttags"))
+ return FALSE;
+
if (!gst_element_register (plugin, "speexenc", GST_RANK_NONE,
GST_TYPE_SPEEXENC))
return FALSE;
diff --git a/ext/speex/gstspeexdec.c b/ext/speex/gstspeexdec.c
index 660f332b..da246aab 100644
--- a/ext/speex/gstspeexdec.c
+++ b/ext/speex/gstspeexdec.c
@@ -1,5 +1,5 @@
/* GStreamer
- * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) 2004 Wim Taymans <wim@fluendo.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -17,177 +17,579 @@
* Boston, MA 02111-1307, USA.
*/
-
#ifdef HAVE_CONFIG_H
-#include "config.h"
+# include "config.h"
#endif
-#include <string.h>
#include "gstspeexdec.h"
+#include <string.h>
+#include <gst/tag/tag.h>
-/* elementfactory information */
-GstElementDetails gst_speexdec_details = {
- "speex audio decoder",
+GST_DEBUG_CATEGORY (speexdec_debug);
+#define GST_CAT_DEFAULT speexdec_debug
+
+static GstElementDetails speex_dec_details = {
+ "SpeexDec",
"Codec/Decoder/Audio",
- ".speex",
- "Wim Taymans <wim.taymans@chello.be>",
+ "decode speex streams to audio",
+ "Wim Taymans <wim@fluendo.com>",
};
-/* SpeexDec signals and args */
+/* Filter signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
+#define DEFAULT_ENH TRUE
+
enum
{
- ARG_0
- /* FILL ME */
+ ARG_0,
+ ARG_ENH
};
-static void gst_speexdec_base_init (gpointer g_class);
-static void gst_speexdec_class_init (GstSpeexDec * klass);
-static void gst_speexdec_init (GstSpeexDec * speexdec);
-
-static void gst_speexdec_chain (GstPad * pad, GstData * _data);
-static GstPadLinkReturn gst_speexdec_sinkconnect (GstPad * pad,
- const GstCaps * caps);
-
-static GstElementClass *parent_class = NULL;
-
-/*static guint gst_speexdec_signals[LAST_SIGNAL] = { 0 }; */
-
-GType
-gst_speexdec_get_type (void)
-{
- static GType speexdec_type = 0;
-
- if (!speexdec_type) {
- static const GTypeInfo speexdec_info = {
- sizeof (GstSpeexDecClass),
- gst_speexdec_base_init,
- NULL,
- (GClassInitFunc) gst_speexdec_class_init,
- NULL,
- NULL,
- sizeof (GstSpeexDec),
- 0,
- (GInstanceInitFunc) gst_speexdec_init,
- };
-
- speexdec_type =
- g_type_register_static (GST_TYPE_ELEMENT, "GstSpeexDec", &speexdec_info,
- 0);
- }
- return speexdec_type;
-}
-
-static GstStaticPadTemplate speexdec_sink_template =
-GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-speex, "
- "rate = (int) [ 1000, 48000 ], " "channels = (int) 1")
- );
-
-static GstStaticPadTemplate speexdec_src_template =
+static GstStaticPadTemplate speex_dec_src_factory =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw-int, "
+ "rate = (int) [ 6000, 48000 ], "
+ "channels = (int) [ 1, 2 ], "
"endianness = (int) BYTE_ORDER, "
- "signed = (boolean) true, "
- "width = (int) 16, "
- "depth = (int) 16, "
- "rate = (int) [ 1000, 48000 ], " "channels = (int) 1")
+ "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16")
+ );
+
+static GstStaticPadTemplate speex_dec_sink_factory =
+GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-speex")
);
+GST_BOILERPLATE (GstSpeexDec, gst_speex_dec, GstElement, GST_TYPE_ELEMENT);
+
+static void speex_dec_chain (GstPad * pad, GstData * data);
+static GstElementStateReturn speex_dec_change_state (GstElement * element);
+static const GstFormat *speex_dec_get_formats (GstPad * pad);
+
+static gboolean speex_dec_src_event (GstPad * pad, GstEvent * event);
+static gboolean speex_dec_src_query (GstPad * pad,
+ GstQueryType query, GstFormat * format, gint64 * value);
+static gboolean speex_dec_convert (GstPad * pad,
+ GstFormat src_format, gint64 src_value,
+ GstFormat * dest_format, gint64 * dest_value);
+
+static void gst_speexdec_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_speexdec_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+
static void
-gst_speexdec_base_init (gpointer g_class)
+gst_speex_dec_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&speexdec_src_template));
+ gst_static_pad_template_get (&speex_dec_src_factory));
gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&speexdec_sink_template));
-
- gst_element_class_set_details (element_class, &gst_speexdec_details);
+ gst_static_pad_template_get (&speex_dec_sink_factory));
+ gst_element_class_set_details (element_class, &speex_dec_details);
}
static void
-gst_speexdec_class_init (GstSpeexDec * klass)
+gst_speex_dec_class_init (GstSpeexDecClass * klass)
{
+ GObjectClass *gobject_class;
GstElementClass *gstelement_class;
+ gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
- parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ENH,
+ g_param_spec_boolean ("enh", "Enh", "Enable perceptual enhancement",
+ DEFAULT_ENH, G_PARAM_READWRITE));
+
+ gstelement_class->change_state = speex_dec_change_state;
+
+ gobject_class->set_property = gst_speexdec_set_property;
+ gobject_class->get_property = gst_speexdec_get_property;
+
+ GST_DEBUG_CATEGORY_INIT (speexdec_debug, "speexdec", 0,
+ "speex decoding element");
}
-static void
-gst_speexdec_init (GstSpeexDec * speexdec)
+static const GstFormat *
+speex_dec_get_formats (GstPad * pad)
+{
+ static GstFormat src_formats[] = {
+ GST_FORMAT_BYTES,
+ GST_FORMAT_DEFAULT, /* samples in the audio case */
+ GST_FORMAT_TIME,
+ 0
+ };
+ static GstFormat sink_formats[] = {
+ GST_FORMAT_BYTES,
+ GST_FORMAT_TIME,
+ GST_FORMAT_DEFAULT, /* samples */
+ 0
+ };
+
+ return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
+}
+
+static const GstEventMask *
+speex_get_event_masks (GstPad * pad)
{
- GST_DEBUG ("gst_speexdec_init: initializing");
+ static const GstEventMask speex_dec_src_event_masks[] = {
+ {GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH},
+ {0,}
+ };
- /* create the sink and src pads */
- speexdec->sinkpad =
+ return speex_dec_src_event_masks;
+}
+
+static const GstQueryType *
+speex_get_query_types (GstPad * pad)
+{
+ static const GstQueryType speex_dec_src_query_types[] = {
+ GST_QUERY_TOTAL,
+ GST_QUERY_POSITION,
+ 0
+ };
+
+ return speex_dec_src_query_types;
+}
+
+static void
+gst_speex_dec_init (GstSpeexDec * dec)
+{
+ dec->sinkpad =
gst_pad_new_from_template (gst_static_pad_template_get
- (&speexdec_sink_template), "sink");
- gst_element_add_pad (GST_ELEMENT (speexdec), speexdec->sinkpad);
- gst_pad_set_chain_function (speexdec->sinkpad, gst_speexdec_chain);
- gst_pad_set_link_function (speexdec->sinkpad, gst_speexdec_sinkconnect);
+ (&speex_dec_sink_factory), "sink");
+ gst_pad_set_chain_function (dec->sinkpad, speex_dec_chain);
+ gst_pad_set_formats_function (dec->sinkpad, speex_dec_get_formats);
+ gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad);
- speexdec->srcpad =
+ dec->srcpad =
gst_pad_new_from_template (gst_static_pad_template_get
- (&speexdec_src_template), "src");
- gst_pad_use_explicit_caps (speexdec->srcpad);
- gst_element_add_pad (GST_ELEMENT (speexdec), speexdec->srcpad);
+ (&speex_dec_src_factory), "src");
+ gst_pad_use_explicit_caps (dec->srcpad);
+ gst_pad_set_event_mask_function (dec->srcpad, speex_get_event_masks);
+ gst_pad_set_event_function (dec->srcpad, speex_dec_src_event);
+ gst_pad_set_query_type_function (dec->srcpad, speex_get_query_types);
+ gst_pad_set_query_function (dec->srcpad, speex_dec_src_query);
+ gst_pad_set_formats_function (dec->srcpad, speex_dec_get_formats);
+ gst_pad_set_convert_function (dec->srcpad, speex_dec_convert);
+ gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad);
+
+ dec->enh = DEFAULT_ENH;
+
+ GST_FLAG_SET (dec, GST_ELEMENT_EVENT_AWARE);
+}
+static gboolean
+speex_dec_convert (GstPad * pad,
+ GstFormat src_format, gint64 src_value,
+ GstFormat * dest_format, gint64 * dest_value)
+{
+ gboolean res = TRUE;
+ GstSpeexDec *dec;
+ guint64 scale = 1;
+
+ dec = GST_SPEEXDEC (gst_pad_get_parent (pad));
+
+ if (dec->packetno < 1)
+ return FALSE;
+
+ switch (src_format) {
+ case GST_FORMAT_TIME:
+ switch (*dest_format) {
+ case GST_FORMAT_BYTES:
+ scale = sizeof (float) * dec->header->nb_channels;
+ case GST_FORMAT_DEFAULT:
+ *dest_value = scale * (src_value * dec->header->rate / GST_SECOND);
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ case GST_FORMAT_DEFAULT:
+ switch (*dest_format) {
+ case GST_FORMAT_BYTES:
+ *dest_value = src_value * sizeof (float) * dec->header->nb_channels;
+ break;
+ case GST_FORMAT_TIME:
+ *dest_value = src_value * GST_SECOND / dec->header->rate;
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ case GST_FORMAT_BYTES:
+ switch (*dest_format) {
+ case GST_FORMAT_DEFAULT:
+ *dest_value = src_value / (sizeof (float) * dec->header->nb_channels);
+ break;
+ case GST_FORMAT_TIME:
+ *dest_value = src_value * GST_SECOND /
+ (dec->header->rate * sizeof (float) * dec->header->nb_channels);
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ default:
+ res = FALSE;
+ }
+
+ return res;
}
-static GstPadLinkReturn
-gst_speexdec_sinkconnect (GstPad * pad, const GstCaps * caps)
+static gboolean
+speex_dec_src_query (GstPad * pad, GstQueryType query, GstFormat * format,
+ gint64 * value)
+{
+ gint64 samples_out = 0;
+ GstSpeexDec *dec = GST_SPEEXDEC (gst_pad_get_parent (pad));
+ GstFormat my_format = GST_FORMAT_DEFAULT;
+
+ if (query == GST_QUERY_POSITION) {
+ samples_out = dec->samples_out;
+ } else {
+ /* query peer in default format */
+ if (!gst_pad_query (GST_PAD_PEER (dec->sinkpad), query, &my_format,
+ &samples_out))
+ return FALSE;
+ }
+
+ /* and convert to the final format */
+ if (!gst_pad_convert (pad, GST_FORMAT_DEFAULT, samples_out, format, value))
+ return FALSE;
+
+ GST_LOG_OBJECT (dec,
+ "query %u: peer returned samples_out: %llu - we return %llu (format %u)\n",
+ query, samples_out, *value, *format);
+ return TRUE;
+}
+
+static gboolean
+speex_dec_src_event (GstPad * pad, GstEvent * event)
+{
+ gboolean res = TRUE;
+ GstSpeexDec *dec = GST_SPEEXDEC (gst_pad_get_parent (pad));
+
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_SEEK:{
+ guint64 value;
+ GstFormat my_format = GST_FORMAT_DEFAULT;
+
+ /* convert to samples_out */
+ res = speex_dec_convert (pad, GST_EVENT_SEEK_FORMAT (event),
+ GST_EVENT_SEEK_OFFSET (event), &my_format, &value);
+ if (res) {
+ GstEvent *real_seek = gst_event_new_seek (
+ (GST_EVENT_SEEK_TYPE (event) & ~GST_SEEK_FORMAT_MASK) |
+ GST_FORMAT_DEFAULT,
+ value);
+
+ res = gst_pad_send_event (GST_PAD_PEER (dec->sinkpad), real_seek);
+ }
+ gst_event_unref (event);
+ break;
+ }
+ default:
+ res = gst_pad_event_default (pad, event);
+ break;
+ }
+
+ return res;
+}
+
+static void
+speex_dec_event (GstSpeexDec * dec, GstEvent * event)
+{
+ guint64 value, time, bytes;
+
+ GST_LOG_OBJECT (dec, "handling event");
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_DISCONTINUOUS:
+ if (gst_event_discont_get_value (event, GST_FORMAT_DEFAULT,
+ (gint64 *) & value)) {
+ dec->samples_out = value;
+ GST_DEBUG_OBJECT (dec,
+ "setting samples_out to %" G_GUINT64_FORMAT " after discont",
+ value);
+ } else {
+ GST_WARNING_OBJECT (dec,
+ "discont event didn't include offset, we might set it wrong now");
+ }
+ if (dec->packetno < 2) {
+ if (dec->samples_out != 0)
+ GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL),
+ ("can't handle discont before parsing first 2 packets"));
+ dec->packetno = 0;
+ gst_pad_push (dec->srcpad, GST_DATA (gst_event_new_discontinuous (FALSE,
+ GST_FORMAT_TIME, (guint64) 0, GST_FORMAT_DEFAULT,
+ (guint64) 0, GST_FORMAT_BYTES, (guint64) 0, 0)));
+ } else {
+ GstFormat time_format, default_format, bytes_format;
+
+ time_format = GST_FORMAT_TIME;
+ default_format = GST_FORMAT_DEFAULT;
+ bytes_format = GST_FORMAT_BYTES;
+
+ dec->packetno = 2;
+ /* if one of them works, all of them work */
+ if (speex_dec_convert (dec->srcpad, GST_FORMAT_DEFAULT,
+ dec->samples_out, &time_format, &time)
+ && speex_dec_convert (dec->srcpad, GST_FORMAT_DEFAULT,
+ dec->samples_out, &bytes_format, &bytes)) {
+ gst_pad_push (dec->srcpad,
+ GST_DATA (gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME,
+ time, GST_FORMAT_DEFAULT, dec->samples_out,
+ GST_FORMAT_BYTES, bytes, 0)));
+ } else {
+ GST_ERROR_OBJECT (dec,
+ "failed to parse data for DISCONT event, not sending any");
+ }
+ }
+ gst_data_unref (GST_DATA (event));
+ break;
+ default:
+ gst_pad_event_default (dec->sinkpad, event);
+ break;
+ }
+}
+
+static void
+speex_dec_chain (GstPad * pad, GstData * data)
+{
+ GstBuffer *buf;
+ GstSpeexDec *dec;
+
+ dec = GST_SPEEXDEC (gst_pad_get_parent (pad));
+ if (GST_IS_EVENT (data)) {
+ speex_dec_event (dec, GST_EVENT (data));
+ return;
+ }
+
+ buf = GST_BUFFER (data);
+
+ if (dec->packetno == 0) {
+ GstCaps *caps;
+
+ /* get the header */
+ dec->header =
+ speex_packet_to_header (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
+ gst_data_unref (data);
+ if (!dec->header) {
+ GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE,
+ (NULL), ("couldn't read header"));
+ return;
+ }
+ if (dec->header->mode >= SPEEX_NB_MODES) {
+ GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE,
+ (NULL),
+ ("Mode number %d does not (yet/any longer) exist in this version",
+ dec->header->mode));
+ return;
+ }
+
+ dec->mode = speex_mode_list[dec->header->mode];
+
+ /* initialize the decoder */
+ dec->state = speex_decoder_init (dec->mode);
+ if (!dec->state) {
+ GST_ELEMENT_ERROR (GST_ELEMENT (dec), STREAM, DECODE,
+ (NULL), ("couldn't initialize decoder"));
+ gst_data_unref (data);
+ return;
+ }
+
+ speex_decoder_ctl (dec->state, SPEEX_SET_ENH, &dec->enh);
+ speex_decoder_ctl (dec->state, SPEEX_GET_FRAME_SIZE, &dec->frame_size);
+
+ if (dec->header->nb_channels != 1) {
+ dec->callback.callback_id = SPEEX_INBAND_STEREO;
+ dec->callback.func = speex_std_stereo_request_handler;
+ dec->callback.data = &dec->stereo;
+ speex_decoder_ctl (dec->state, SPEEX_SET_HANDLER, &dec->callback);
+ }
+
+ speex_decoder_ctl (dec->state, SPEEX_SET_SAMPLING_RATE, &dec->header->rate);
+
+ speex_bits_init (&dec->bits);
+
+ /* set caps */
+ caps = gst_caps_new_simple ("audio/x-raw-int",
+ "rate", G_TYPE_INT, dec->header->rate,
+ "channels", G_TYPE_INT, dec->header->nb_channels,
+ "signed", G_TYPE_BOOLEAN, TRUE,
+ "endianness", G_TYPE_INT, G_BYTE_ORDER,
+ "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL);
+
+ if (!gst_pad_set_explicit_caps (dec->srcpad, caps)) {
+ gst_caps_free (caps);
+ return;
+ }
+ gst_caps_free (caps);
+ } else if (dec->packetno == 1) {
+ gchar *encoder = NULL;
+
+ /* FIXME parse comments */
+ GstTagList *list = gst_tag_list_from_vorbiscomment_buffer (buf, "", 1,
+ &encoder);
+
+ gst_data_unref (data);
+
+ if (!list) {
+ GST_WARNING_OBJECT (dec, "couldn't decode comments");
+ list = gst_tag_list_new ();
+ }
+ if (encoder) {
+ gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_ENCODER, encoder, NULL);
+ g_free (encoder);
+ }
+ /*
+ gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_ENCODER_VERSION, dec->vi.version, NULL);
+
+ if (dec->vi.bitrate_upper > 0)
+ gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_MAXIMUM_BITRATE, (guint) vd->vi.bitrate_upper, NULL);
+ if (vd->vi.bitrate_nominal > 0)
+ gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_NOMINAL_BITRATE, (guint) vd->vi.bitrate_nominal, NULL);
+ if (vd->vi.bitrate_lower > 0)
+ gst_tag_list_add (list, GST_TAG_MERGE_REPLACE,
+ GST_TAG_MINIMUM_BITRATE, (guint) vd->vi.bitrate_lower, NULL);
+ */
+ gst_element_found_tags_for_pad (GST_ELEMENT (dec), dec->srcpad, 0, list);
+ } else {
+ gint i;
+
+ /* send data to the bitstream */
+ speex_bits_read_from (&dec->bits, GST_BUFFER_DATA (buf),
+ GST_BUFFER_SIZE (buf));
+ gst_data_unref (data);
+
+ /* now decode each frame */
+ for (i = 0; i < dec->header->frames_per_packet; i++) {
+ gint ret;
+ GstBuffer *outbuf;
+ gint16 *out_data;
+
+ ret = speex_decode (dec->state, &dec->bits, dec->output);
+ if (ret == -1) {
+ /* FIXME emit warning */
+ break;
+ } else if (ret == -2) {
+ /* FIXME emit warning */
+ break;
+ }
+ if (speex_bits_remaining (&dec->bits) < 0) {
+ fprintf (stderr, "Decoding overflow: corrupted stream?\n");
+ break;
+ }
+ if (dec->header->nb_channels == 2)
+ speex_decode_stereo (dec->output, dec->frame_size, &dec->stereo);
+
+ outbuf = gst_pad_alloc_buffer (dec->srcpad, GST_BUFFER_OFFSET_NONE,
+ dec->frame_size * dec->header->nb_channels * 2);
+ out_data = (gint16 *) GST_BUFFER_DATA (outbuf);
+
+ /*PCM saturation (just in case) */
+ for (i = 0; i < dec->frame_size * dec->header->nb_channels; i++) {
+ if (dec->output[i] > 32000.0)
+ out_data[i] = 32000;
+ else if (dec->output[i] < -32000.0)
+ out_data[i] = -32000;
+ else
+ out_data[i] = (gint16) dec->output[i];
+ }
+
+ GST_BUFFER_OFFSET (outbuf) = dec->samples_out;
+ GST_BUFFER_OFFSET_END (outbuf) = dec->samples_out + dec->frame_size;
+ GST_BUFFER_TIMESTAMP (outbuf) =
+ dec->samples_out * GST_SECOND / dec->header->rate;
+ GST_BUFFER_DURATION (outbuf) =
+ dec->frame_size * GST_SECOND / dec->header->rate;
+ gst_pad_push (dec->srcpad, GST_DATA (outbuf));
+ dec->samples_out += dec->frame_size;
+ }
+ }
+ dec->packetno++;
+}
+
+static void
+gst_speexdec_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
{
GstSpeexDec *speexdec;
- gint rate;
- GstStructure *structure;
-
- speexdec = GST_SPEEXDEC (gst_pad_get_parent (pad));
-
- structure = gst_caps_get_structure (caps, 0);
- gst_structure_get_int (structure, "rate", &rate);
-
- if (gst_pad_set_explicit_caps (speexdec->srcpad,
- gst_caps_new_simple ("audio/x-raw-int",
- "endianness", G_TYPE_INT, G_BYTE_ORDER,
- "signed", G_TYPE_BOOLEAN, TRUE,
- "width", G_TYPE_INT, 16,
- "depth", G_TYPE_INT, 16,
- "rate", G_TYPE_INT, rate, "channels", G_TYPE_INT, 1, NULL))) {
- return GST_PAD_LINK_OK;
+
+ /* it's not null if we got it, but it might not be ours */
+ g_return_if_fail (GST_IS_SPEEXDEC (object));
+
+ speexdec = GST_SPEEXDEC (object);
+
+ switch (prop_id) {
+ case ARG_ENH:
+ g_value_set_boolean (value, speexdec->enh);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
}
- return GST_PAD_LINK_REFUSED;
}
static void
-gst_speexdec_chain (GstPad * pad, GstData * _data)
+gst_speexdec_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
{
- GstBuffer *buf = GST_BUFFER (_data);
GstSpeexDec *speexdec;
- gchar *data;
- guint size;
- g_return_if_fail (pad != NULL);
- g_return_if_fail (GST_IS_PAD (pad));
- g_return_if_fail (buf != NULL);
- /*g_return_if_fail(GST_IS_BUFFER(buf)); */
+ /* it's not null if we got it, but it might not be ours */
+ g_return_if_fail (GST_IS_SPEEXDEC (object));
+
+ speexdec = GST_SPEEXDEC (object);
+
+ switch (prop_id) {
+ case ARG_ENH:
+ speexdec->enh = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
- speexdec = GST_SPEEXDEC (gst_pad_get_parent (pad));
- data = GST_BUFFER_DATA (buf);
- size = GST_BUFFER_SIZE (buf);
+static GstElementStateReturn
+speex_dec_change_state (GstElement * element)
+{
+ GstSpeexDec *vd = GST_SPEEXDEC (element);
+
+ switch (GST_STATE_TRANSITION (element)) {
+ case GST_STATE_NULL_TO_READY:
+ break;
+ case GST_STATE_READY_TO_PAUSED:
+ break;
+ case GST_STATE_PAUSED_TO_PLAYING:
+ break;
+ case GST_STATE_PLAYING_TO_PAUSED:
+ break;
+ case GST_STATE_PAUSED_TO_READY:
+ vd->packetno = 0;
+ vd->samples_out = 0;
+ break;
+ case GST_STATE_READY_TO_NULL:
+ break;
+ default:
+ break;
+ }
- gst_buffer_unref (buf);
+ return parent_class->change_state (element);
}
diff --git a/ext/speex/gstspeexdec.h b/ext/speex/gstspeexdec.h
index c679dc18..dcb04a2c 100644
--- a/ext/speex/gstspeexdec.h
+++ b/ext/speex/gstspeexdec.h
@@ -23,7 +23,10 @@
#include <gst/gst.h>
-#include <speex.h>
+#include <speex/speex.h>
+#include <speex/speex_callbacks.h>
+#include <speex/speex_header.h>
+#include <speex/speex_stereo.h>
#ifdef __cplusplus
extern "C" {
@@ -31,7 +34,7 @@ extern "C" {
#define GST_TYPE_SPEEXDEC \
- (gst_speexdec_get_type())
+ (gst_speex_dec_get_type())
#define GST_SPEEXDEC(obj) \
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SPEEXDEC,GstSpeexDec))
#define GST_SPEEXDEC_CLASS(klass) \
@@ -44,18 +47,35 @@ extern "C" {
typedef struct _GstSpeexDec GstSpeexDec;
typedef struct _GstSpeexDecClass GstSpeexDecClass;
+#define DEC_MAX_FRAME_SIZE 2000
+
struct _GstSpeexDec {
GstElement element;
/* pads */
GstPad *sinkpad,*srcpad;
+
+ void *state;
+ SpeexStereoState stereo;
+ SpeexMode *mode;
+ SpeexHeader *header;
+ SpeexCallback callback;
+ SpeexBits bits;
+
+ gfloat output[DEC_MAX_FRAME_SIZE];
+
+ gboolean enh;
+
+ gint frame_size;
+ guint64 samples_out;
+ guint64 packetno;
};
struct _GstSpeexDecClass {
GstElementClass parent_class;
};
-GType gst_speexdec_get_type(void);
+GType gst_speex_dec_get_type(void);
#ifdef __cplusplus
diff --git a/ext/speex/gstspeexenc.c b/ext/speex/gstspeexenc.c
index 2552c92e..530989c0 100644
--- a/ext/speex/gstspeexenc.c
+++ b/ext/speex/gstspeexenc.c
@@ -21,42 +21,94 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
+#include <stdlib.h>
#include <string.h>
+#include <time.h>
+#include <math.h>
+#include <speex/speex.h>
+#include <speex/speex_stereo.h>
+#include <gst/gsttaginterface.h>
+#include <gst/tag/tag.h>
#include "gstspeexenc.h"
+GST_DEBUG_CATEGORY (speexenc_debug);
+#define GST_CAT_DEFAULT speexenc_debug
+
+static GstPadTemplate *gst_speexenc_src_template, *gst_speexenc_sink_template;
+
/* elementfactory information */
-GstElementDetails gst_speexenc_details = {
- "speex audio encoder",
+GstElementDetails speexenc_details = {
+ "Speex encoder",
"Codec/Encoder/Audio",
- ".speex",
- "Wim Taymans <wim.taymans@chello.be>",
+ "Encodes audio in Speex format",
+ "Wim Taymans <wim@fluendo.com>",
};
-/* SpeexEnc signals and args */
+/* GstSpeexEnc signals and args */
enum
{
- FRAME_ENCODED,
/* FILL ME */
LAST_SIGNAL
};
+#define DEFAULT_QUALITY 8.0
+#define DEFAULT_BITRATE 0
+#define DEFAULT_VBR FALSE
+#define DEFAULT_ABR 0
+#define DEFAULT_VAD FALSE
+#define DEFAULT_DTX FALSE
+#define DEFAULT_COMPLEXITY 3
+#define DEFAULT_NFRAMES 1
+
enum
{
- ARG_0
- /* FILL ME */
+ ARG_0,
+ ARG_QUALITY,
+ ARG_BITRATE,
+ ARG_VBR,
+ ARG_ABR,
+ ARG_VAD,
+ ARG_DTX,
+ ARG_COMPLEXITY,
+ ARG_NFRAMES,
+ ARG_LAST_MESSAGE
};
+static const GstFormat *
+gst_speexenc_get_formats (GstPad * pad)
+{
+ static const GstFormat src_formats[] = {
+ GST_FORMAT_BYTES,
+ GST_FORMAT_TIME,
+ 0
+ };
+ static const GstFormat sink_formats[] = {
+ GST_FORMAT_BYTES,
+ GST_FORMAT_DEFAULT,
+ GST_FORMAT_TIME,
+ 0
+ };
+
+ return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
+}
+
static void gst_speexenc_base_init (gpointer g_class);
-static void gst_speexenc_class_init (GstSpeexEnc * klass);
+static void gst_speexenc_class_init (GstSpeexEncClass * klass);
static void gst_speexenc_init (GstSpeexEnc * speexenc);
static void gst_speexenc_chain (GstPad * pad, GstData * _data);
-static GstPadLinkReturn gst_speexenc_sinkconnect (GstPad * pad,
- const GstCaps * caps);
+static gboolean gst_speexenc_setup (GstSpeexEnc * speexenc);
+
+static void gst_speexenc_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+static void gst_speexenc_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static GstElementStateReturn gst_speexenc_change_state (GstElement * element);
static GstElementClass *parent_class = NULL;
-static guint gst_speexenc_signals[LAST_SIGNAL] = { 0 };
+
+/*static guint gst_speexenc_signals[LAST_SIGNAL] = { 0 }; */
GType
gst_speexenc_get_type (void)
@@ -75,49 +127,63 @@ gst_speexenc_get_type (void)
0,
(GInstanceInitFunc) gst_speexenc_init,
};
+ static const GInterfaceInfo tag_setter_info = {
+ NULL,
+ NULL,
+ NULL
+ };
speexenc_type =
g_type_register_static (GST_TYPE_ELEMENT, "GstSpeexEnc", &speexenc_info,
0);
+
+ g_type_add_interface_static (speexenc_type, GST_TYPE_TAG_SETTER,
+ &tag_setter_info);
+
+ GST_DEBUG_CATEGORY_INIT (speexenc_debug, "speexenc", 0, "Speex encoder");
}
return speexenc_type;
}
-static GstStaticPadTemplate speexenc_sink_template =
-GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-raw-int, "
- "endianness = (int) BYTE_ORDER, "
- "signed = (boolean) true, "
- "width = (int) 16, "
- "depth = (int) 16, "
- "rate = (int) [ 1000, 48000 ], " "channels = (int) 1")
- );
-
-static GstStaticPadTemplate speexenc_src_template =
-GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-speex, "
- "rate = (int) [ 1000, 48000 ], " "channels = (int) 1")
- );
+static GstCaps *
+speex_caps_factory (void)
+{
+ return gst_caps_new_simple ("audio/x-speex", NULL);
+}
+
+static GstCaps *
+raw_caps_factory (void)
+{
+ return
+ gst_caps_new_simple ("audio/x-raw-int",
+ "rate", GST_TYPE_INT_RANGE, 6000, 48000,
+ "channels", GST_TYPE_INT_RANGE, 1, 2,
+ "endianness", G_TYPE_INT, G_BYTE_ORDER,
+ "signed", G_TYPE_BOOLEAN, TRUE,
+ "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL);
+}
static void
gst_speexenc_base_init (gpointer g_class)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+ GstCaps *raw_caps, *speex_caps;
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&speexenc_sink_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&speexenc_src_template));
+ raw_caps = raw_caps_factory ();
+ speex_caps = speex_caps_factory ();
- gst_element_class_set_details (element_class, &gst_speexenc_details);
+ gst_speexenc_sink_template = gst_pad_template_new ("sink", GST_PAD_SINK,
+ GST_PAD_ALWAYS, raw_caps);
+ gst_speexenc_src_template = gst_pad_template_new ("src", GST_PAD_SRC,
+ GST_PAD_ALWAYS, speex_caps);
+ gst_element_class_add_pad_template (element_class,
+ gst_speexenc_sink_template);
+ gst_element_class_add_pad_template (element_class, gst_speexenc_src_template);
+ gst_element_class_set_details (element_class, &speexenc_details);
}
static void
-gst_speexenc_class_init (GstSpeexEnc * klass)
+gst_speexenc_class_init (GstSpeexEncClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
@@ -125,63 +191,640 @@ gst_speexenc_class_init (GstSpeexEnc * klass)
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_QUALITY,
+ g_param_spec_float ("quality", "Quality", "Encoding quality",
+ 0.0, 10.0, DEFAULT_QUALITY, G_PARAM_READWRITE));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE,
+ g_param_spec_int ("bitrate", "Encoding Bit-rate",
+ "Specify an encoding bit-rate (in bps). ",
+ 0, G_MAXINT, DEFAULT_BITRATE, G_PARAM_READWRITE));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_VBR,
+ g_param_spec_boolean ("vbr", "VBR",
+ "Enable variable bit-rate", DEFAULT_VBR, G_PARAM_READWRITE));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ABR,
+ g_param_spec_int ("abr", "ABR",
+ "Enable average bit-rate (0 = disabled)",
+ 0, G_MAXINT, DEFAULT_ABR, G_PARAM_READWRITE));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_VAD,
+ g_param_spec_boolean ("vad", "VAD",
+ "Enable voice activity detection", DEFAULT_VAD, G_PARAM_READWRITE));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DTX,
+ g_param_spec_boolean ("dtx", "DTX",
+ "Enable discontinuous transmission", DEFAULT_DTX, G_PARAM_READWRITE));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_COMPLEXITY,
+ g_param_spec_int ("complexity", "Complexity",
+ "Set encoding complexity",
+ 0, G_MAXINT, DEFAULT_COMPLEXITY, G_PARAM_READWRITE));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NFRAMES,
+ g_param_spec_int ("nframes", "NFrames",
+ "Number of frames per buffer",
+ 0, G_MAXINT, DEFAULT_NFRAMES, G_PARAM_READWRITE));
+ g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE,
+ g_param_spec_string ("last-message", "last-message",
+ "The last status message", NULL, G_PARAM_READABLE));
+
parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
- gst_speexenc_signals[FRAME_ENCODED] =
- g_signal_new ("frame-encoded", G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstSpeexEncClass, frame_encoded),
- NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+ gobject_class->set_property = gst_speexenc_set_property;
+ gobject_class->get_property = gst_speexenc_get_property;
+
+ gstelement_class->change_state = gst_speexenc_change_state;
+}
+
+static GstPadLinkReturn
+gst_speexenc_sinkconnect (GstPad * pad, const GstCaps * caps)
+{
+ GstSpeexEnc *speexenc;
+ GstStructure *structure;
+
+ speexenc = GST_SPEEXENC (gst_pad_get_parent (pad));
+ speexenc->setup = FALSE;
+
+ structure = gst_caps_get_structure (caps, 0);
+ gst_structure_get_int (structure, "channels", &speexenc->channels);
+ gst_structure_get_int (structure, "rate", &speexenc->rate);
+
+ gst_speexenc_setup (speexenc);
+
+ if (speexenc->setup)
+ return GST_PAD_LINK_OK;
+
+ return GST_PAD_LINK_REFUSED;
+}
+
+static gboolean
+gst_speexenc_convert_src (GstPad * pad, GstFormat src_format, gint64 src_value,
+ GstFormat * dest_format, gint64 * dest_value)
+{
+ gboolean res = TRUE;
+ GstSpeexEnc *speexenc;
+ gint64 avg;
+
+ speexenc = GST_SPEEXENC (gst_pad_get_parent (pad));
+
+ if (speexenc->samples_in == 0 ||
+ speexenc->bytes_out == 0 || speexenc->rate == 0)
+ return FALSE;
+
+ avg = (speexenc->bytes_out * speexenc->rate) / (speexenc->samples_in);
+
+ switch (src_format) {
+ case GST_FORMAT_BYTES:
+ switch (*dest_format) {
+ case GST_FORMAT_TIME:
+ *dest_value = src_value * GST_SECOND / avg;
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ case GST_FORMAT_TIME:
+ switch (*dest_format) {
+ case GST_FORMAT_BYTES:
+ *dest_value = src_value * avg / GST_SECOND;
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ default:
+ res = FALSE;
+ }
+ return res;
+}
+
+static gboolean
+gst_speexenc_convert_sink (GstPad * pad, GstFormat src_format,
+ gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
+{
+ gboolean res = TRUE;
+ guint scale = 1;
+ gint bytes_per_sample;
+ GstSpeexEnc *speexenc;
+
+ speexenc = GST_SPEEXENC (gst_pad_get_parent (pad));
+
+ bytes_per_sample = speexenc->channels * 2;
+
+ switch (src_format) {
+ case GST_FORMAT_BYTES:
+ switch (*dest_format) {
+ case GST_FORMAT_DEFAULT:
+ if (bytes_per_sample == 0)
+ return FALSE;
+ *dest_value = src_value / bytes_per_sample;
+ break;
+ case GST_FORMAT_TIME:
+ {
+ gint byterate = bytes_per_sample * speexenc->rate;
+
+ if (byterate == 0)
+ return FALSE;
+ *dest_value = src_value * GST_SECOND / byterate;
+ break;
+ }
+ default:
+ res = FALSE;
+ }
+ break;
+ case GST_FORMAT_DEFAULT:
+ switch (*dest_format) {
+ case GST_FORMAT_BYTES:
+ *dest_value = src_value * bytes_per_sample;
+ break;
+ case GST_FORMAT_TIME:
+ if (speexenc->rate == 0)
+ return FALSE;
+ *dest_value = src_value * GST_SECOND / speexenc->rate;
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ case GST_FORMAT_TIME:
+ switch (*dest_format) {
+ case GST_FORMAT_BYTES:
+ scale = bytes_per_sample;
+ /* fallthrough */
+ case GST_FORMAT_DEFAULT:
+ *dest_value = src_value * scale * speexenc->rate / GST_SECOND;
+ break;
+ default:
+ res = FALSE;
+ }
+ break;
+ default:
+ res = FALSE;
+ }
+ return res;
+}
+
+static const GstQueryType *
+gst_speexenc_get_query_types (GstPad * pad)
+{
+ static const GstQueryType gst_speexenc_src_query_types[] = {
+ GST_QUERY_TOTAL,
+ GST_QUERY_POSITION,
+ 0
+ };
+
+ return gst_speexenc_src_query_types;
}
+static gboolean
+gst_speexenc_src_query (GstPad * pad, GstQueryType type,
+ GstFormat * format, gint64 * value)
+{
+ gboolean res = TRUE;
+ GstSpeexEnc *speexenc;
+
+ speexenc = GST_SPEEXENC (gst_pad_get_parent (pad));
+
+ switch (type) {
+ case GST_QUERY_TOTAL:
+ {
+ switch (*format) {
+ case GST_FORMAT_BYTES:
+ case GST_FORMAT_TIME:
+ {
+ gint64 peer_value;
+ const GstFormat *peer_formats;
+
+ res = FALSE;
+
+ peer_formats = gst_pad_get_formats (GST_PAD_PEER (speexenc->sinkpad));
+
+ while (peer_formats && *peer_formats && !res) {
+
+ GstFormat peer_format = *peer_formats;
+
+ /* do the probe */
+ if (gst_pad_query (GST_PAD_PEER (speexenc->sinkpad),
+ GST_QUERY_TOTAL, &peer_format, &peer_value)) {
+ GstFormat conv_format;
+
+ /* convert to TIME */
+ conv_format = GST_FORMAT_TIME;
+ res = gst_pad_convert (speexenc->sinkpad,
+ peer_format, peer_value, &conv_format, value);
+ /* and to final format */
+ res &= gst_pad_convert (pad,
+ GST_FORMAT_TIME, *value, format, value);
+ }
+ peer_formats++;
+ }
+ break;
+ }
+ default:
+ res = FALSE;
+ break;
+ }
+ break;
+ }
+ case GST_QUERY_POSITION:
+ switch (*format) {
+ default:
+ {
+ /* we only know about our samples, convert to requested format */
+ res = gst_pad_convert (pad,
+ GST_FORMAT_BYTES, speexenc->bytes_out, format, value);
+ break;
+ }
+ }
+ break;
+ default:
+ res = FALSE;
+ break;
+ }
+ return res;
+}
static void
gst_speexenc_init (GstSpeexEnc * speexenc)
{
- /* create the sink and src pads */
speexenc->sinkpad =
- gst_pad_new_from_template (gst_static_pad_template_get
- (&speexenc_sink_template), "sink");
+ gst_pad_new_from_template (gst_speexenc_sink_template, "sink");
gst_element_add_pad (GST_ELEMENT (speexenc), speexenc->sinkpad);
gst_pad_set_chain_function (speexenc->sinkpad, gst_speexenc_chain);
gst_pad_set_link_function (speexenc->sinkpad, gst_speexenc_sinkconnect);
+ gst_pad_set_convert_function (speexenc->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_speexenc_convert_sink));
+ gst_pad_set_formats_function (speexenc->sinkpad,
+ GST_DEBUG_FUNCPTR (gst_speexenc_get_formats));
speexenc->srcpad =
- gst_pad_new_from_template (gst_static_pad_template_get
- (&speexenc_src_template), "src");
+ gst_pad_new_from_template (gst_speexenc_src_template, "src");
+ gst_pad_set_query_function (speexenc->srcpad,
+ GST_DEBUG_FUNCPTR (gst_speexenc_src_query));
+ gst_pad_set_query_type_function (speexenc->srcpad,
+ GST_DEBUG_FUNCPTR (gst_speexenc_get_query_types));
+ gst_pad_set_convert_function (speexenc->srcpad,
+ GST_DEBUG_FUNCPTR (gst_speexenc_convert_src));
+ gst_pad_set_formats_function (speexenc->srcpad,
+ GST_DEBUG_FUNCPTR (gst_speexenc_get_formats));
gst_element_add_pad (GST_ELEMENT (speexenc), speexenc->srcpad);
- speex_bits_init (&speexenc->bits);
- speexenc->mode = (SpeexMode *) & speex_nb_mode;
- speexenc->bufsize = 0;
- speexenc->packet_count = 0;
- speexenc->n_packets = 20;
+ speexenc->channels = -1;
+ speexenc->rate = -1;
+
+ speexenc->quality = DEFAULT_QUALITY;
+ speexenc->bitrate = DEFAULT_BITRATE;
+ speexenc->vbr = DEFAULT_VBR;
+ speexenc->abr = DEFAULT_ABR;
+ speexenc->vad = DEFAULT_VAD;
+ speexenc->dtx = DEFAULT_DTX;
+ speexenc->complexity = DEFAULT_COMPLEXITY;
+ speexenc->nframes = DEFAULT_NFRAMES;
+
+ speexenc->setup = FALSE;
+ speexenc->eos = FALSE;
+ speexenc->header_sent = FALSE;
+
+ speexenc->tags = gst_tag_list_new ();
+ speexenc->adapter = gst_adapter_new ();
+
+ /* we're chained and we can deal with events */
+ GST_FLAG_SET (speexenc, GST_ELEMENT_EVENT_AWARE);
}
-static GstPadLinkReturn
-gst_speexenc_sinkconnect (GstPad * pad, const GstCaps * caps)
+
+static gchar *
+gst_speexenc_get_tag_value (const GstTagList * list, const gchar * tag,
+ int index)
{
- GstSpeexEnc *speexenc;
- GstStructure *structure;
+ gchar *speexvalue = NULL;
- speexenc = GST_SPEEXENC (gst_pad_get_parent (pad));
+ if (tag == NULL) {
+ return NULL;
+ }
- structure = gst_caps_get_structure (caps, 0);
- gst_structure_get_int (structure, "rate", &speexenc->rate);
- if (gst_pad_try_set_caps (speexenc->srcpad,
- gst_caps_new_simple ("audio/x-speex",
- "rate", G_TYPE_INT, speexenc->rate,
- "channels", G_TYPE_INT, 1, NULL))) {
- speex_init_header (&speexenc->header, speexenc->rate, 1, speexenc->mode);
- speexenc->header.frames_per_packet = speexenc->n_packets;
+ /* get tag name right */
+ if ((strcmp (tag, GST_TAG_TRACK_NUMBER) == 0)
+ || (strcmp (tag, GST_TAG_ALBUM_VOLUME_NUMBER) == 0)
+ || (strcmp (tag, GST_TAG_TRACK_COUNT) == 0)
+ || (strcmp (tag, GST_TAG_ALBUM_VOLUME_COUNT) == 0)) {
+ guint track_no;
- speexenc->state = speex_encoder_init (speexenc->mode);
- speex_encoder_ctl (speexenc->state, SPEEX_GET_FRAME_SIZE,
- &speexenc->frame_size);
+ if (!gst_tag_list_get_uint_index (list, tag, index, &track_no))
+ g_assert_not_reached ();
+ speexvalue = g_strdup_printf ("%u", track_no);
+ } else if (strcmp (tag, GST_TAG_DATE) == 0) {
+ /* FIXME: how are dates represented in speex files? */
+ GDate *date;
+ guint u;
- return GST_PAD_LINK_OK;
+ if (!gst_tag_list_get_uint_index (list, tag, index, &u))
+ g_assert_not_reached ();
+ date = g_date_new_julian (u);
+ speexvalue =
+ g_strdup_printf ("%04d-%02d-%02d", (gint) g_date_get_year (date),
+ (gint) g_date_get_month (date), (gint) g_date_get_day (date));
+ g_date_free (date);
+ } else if (gst_tag_get_type (tag) == G_TYPE_STRING) {
+ if (!gst_tag_list_get_string_index (list, tag, index, &speexvalue))
+ g_assert_not_reached ();
}
- return GST_PAD_LINK_REFUSED;
+ return speexvalue;
+}
+
+/*
+ * Comments will be stored in the Vorbis style.
+ * It is describled in the "Structure" section of
+ * http://www.xiph.org/ogg/vorbis/doc/v-comment.html
+ *
+ * The comment header is decoded as follows:
+ * 1) [vendor_length] = read an unsigned integer of 32 bits
+ * 2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
+ * 3) [user_comment_list_length] = read an unsigned integer of 32 bits
+ * 4) iterate [user_comment_list_length] times {
+ * 5) [length] = read an unsigned integer of 32 bits
+ * 6) this iteration's user comment = read a UTF-8 vector as [length] octets
+ * }
+ * 7) [framing_bit] = read a single bit as boolean
+ * 8) if ( [framing_bit] unset or end of packet ) then ERROR
+ * 9) done.
+ *
+ * If you have troubles, please write to ymnk@jcraft.com.
+ */
+#define readint(buf, base) (((buf[base+3]<<24) & 0xff000000)| \
+ ((buf[base+2]<<16) & 0xff0000)| \
+ ((buf[base+1]<< 8) & 0xff00)| \
+ (buf[base ] & 0xff))
+#define writeint(buf, base, val) do{ buf[base+3] = ((val)>>24) & 0xff; \
+ buf[base+2] = ((val)>>16) & 0xff; \
+ buf[base+1] = ((val)>> 8) & 0xff; \
+ buf[base ] = (val) & 0xff; \
+ }while(0)
+
+static void
+comment_init (char **comments, int *length, char *vendor_string)
+{
+ int vendor_length = strlen (vendor_string);
+ int user_comment_list_length = 0;
+ int len = 4 + vendor_length + 4;
+ char *p = (char *) malloc (len);
+
+ if (p == NULL) {
+ }
+ writeint (p, 0, vendor_length);
+ memcpy (p + 4, vendor_string, vendor_length);
+ writeint (p, 4 + vendor_length, user_comment_list_length);
+ *length = len;
+ *comments = p;
+}
+static void
+comment_add (char **comments, int *length, const char *tag, char *val)
+{
+ char *p = *comments;
+ int vendor_length = readint (p, 0);
+ int user_comment_list_length = readint (p, 4 + vendor_length);
+ int tag_len = (tag ? strlen (tag) : 0);
+ int val_len = strlen (val);
+ int len = (*length) + 4 + tag_len + val_len;
+
+ p = (char *) realloc (p, len);
+
+ writeint (p, *length, tag_len + val_len); /* length of comment */
+ if (tag)
+ memcpy (p + *length + 4, tag, tag_len); /* comment */
+ memcpy (p + *length + 4 + tag_len, val, val_len); /* comment */
+ writeint (p, 4 + vendor_length, user_comment_list_length + 1);
+
+ *comments = p;
+ *length = len;
+}
+
+#undef readint
+#undef writeint
+
+static void
+gst_speexenc_metadata_set1 (const GstTagList * list, const gchar * tag,
+ gpointer speexenc)
+{
+ const gchar *speextag = NULL;
+ gchar *speexvalue = NULL;
+ guint i, count;
+ GstSpeexEnc *enc = GST_SPEEXENC (speexenc);
+
+ speextag = gst_tag_to_vorbis_tag (tag);
+ if (speextag == NULL) {
+ return;
+ }
+
+ count = gst_tag_list_get_tag_size (list, tag);
+ for (i = 0; i < count; i++) {
+ speexvalue = gst_speexenc_get_tag_value (list, tag, i);
+
+ if (speexvalue != NULL) {
+ comment_add (&enc->comments, &enc->comment_len, speextag, speexvalue);
+ }
+ }
+}
+
+static void
+gst_speexenc_set_metadata (GstSpeexEnc * speexenc)
+{
+ GstTagList *copy;
+ const GstTagList *user_tags;
+
+ user_tags = gst_tag_setter_get_list (GST_TAG_SETTER (speexenc));
+ if (!(speexenc->tags || user_tags))
+ return;
+
+ comment_init (&speexenc->comments, &speexenc->comment_len,
+ "Encoded with GStreamer Speexenc");
+ copy =
+ gst_tag_list_merge (user_tags, speexenc->tags,
+ gst_tag_setter_get_merge_mode (GST_TAG_SETTER (speexenc)));
+ gst_tag_list_foreach (copy, gst_speexenc_metadata_set1, speexenc);
+ gst_tag_list_free (copy);
+}
+
+static gboolean
+gst_speexenc_setup (GstSpeexEnc * speexenc)
+{
+ speexenc->setup = FALSE;
+
+ switch (speexenc->mode) {
+ case GST_SPEEXENC_MODE_UWB:
+ speexenc->speex_mode = &speex_uwb_mode;
+ break;
+ case GST_SPEEXENC_MODE_WB:
+ speexenc->speex_mode = &speex_wb_mode;
+ break;
+ case GST_SPEEXENC_MODE_NB:
+ speexenc->speex_mode = &speex_nb_mode;
+ break;
+ case GST_SPEEXENC_MODE_AUTO:
+ default:
+ break;
+ }
+
+ if (speexenc->rate > 25000) {
+ if (speexenc->mode == GST_SPEEXENC_MODE_AUTO) {
+ speexenc->speex_mode = &speex_uwb_mode;
+ } else {
+ if (speexenc->speex_mode != &speex_uwb_mode) {
+ speexenc->last_message =
+ g_strdup_printf
+ ("Warning: suggest to use ultra wide band mode for this rate");
+ g_object_notify (G_OBJECT (speexenc), "last_message");
+ }
+ }
+ } else if (speexenc->rate > 12500) {
+ if (speexenc->mode == GST_SPEEXENC_MODE_AUTO) {
+ speexenc->speex_mode = &speex_wb_mode;
+ } else {
+ if (speexenc->speex_mode != &speex_wb_mode) {
+ speexenc->last_message =
+ g_strdup_printf
+ ("Warning: suggest to use wide band mode for this rate");
+ g_object_notify (G_OBJECT (speexenc), "last_message");
+ }
+ }
+ } else {
+ if (speexenc->mode == GST_SPEEXENC_MODE_AUTO) {
+ speexenc->speex_mode = &speex_nb_mode;
+ } else {
+ if (speexenc->speex_mode != &speex_nb_mode) {
+ speexenc->last_message =
+ g_strdup_printf
+ ("Warning: suggest to use narrow band mode for this rate");
+ g_object_notify (G_OBJECT (speexenc), "last_message");
+ }
+ }
+ }
+
+ if (speexenc->rate != 8000 && speexenc->rate != 16000
+ && speexenc->rate != 32000) {
+ speexenc->last_message =
+ g_strdup_printf ("Warning: speex is optimized for 8, 16 and 32 KHz");
+ g_object_notify (G_OBJECT (speexenc), "last_message");
+ }
+
+ speex_init_header (&speexenc->header, speexenc->rate, 1,
+ speexenc->speex_mode);
+ speexenc->header.frames_per_packet = speexenc->nframes;
+ speexenc->header.vbr = speexenc->vbr;
+ speexenc->header.nb_channels = speexenc->channels;
+
+ /*Initialize Speex encoder */
+ speexenc->state = speex_encoder_init (speexenc->speex_mode);
+
+ speex_encoder_ctl (speexenc->state, SPEEX_GET_FRAME_SIZE,
+ &speexenc->frame_size);
+ speex_encoder_ctl (speexenc->state, SPEEX_SET_COMPLEXITY,
+ &speexenc->complexity);
+ speex_encoder_ctl (speexenc->state, SPEEX_SET_SAMPLING_RATE, &speexenc->rate);
+
+ if (speexenc->vbr)
+ speex_encoder_ctl (speexenc->state, SPEEX_SET_VBR_QUALITY,
+ &speexenc->quality);
+ else {
+ gint tmp = floor (speexenc->quality);
+
+ speex_encoder_ctl (speexenc->state, SPEEX_SET_QUALITY, &tmp);
+ }
+ if (speexenc->bitrate) {
+ if (speexenc->quality >= 0.0 && speexenc->vbr) {
+ speexenc->last_message =
+ g_strdup_printf ("Warning: bitrate option is overriding quality");
+ g_object_notify (G_OBJECT (speexenc), "last_message");
+ }
+ speex_encoder_ctl (speexenc->state, SPEEX_SET_BITRATE, &speexenc->bitrate);
+ }
+ if (speexenc->vbr) {
+ gint tmp = 1;
+
+ speex_encoder_ctl (speexenc->state, SPEEX_SET_VBR, &tmp);
+ } else if (speexenc->vad) {
+ gint tmp = 1;
+
+ speex_encoder_ctl (speexenc->state, SPEEX_SET_VAD, &tmp);
+ }
+
+ if (speexenc->dtx) {
+ gint tmp = 1;
+
+ speex_encoder_ctl (speexenc->state, SPEEX_SET_DTX, &tmp);
+ }
+
+ if (speexenc->dtx && !(speexenc->vbr || speexenc->abr || speexenc->vad)) {
+ speexenc->last_message =
+ g_strdup_printf ("Warning: dtx is useless without vad, vbr or abr");
+ g_object_notify (G_OBJECT (speexenc), "last_message");
+ } else if ((speexenc->vbr || speexenc->abr) && (speexenc->vad)) {
+ speexenc->last_message =
+ g_strdup_printf ("Warning: vad is already implied by vbr or abr");
+ g_object_notify (G_OBJECT (speexenc), "last_message");
+ }
+
+ if (speexenc->abr) {
+ speex_encoder_ctl (speexenc->state, SPEEX_SET_ABR, &speexenc->abr);
+ }
+
+ speex_encoder_ctl (speexenc->state, SPEEX_GET_LOOKAHEAD,
+ &speexenc->lookahead);
+
+ speexenc->setup = TRUE;
+
+ return TRUE;
+}
+
+/* prepare a buffer for transmission */
+static GstBuffer *
+gst_speexenc_buffer_from_data (GstSpeexEnc * speexenc, guchar * data,
+ gint data_len, guint64 granulepos)
+{
+ GstBuffer *outbuf;
+
+ outbuf = gst_buffer_new_and_alloc (data_len);
+ memcpy (GST_BUFFER_DATA (outbuf), data, data_len);
+ GST_BUFFER_OFFSET (outbuf) = speexenc->bytes_out;
+ GST_BUFFER_OFFSET_END (outbuf) = granulepos;
+
+ GST_DEBUG ("encoded buffer of %d bytes", GST_BUFFER_SIZE (outbuf));
+ return outbuf;
+}
+
+/* push out the buffer and do internal bookkeeping */
+static void
+gst_speexenc_push_buffer (GstSpeexEnc * speexenc, GstBuffer * buffer)
+{
+ speexenc->bytes_out += GST_BUFFER_SIZE (buffer);
+
+ if (GST_PAD_IS_USABLE (speexenc->srcpad)) {
+ gst_pad_push (speexenc->srcpad, GST_DATA (buffer));
+ } else {
+ gst_buffer_unref (buffer);
+ }
+}
+
+static void
+gst_speexenc_set_header_on_caps (GstCaps * caps, GstBuffer * buf1,
+ GstBuffer * buf2)
+{
+ GstStructure *structure = gst_caps_get_structure (caps, 0);
+ GValue list = { 0 };
+ GValue value = { 0 };
+
+ /* mark buffers */
+ GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_IN_CAPS);
+ GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_IN_CAPS);
+
+ /* put buffers in a fixed list */
+ g_value_init (&list, GST_TYPE_FIXED_LIST);
+ g_value_init (&value, GST_TYPE_BUFFER);
+ g_value_set_boxed (&value, buf1);
+ gst_value_list_append_value (&list, &value);
+ g_value_unset (&value);
+ g_value_init (&value, GST_TYPE_BUFFER);
+ g_value_set_boxed (&value, buf2);
+ gst_value_list_append_value (&list, &value);
+ gst_structure_set_value (structure, "streamheader", &list);
+ g_value_unset (&value);
+ g_value_unset (&list);
}
static void
@@ -189,103 +832,258 @@ gst_speexenc_chain (GstPad * pad, GstData * _data)
{
GstBuffer *buf = GST_BUFFER (_data);
GstSpeexEnc *speexenc;
- GstBuffer *outbuf;
- gint16 *data;
- guint8 *header_data;
- gint size;
- float input[1000];
- gint frame_size;
- gint i;
g_return_if_fail (pad != NULL);
g_return_if_fail (GST_IS_PAD (pad));
g_return_if_fail (buf != NULL);
- speexenc = GST_SPEEXENC (GST_OBJECT_PARENT (pad));
+ speexenc = GST_SPEEXENC (gst_pad_get_parent (pad));
- if (!GST_PAD_CAPS (speexenc->srcpad)) {
+ if (GST_IS_EVENT (buf)) {
+ GstEvent *event = GST_EVENT (buf);
- if (!gst_pad_try_set_caps (speexenc->srcpad,
- gst_caps_new_simple ("audio/x-speex",
- "rate", G_TYPE_INT, speexenc->rate,
- "channels", G_TYPE_INT, 1, NULL))) {
- GST_ELEMENT_ERROR (speexenc, CORE, NEGOTIATION, (NULL), (NULL));
+ switch (GST_EVENT_TYPE (event)) {
+ case GST_EVENT_EOS:
+ speexenc->eos = TRUE;
+ gst_event_unref (event);
+ break;
+ case GST_EVENT_TAG:
+ if (speexenc->tags) {
+ gst_tag_list_insert (speexenc->tags, gst_event_tag_get_list (event),
+ gst_tag_setter_get_merge_mode (GST_TAG_SETTER (speexenc)));
+ } else {
+ g_assert_not_reached ();
+ }
+ gst_pad_event_default (pad, event);
+ return;
+ default:
+ gst_pad_event_default (pad, event);
+ return;
+ }
+ } else {
+ if (!speexenc->setup) {
+ gst_buffer_unref (buf);
+ GST_ELEMENT_ERROR (speexenc, CORE, NEGOTIATION, (NULL),
+ ("encoder not initialized (input is not audio?)"));
return;
}
- }
- if (speexenc->packet_count == 0) {
- header_data = speex_header_to_packet (&speexenc->header, &size);
+ if (!speexenc->header_sent) {
+ /* Speex streams begin with two headers; the initial header (with
+ most of the codec setup parameters) which is mandated by the Ogg
+ bitstream spec. The second header holds any comment fields.
+ We merely need to make the headers, then pass them to libspeex
+ one at a time; libspeex handles the additional Ogg bitstream
+ constraints */
+ GstBuffer *buf1, *buf2;
+ GstCaps *caps;
+ guchar *data;
+ gint data_len;
- outbuf = gst_buffer_new ();
- GST_BUFFER_DATA (outbuf) = header_data;
- GST_BUFFER_SIZE (outbuf) = size;
+ gst_speexenc_set_metadata (speexenc);
- gst_pad_push (speexenc->srcpad, GST_DATA (outbuf));
- }
+ /* create header buffer */
+ data = speex_header_to_packet (&speexenc->header, &data_len);
+ buf1 = gst_speexenc_buffer_from_data (speexenc, data, data_len, 0);
- data = (gint16 *) GST_BUFFER_DATA (buf);
- size = GST_BUFFER_SIZE (buf) / sizeof (gint16);
+ /* create comment buffer */
+ buf2 =
+ gst_speexenc_buffer_from_data (speexenc, speexenc->comments,
+ speexenc->comment_len, 0);
- frame_size = speexenc->frame_size;
+ /* mark and put on caps */
+ caps = gst_pad_get_caps (speexenc->srcpad);
+ gst_speexenc_set_header_on_caps (caps, buf1, buf2);
- if (speexenc->bufsize && (speexenc->bufsize + size >= frame_size)) {
- memcpy (speexenc->buffer + speexenc->bufsize, data,
- (frame_size - speexenc->bufsize) * sizeof (gint16));
+ /* negotiate with these caps */
+ GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps);
+ gst_pad_try_set_caps (speexenc->srcpad, caps);
- for (i = 0; i < frame_size; i++)
- input[i] = speexenc->buffer[i];
+ /* push out buffers */
+ gst_speexenc_push_buffer (speexenc, buf1);
+ gst_speexenc_push_buffer (speexenc, buf2);
- speex_encode (speexenc->state, input, &speexenc->bits);
- speexenc->packet_count++;
-
- if (speexenc->packet_count % speexenc->n_packets == 0) {
- GstBuffer *outbuf;
-
- outbuf = gst_buffer_new_and_alloc (frame_size * speexenc->n_packets);
- GST_BUFFER_SIZE (outbuf) = speex_bits_write (&speexenc->bits,
- GST_BUFFER_DATA (outbuf), GST_BUFFER_SIZE (outbuf));
- GST_BUFFER_TIMESTAMP (outbuf) = speexenc->next_ts;
+ speex_bits_init (&speexenc->bits);
speex_bits_reset (&speexenc->bits);
- gst_pad_push (speexenc->srcpad, GST_DATA (outbuf));
- speexenc->next_ts += frame_size * GST_SECOND / speexenc->rate;
+ speexenc->header_sent = TRUE;
}
- size -= (speexenc->frame_size - speexenc->bufsize);
- data += (speexenc->frame_size - speexenc->bufsize);
+ {
+ gint frame_size = speexenc->frame_size;
+ gint bytes = frame_size * 2 * speexenc->channels;
- speexenc->bufsize = 0;
- }
+ /* push buffer to adapter */
+ gst_adapter_push (speexenc->adapter, buf);
- while (size >= frame_size) {
- for (i = 0; i < frame_size; i++)
- input[i] = data[i];
+ while (gst_adapter_available (speexenc->adapter) >= bytes) {
+ gint16 *data;
+ gint i;
+ gint outsize, written;
+ GstBuffer *outbuf;
- speex_encode (speexenc->state, input, &speexenc->bits);
- speexenc->packet_count++;
+ data = (gint16 *) gst_adapter_peek (speexenc->adapter, bytes);
- if (speexenc->packet_count % speexenc->n_packets == 0) {
- GstBuffer *outbuf;
+ for (i = 0; i < frame_size * speexenc->channels; i++) {
+ speexenc->input[i] = (gfloat) data[i];
+ }
+ gst_adapter_flush (speexenc->adapter, bytes);
- outbuf = gst_buffer_new_and_alloc (frame_size * speexenc->n_packets);
- GST_BUFFER_SIZE (outbuf) = speex_bits_write (&speexenc->bits,
- GST_BUFFER_DATA (outbuf), GST_BUFFER_SIZE (outbuf));
- GST_BUFFER_TIMESTAMP (outbuf) = speexenc->next_ts;
- speex_bits_reset (&speexenc->bits);
+ speexenc->samples_in += frame_size;
+
+ if (speexenc->channels == 2) {
+ speex_encode_stereo (speexenc->input, frame_size, &speexenc->bits);
+ }
+ speex_encode (speexenc->state, speexenc->input, &speexenc->bits);
+
+ speexenc->frameno++;
- gst_pad_push (speexenc->srcpad, GST_DATA (outbuf));
- speexenc->next_ts += frame_size * GST_SECOND / speexenc->rate;
+ if ((speexenc->frameno % speexenc->nframes) != 0)
+ continue;
+
+ speex_bits_insert_terminator (&speexenc->bits);
+ outsize = speex_bits_nbytes (&speexenc->bits);
+ outbuf =
+ gst_pad_alloc_buffer (speexenc->srcpad, GST_BUFFER_OFFSET_NONE,
+ outsize);
+ written =
+ speex_bits_write (&speexenc->bits, GST_BUFFER_DATA (outbuf),
+ outsize);
+ g_assert (written == outsize);
+ speex_bits_reset (&speexenc->bits);
+
+ GST_BUFFER_OFFSET (outbuf) = speexenc->bytes_out;
+ GST_BUFFER_OFFSET_END (outbuf) =
+ speexenc->frameno * frame_size - speexenc->lookahead;
+
+ gst_speexenc_push_buffer (speexenc, outbuf);
+ }
}
+ }
+
+ if (speexenc->eos) {
+ /* clean up and exit. */
+ gst_pad_push (speexenc->srcpad, GST_DATA (gst_event_new (GST_EVENT_EOS)));
+ gst_element_set_eos (GST_ELEMENT (speexenc));
+ }
+}
+
+static void
+gst_speexenc_get_property (GObject * object, guint prop_id, GValue * value,
+ GParamSpec * pspec)
+{
+ GstSpeexEnc *speexenc;
+
+ /* it's not null if we got it, but it might not be ours */
+ g_return_if_fail (GST_IS_SPEEXENC (object));
+
+ speexenc = GST_SPEEXENC (object);
- size -= frame_size;
- data += frame_size;
+ switch (prop_id) {
+ case ARG_QUALITY:
+ g_value_set_float (value, speexenc->quality);
+ break;
+ case ARG_BITRATE:
+ g_value_set_int (value, speexenc->bitrate);
+ break;
+ case ARG_VBR:
+ g_value_set_boolean (value, speexenc->vbr);
+ break;
+ case ARG_ABR:
+ g_value_set_int (value, speexenc->abr);
+ break;
+ case ARG_VAD:
+ g_value_set_boolean (value, speexenc->vad);
+ break;
+ case ARG_DTX:
+ g_value_set_boolean (value, speexenc->dtx);
+ break;
+ case ARG_COMPLEXITY:
+ g_value_set_int (value, speexenc->complexity);
+ break;
+ case ARG_NFRAMES:
+ g_value_set_int (value, speexenc->nframes);
+ break;
+ case ARG_LAST_MESSAGE:
+ g_value_set_string (value, speexenc->last_message);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
}
+}
+
+static void
+gst_speexenc_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstSpeexEnc *speexenc;
+
+ /* it's not null if we got it, but it might not be ours */
+ g_return_if_fail (GST_IS_SPEEXENC (object));
+
+ speexenc = GST_SPEEXENC (object);
- if (size) {
- memcpy (speexenc->buffer + speexenc->bufsize, data, size * sizeof (gint16));
- speexenc->bufsize += size;
+ switch (prop_id) {
+ case ARG_QUALITY:
+ speexenc->quality = g_value_get_float (value);
+ break;
+ case ARG_BITRATE:
+ speexenc->bitrate = g_value_get_int (value);
+ break;
+ case ARG_VBR:
+ speexenc->vbr = g_value_get_boolean (value);
+ break;
+ case ARG_ABR:
+ speexenc->abr = g_value_get_int (value);
+ break;
+ case ARG_VAD:
+ speexenc->vad = g_value_get_boolean (value);
+ break;
+ case ARG_DTX:
+ speexenc->dtx = g_value_get_boolean (value);
+ break;
+ case ARG_COMPLEXITY:
+ speexenc->complexity = g_value_get_int (value);
+ break;
+ case ARG_NFRAMES:
+ speexenc->nframes = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
}
+}
+
+static GstElementStateReturn
+gst_speexenc_change_state (GstElement * element)
+{
+ GstSpeexEnc *speexenc = GST_SPEEXENC (element);
+
+ switch (GST_STATE_TRANSITION (element)) {
+ case GST_STATE_NULL_TO_READY:
+ break;
+ case GST_STATE_READY_TO_PAUSED:
+ speexenc->eos = FALSE;
+ speexenc->frameno = 0;
+ speexenc->samples_in = 0;
+ break;
+ case GST_STATE_PAUSED_TO_PLAYING:
+ case GST_STATE_PLAYING_TO_PAUSED:
+ break;
+ case GST_STATE_PAUSED_TO_READY:
+ speexenc->setup = FALSE;
+ speexenc->header_sent = FALSE;
+ gst_tag_list_free (speexenc->tags);
+ speexenc->tags = gst_tag_list_new ();
+ break;
+ case GST_STATE_READY_TO_NULL:
+ default:
+ break;
+ }
+
+ if (GST_ELEMENT_CLASS (parent_class)->change_state)
+ return GST_ELEMENT_CLASS (parent_class)->change_state (element);
- gst_buffer_unref (buf);
+ return GST_STATE_SUCCESS;
}
diff --git a/ext/speex/gstspeexenc.h b/ext/speex/gstspeexenc.h
index 4677f6a1..a8a83805 100644
--- a/ext/speex/gstspeexenc.h
+++ b/ext/speex/gstspeexenc.h
@@ -23,6 +23,7 @@
#include <gst/gst.h>
+#include <gst/bytestream/adapter.h>
#include <speex.h>
#include <speex_header.h>
@@ -43,6 +44,17 @@ extern "C" {
#define GST_IS_SPEEXENC_CLASS(obj) \
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SPEEXENC))
+#define MAX_FRAME_SIZE 2000*2
+#define MAX_FRAME_BYTES 2000
+
+typedef enum
+{
+ GST_SPEEXENC_MODE_AUTO,
+ GST_SPEEXENC_MODE_UWB,
+ GST_SPEEXENC_MODE_WB,
+ GST_SPEEXENC_MODE_NB,
+} GstSpeexMode;
+
typedef struct _GstSpeexEnc GstSpeexEnc;
typedef struct _GstSpeexEncClass GstSpeexEncClass;
@@ -58,14 +70,43 @@ struct _GstSpeexEnc {
SpeexBits bits;
SpeexHeader header;
- SpeexMode *mode;
+ SpeexMode *speex_mode;
void *state;
+ GstSpeexMode mode;
+ GstAdapter *adapter;
+
+ gfloat quality;
+ gint bitrate;
+ gboolean vbr;
+ gint abr;
+ gboolean vad;
+ gboolean dtx;
+ gint complexity;
+ gint nframes;
+
+ gint lookahead;
+
+ gint channels;
+ gint rate;
+
+ gboolean setup;
+ gboolean header_sent;
+ gboolean eos;
+
+ guint64 samples_in;
+ guint64 bytes_out;
+
+ GstTagList *tags;
+
+ gchar *last_message;
+
gint frame_size;
- gint16 buffer[2000];
- gint bufsize;
- guint64 next_ts;
+ guint64 frameno;
+
+ gchar *comments;
+ gint comment_len;
- gint rate;
+ gfloat input[MAX_FRAME_SIZE];
};
struct _GstSpeexEncClass {