1 /* GStreamer Speex Encoder
2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:element-speexenc
22 * @see_also: speexdec, oggmux
24 * This element encodes audio as a Speex stream.
25 * <ulink url="http://www.speex.org/">Speex</ulink> is a royalty-free
26 * audio codec maintained by the <ulink url="http://www.xiph.org/">Xiph.org
30 * <title>Example pipelines</title>
32 * gst-launch audiotestsrc num-buffers=100 ! speexenc ! oggmux ! filesink location=beep.ogg
33 * ]| Encode an Ogg/Speex file.
44 #include <speex/speex.h>
45 #include <speex/speex_stereo.h>
47 #include <gst/gsttagsetter.h>
48 #include <gst/tag/tag.h>
49 #include <gst/audio/audio.h>
50 #include "gstspeexenc.h"
52 GST_DEBUG_CATEGORY_STATIC (speexenc_debug);
53 #define GST_CAT_DEFAULT speexenc_debug
55 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
58 GST_STATIC_CAPS ("audio/x-raw-int, "
59 "rate = (int) [ 6000, 48000 ], "
60 "channels = (int) [ 1, 2 ], "
61 "endianness = (int) BYTE_ORDER, "
62 "signed = (boolean) TRUE, " "width = (int) 16, " "depth = (int) 16")
65 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
68 GST_STATIC_CAPS ("audio/x-speex, "
69 "rate = (int) [ 6000, 48000 ], " "channels = (int) [ 1, 2]")
72 #define DEFAULT_QUALITY 8.0
73 #define DEFAULT_BITRATE 0
74 #define DEFAULT_MODE GST_SPEEX_ENC_MODE_AUTO
75 #define DEFAULT_VBR FALSE
77 #define DEFAULT_VAD FALSE
78 #define DEFAULT_DTX FALSE
79 #define DEFAULT_COMPLEXITY 3
80 #define DEFAULT_NFRAMES 1
97 #define GST_TYPE_SPEEX_ENC_MODE (gst_speex_enc_mode_get_type())
99 gst_speex_enc_mode_get_type (void)
101 static GType speex_enc_mode_type = 0;
102 static const GEnumValue speex_enc_modes[] = {
103 {GST_SPEEX_ENC_MODE_AUTO, "Auto", "auto"},
104 {GST_SPEEX_ENC_MODE_UWB, "Ultra Wide Band", "uwb"},
105 {GST_SPEEX_ENC_MODE_WB, "Wide Band", "wb"},
106 {GST_SPEEX_ENC_MODE_NB, "Narrow Band", "nb"},
109 if (G_UNLIKELY (speex_enc_mode_type == 0)) {
110 speex_enc_mode_type = g_enum_register_static ("GstSpeexEncMode",
113 return speex_enc_mode_type;
117 static const GstFormat *
118 gst_speex_enc_get_formats (GstPad * pad)
120 static const GstFormat src_formats[] = {
125 static const GstFormat sink_formats[] = {
132 return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
136 static void gst_speex_enc_finalize (GObject * object);
138 static gboolean gst_speex_enc_sink_event (GstPad * pad, GstEvent * event);
139 static GstFlowReturn gst_speex_enc_chain (GstPad * pad, GstBuffer * buf);
140 static gboolean gst_speex_enc_setup (GstSpeexEnc * enc);
142 static void gst_speex_enc_get_property (GObject * object, guint prop_id,
143 GValue * value, GParamSpec * pspec);
144 static void gst_speex_enc_set_property (GObject * object, guint prop_id,
145 const GValue * value, GParamSpec * pspec);
146 static GstStateChangeReturn gst_speex_enc_change_state (GstElement * element,
147 GstStateChange transition);
149 static GstFlowReturn gst_speex_enc_encode (GstSpeexEnc * enc, gboolean flush);
151 #define gst_speex_enc_parent_class parent_class
152 G_DEFINE_TYPE_WITH_CODE (GstSpeexEnc, gst_speex_enc, GST_TYPE_ELEMENT,
153 G_IMPLEMENT_INTERFACE (GST_TYPE_TAG_SETTER, NULL);
154 G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL));
157 gst_speex_enc_class_init (GstSpeexEncClass * klass)
159 GObjectClass *gobject_class;
160 GstElementClass *gstelement_class;
162 gobject_class = (GObjectClass *) klass;
163 gstelement_class = (GstElementClass *) klass;
165 gobject_class->finalize = gst_speex_enc_finalize;
166 gobject_class->set_property = gst_speex_enc_set_property;
167 gobject_class->get_property = gst_speex_enc_get_property;
169 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_QUALITY,
170 g_param_spec_float ("quality", "Quality", "Encoding quality",
171 0.0, 10.0, DEFAULT_QUALITY,
172 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
173 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BITRATE,
174 g_param_spec_int ("bitrate", "Encoding Bit-rate",
175 "Specify an encoding bit-rate (in bps). (0 = automatic)",
176 0, G_MAXINT, DEFAULT_BITRATE,
177 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
178 g_object_class_install_property (gobject_class, PROP_MODE,
179 g_param_spec_enum ("mode", "Mode", "The encoding mode",
180 GST_TYPE_SPEEX_ENC_MODE, GST_SPEEX_ENC_MODE_AUTO,
181 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
182 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VBR,
183 g_param_spec_boolean ("vbr", "VBR",
184 "Enable variable bit-rate", DEFAULT_VBR,
185 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
186 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ABR,
187 g_param_spec_int ("abr", "ABR",
188 "Enable average bit-rate (0 = disabled)",
189 0, G_MAXINT, DEFAULT_ABR,
190 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
191 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VAD,
192 g_param_spec_boolean ("vad", "VAD",
193 "Enable voice activity detection", DEFAULT_VAD,
194 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
195 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DTX,
196 g_param_spec_boolean ("dtx", "DTX",
197 "Enable discontinuous transmission", DEFAULT_DTX,
198 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
199 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COMPLEXITY,
200 g_param_spec_int ("complexity", "Complexity",
201 "Set encoding complexity",
202 0, G_MAXINT, DEFAULT_COMPLEXITY,
203 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
204 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_NFRAMES,
205 g_param_spec_int ("nframes", "NFrames",
206 "Number of frames per buffer",
207 0, G_MAXINT, DEFAULT_NFRAMES,
208 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
209 g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LAST_MESSAGE,
210 g_param_spec_string ("last-message", "last-message",
211 "The last status message", NULL,
212 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
214 gstelement_class->change_state =
215 GST_DEBUG_FUNCPTR (gst_speex_enc_change_state);
217 gst_element_class_add_pad_template (gstelement_class,
218 gst_static_pad_template_get (&src_factory));
219 gst_element_class_add_pad_template (gstelement_class,
220 gst_static_pad_template_get (&sink_factory));
221 gst_element_class_set_details_simple (gstelement_class, "Speex audio encoder",
222 "Codec/Encoder/Audio",
223 "Encodes audio in Speex format", "Wim Taymans <wim@fluendo.com>");
225 GST_DEBUG_CATEGORY_INIT (speexenc_debug, "speexenc", 0, "Speex encoder");
229 gst_speex_enc_finalize (GObject * object)
233 enc = GST_SPEEX_ENC (object);
235 g_free (enc->last_message);
236 g_object_unref (enc->adapter);
238 G_OBJECT_CLASS (parent_class)->finalize (object);
242 gst_speex_enc_sink_setcaps (GstPad * pad, GstCaps * caps)
245 GstStructure *structure;
247 enc = GST_SPEEX_ENC (GST_PAD_PARENT (pad));
250 structure = gst_caps_get_structure (caps, 0);
251 gst_structure_get_int (structure, "channels", &enc->channels);
252 gst_structure_get_int (structure, "rate", &enc->rate);
254 gst_speex_enc_setup (enc);
261 gst_speex_enc_sink_getcaps (GstPad * pad, GstCaps * filter)
263 GstCaps *caps = gst_caps_copy (gst_pad_get_pad_template_caps (pad));
264 GstCaps *peercaps = NULL;
265 GstSpeexEnc *enc = GST_SPEEX_ENC (gst_pad_get_parent_element (pad));
267 peercaps = gst_pad_peer_get_caps (enc->srcpad, filter);
270 if (!gst_caps_is_empty (peercaps) && !gst_caps_is_any (peercaps)) {
271 GstStructure *ps = gst_caps_get_structure (peercaps, 0);
272 GstStructure *s = gst_caps_get_structure (caps, 0);
275 if (gst_structure_get_int (ps, "rate", &rate)) {
276 gst_structure_fixate_field_nearest_int (s, "rate", rate);
279 if (gst_structure_get_int (ps, "channels", &channels)) {
280 gst_structure_fixate_field_nearest_int (s, "channels", channels);
283 gst_caps_unref (peercaps);
286 gst_object_unref (enc);
293 gst_speex_enc_convert_src (GstPad * pad, GstFormat src_format, gint64 src_value,
294 GstFormat * dest_format, gint64 * dest_value)
300 enc = GST_SPEEX_ENC (GST_PAD_PARENT (pad));
302 if (enc->samples_in == 0 || enc->bytes_out == 0 || enc->rate == 0)
305 avg = (enc->bytes_out * enc->rate) / (enc->samples_in);
307 switch (src_format) {
308 case GST_FORMAT_BYTES:
309 switch (*dest_format) {
310 case GST_FORMAT_TIME:
311 *dest_value = src_value * GST_SECOND / avg;
317 case GST_FORMAT_TIME:
318 switch (*dest_format) {
319 case GST_FORMAT_BYTES:
320 *dest_value = src_value * avg / GST_SECOND;
333 gst_speex_enc_convert_sink (GstPad * pad, GstFormat src_format,
334 gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
338 gint bytes_per_sample;
341 enc = GST_SPEEX_ENC (GST_PAD_PARENT (pad));
343 bytes_per_sample = enc->channels * 2;
345 switch (src_format) {
346 case GST_FORMAT_BYTES:
347 switch (*dest_format) {
348 case GST_FORMAT_DEFAULT:
349 if (bytes_per_sample == 0)
351 *dest_value = src_value / bytes_per_sample;
353 case GST_FORMAT_TIME:
355 gint byterate = bytes_per_sample * enc->rate;
359 *dest_value = src_value * GST_SECOND / byterate;
366 case GST_FORMAT_DEFAULT:
367 switch (*dest_format) {
368 case GST_FORMAT_BYTES:
369 *dest_value = src_value * bytes_per_sample;
371 case GST_FORMAT_TIME:
374 *dest_value = src_value * GST_SECOND / enc->rate;
380 case GST_FORMAT_TIME:
381 switch (*dest_format) {
382 case GST_FORMAT_BYTES:
383 scale = bytes_per_sample;
385 case GST_FORMAT_DEFAULT:
386 *dest_value = src_value * scale * enc->rate / GST_SECOND;
399 gst_speex_enc_get_latency (GstSpeexEnc * enc)
401 /* See the Speex manual section "Latency and algorithmic delay" */
402 if (enc->rate == 8000)
403 return 30 * GST_MSECOND;
405 return 34 * GST_MSECOND;
408 static const GstQueryType *
409 gst_speex_enc_get_query_types (GstPad * pad)
411 static const GstQueryType gst_speex_enc_src_query_types[] = {
419 return gst_speex_enc_src_query_types;
423 gst_speex_enc_src_query (GstPad * pad, GstQuery * query)
428 enc = GST_SPEEX_ENC (gst_pad_get_parent (pad));
430 switch (GST_QUERY_TYPE (query)) {
431 case GST_QUERY_POSITION:
433 GstFormat fmt, req_fmt;
436 gst_query_parse_position (query, &req_fmt, NULL);
437 if ((res = gst_pad_query_peer_position (enc->sinkpad, &req_fmt, &val))) {
438 gst_query_set_position (query, req_fmt, val);
442 fmt = GST_FORMAT_TIME;
443 if (!(res = gst_pad_query_peer_position (enc->sinkpad, &fmt, &pos)))
447 gst_pad_query_peer_convert (enc->sinkpad, fmt, pos, &req_fmt,
449 gst_query_set_position (query, req_fmt, val);
453 case GST_QUERY_DURATION:
455 GstFormat fmt, req_fmt;
458 gst_query_parse_duration (query, &req_fmt, NULL);
459 if ((res = gst_pad_query_peer_duration (enc->sinkpad, &req_fmt, &val))) {
460 gst_query_set_duration (query, req_fmt, val);
464 fmt = GST_FORMAT_TIME;
465 if (!(res = gst_pad_query_peer_duration (enc->sinkpad, &fmt, &dur)))
469 gst_pad_query_peer_convert (enc->sinkpad, fmt, dur, &req_fmt,
471 gst_query_set_duration (query, req_fmt, val);
475 case GST_QUERY_CONVERT:
477 GstFormat src_fmt, dest_fmt;
478 gint64 src_val, dest_val;
480 gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
481 if (!(res = gst_speex_enc_convert_src (pad, src_fmt, src_val, &dest_fmt,
484 gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
487 case GST_QUERY_LATENCY:
490 GstClockTime min_latency, max_latency;
493 if ((res = gst_pad_peer_query (enc->sinkpad, query))) {
494 gst_query_parse_latency (query, &live, &min_latency, &max_latency);
495 GST_LOG_OBJECT (pad, "Upstream latency: %" GST_PTR_FORMAT, query);
497 latency = gst_speex_enc_get_latency (enc);
499 /* add our latency */
500 min_latency += latency;
501 if (max_latency != -1)
502 max_latency += latency;
504 gst_query_set_latency (query, live, min_latency, max_latency);
505 GST_LOG_OBJECT (pad, "Adjusted latency: %" GST_PTR_FORMAT, query);
510 res = gst_pad_peer_query (enc->sinkpad, query);
516 gst_object_unref (enc);
522 gst_speex_enc_sink_query (GstPad * pad, GstQuery * query)
526 switch (GST_QUERY_TYPE (query)) {
527 case GST_QUERY_CONVERT:
529 GstFormat src_fmt, dest_fmt;
530 gint64 src_val, dest_val;
532 gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
534 gst_speex_enc_convert_sink (pad, src_fmt, src_val, &dest_fmt,
537 gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
541 res = gst_pad_query_default (pad, query);
550 gst_speex_enc_init (GstSpeexEnc * enc)
552 enc->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
553 gst_element_add_pad (GST_ELEMENT (enc), enc->sinkpad);
554 gst_pad_set_event_function (enc->sinkpad,
555 GST_DEBUG_FUNCPTR (gst_speex_enc_sink_event));
556 gst_pad_set_chain_function (enc->sinkpad,
557 GST_DEBUG_FUNCPTR (gst_speex_enc_chain));
558 gst_pad_set_getcaps_function (enc->sinkpad,
559 GST_DEBUG_FUNCPTR (gst_speex_enc_sink_getcaps));
560 gst_pad_set_query_function (enc->sinkpad,
561 GST_DEBUG_FUNCPTR (gst_speex_enc_sink_query));
563 enc->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
564 gst_pad_set_query_function (enc->srcpad,
565 GST_DEBUG_FUNCPTR (gst_speex_enc_src_query));
566 gst_pad_set_query_type_function (enc->srcpad,
567 GST_DEBUG_FUNCPTR (gst_speex_enc_get_query_types));
568 gst_element_add_pad (GST_ELEMENT (enc), enc->srcpad);
573 enc->quality = DEFAULT_QUALITY;
574 enc->bitrate = DEFAULT_BITRATE;
575 enc->mode = DEFAULT_MODE;
576 enc->vbr = DEFAULT_VBR;
577 enc->abr = DEFAULT_ABR;
578 enc->vad = DEFAULT_VAD;
579 enc->dtx = DEFAULT_DTX;
580 enc->complexity = DEFAULT_COMPLEXITY;
581 enc->nframes = DEFAULT_NFRAMES;
584 enc->header_sent = FALSE;
586 enc->adapter = gst_adapter_new ();
590 gst_speex_enc_create_metadata_buffer (GstSpeexEnc * enc)
592 const GstTagList *user_tags;
593 GstTagList *merged_tags;
594 GstBuffer *comments = NULL;
596 user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc));
598 GST_DEBUG_OBJECT (enc, "upstream tags = %" GST_PTR_FORMAT, enc->tags);
599 GST_DEBUG_OBJECT (enc, "user-set tags = %" GST_PTR_FORMAT, user_tags);
601 /* gst_tag_list_merge() will handle NULL for either or both lists fine */
602 merged_tags = gst_tag_list_merge (user_tags, enc->tags,
603 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (enc)));
605 if (merged_tags == NULL)
606 merged_tags = gst_tag_list_new ();
608 GST_DEBUG_OBJECT (enc, "merged tags = %" GST_PTR_FORMAT, merged_tags);
609 comments = gst_tag_list_to_vorbiscomment_buffer (merged_tags, NULL,
610 0, "Encoded with GStreamer Speexenc");
611 gst_tag_list_free (merged_tags);
613 GST_BUFFER_OFFSET (comments) = enc->bytes_out;
614 GST_BUFFER_OFFSET_END (comments) = 0;
620 gst_speex_enc_set_last_msg (GstSpeexEnc * enc, const gchar * msg)
622 g_free (enc->last_message);
623 enc->last_message = g_strdup (msg);
624 GST_WARNING_OBJECT (enc, "%s", msg);
625 g_object_notify (G_OBJECT (enc), "last-message");
629 gst_speex_enc_setup (GstSpeexEnc * enc)
634 case GST_SPEEX_ENC_MODE_UWB:
635 GST_LOG_OBJECT (enc, "configuring for requested UWB mode");
636 enc->speex_mode = speex_lib_get_mode (SPEEX_MODEID_UWB);
638 case GST_SPEEX_ENC_MODE_WB:
639 GST_LOG_OBJECT (enc, "configuring for requested WB mode");
640 enc->speex_mode = speex_lib_get_mode (SPEEX_MODEID_WB);
642 case GST_SPEEX_ENC_MODE_NB:
643 GST_LOG_OBJECT (enc, "configuring for requested NB mode");
644 enc->speex_mode = speex_lib_get_mode (SPEEX_MODEID_NB);
646 case GST_SPEEX_ENC_MODE_AUTO:
648 GST_LOG_OBJECT (enc, "finding best mode");
653 if (enc->rate > 25000) {
654 if (enc->mode == GST_SPEEX_ENC_MODE_AUTO) {
655 GST_LOG_OBJECT (enc, "selected UWB mode for samplerate %d", enc->rate);
656 enc->speex_mode = speex_lib_get_mode (SPEEX_MODEID_UWB);
658 if (enc->speex_mode != speex_lib_get_mode (SPEEX_MODEID_UWB)) {
659 gst_speex_enc_set_last_msg (enc,
660 "Warning: suggest to use ultra wide band mode for this rate");
663 } else if (enc->rate > 12500) {
664 if (enc->mode == GST_SPEEX_ENC_MODE_AUTO) {
665 GST_LOG_OBJECT (enc, "selected WB mode for samplerate %d", enc->rate);
666 enc->speex_mode = speex_lib_get_mode (SPEEX_MODEID_WB);
668 if (enc->speex_mode != speex_lib_get_mode (SPEEX_MODEID_WB)) {
669 gst_speex_enc_set_last_msg (enc,
670 "Warning: suggest to use wide band mode for this rate");
674 if (enc->mode == GST_SPEEX_ENC_MODE_AUTO) {
675 GST_LOG_OBJECT (enc, "selected NB mode for samplerate %d", enc->rate);
676 enc->speex_mode = speex_lib_get_mode (SPEEX_MODEID_NB);
678 if (enc->speex_mode != speex_lib_get_mode (SPEEX_MODEID_NB)) {
679 gst_speex_enc_set_last_msg (enc,
680 "Warning: suggest to use narrow band mode for this rate");
685 if (enc->rate != 8000 && enc->rate != 16000 && enc->rate != 32000) {
686 gst_speex_enc_set_last_msg (enc,
687 "Warning: speex is optimized for 8, 16 and 32 KHz");
690 speex_init_header (&enc->header, enc->rate, 1, enc->speex_mode);
691 enc->header.frames_per_packet = enc->nframes;
692 enc->header.vbr = enc->vbr;
693 enc->header.nb_channels = enc->channels;
695 /*Initialize Speex encoder */
696 enc->state = speex_encoder_init (enc->speex_mode);
698 speex_encoder_ctl (enc->state, SPEEX_GET_FRAME_SIZE, &enc->frame_size);
699 speex_encoder_ctl (enc->state, SPEEX_SET_COMPLEXITY, &enc->complexity);
700 speex_encoder_ctl (enc->state, SPEEX_SET_SAMPLING_RATE, &enc->rate);
703 speex_encoder_ctl (enc->state, SPEEX_SET_VBR_QUALITY, &enc->quality);
705 gint tmp = floor (enc->quality);
707 speex_encoder_ctl (enc->state, SPEEX_SET_QUALITY, &tmp);
710 if (enc->quality >= 0.0 && enc->vbr) {
711 gst_speex_enc_set_last_msg (enc,
712 "Warning: bitrate option is overriding quality");
714 speex_encoder_ctl (enc->state, SPEEX_SET_BITRATE, &enc->bitrate);
719 speex_encoder_ctl (enc->state, SPEEX_SET_VBR, &tmp);
720 } else if (enc->vad) {
723 speex_encoder_ctl (enc->state, SPEEX_SET_VAD, &tmp);
729 speex_encoder_ctl (enc->state, SPEEX_SET_DTX, &tmp);
732 if (enc->dtx && !(enc->vbr || enc->abr || enc->vad)) {
733 gst_speex_enc_set_last_msg (enc,
734 "Warning: dtx is useless without vad, vbr or abr");
735 } else if ((enc->vbr || enc->abr) && (enc->vad)) {
736 gst_speex_enc_set_last_msg (enc,
737 "Warning: vad is already implied by vbr or abr");
741 speex_encoder_ctl (enc->state, SPEEX_SET_ABR, &enc->abr);
744 speex_encoder_ctl (enc->state, SPEEX_GET_LOOKAHEAD, &enc->lookahead);
746 GST_LOG_OBJECT (enc, "we have frame size %d, lookahead %d", enc->frame_size,
754 /* prepare a buffer for transmission */
756 gst_speex_enc_buffer_from_data (GstSpeexEnc * enc, guchar * data,
757 gint data_len, guint64 granulepos)
761 outbuf = gst_buffer_new_and_alloc (data_len);
762 gst_buffer_fill (outbuf, 0, data, data_len);
763 GST_BUFFER_OFFSET (outbuf) = enc->bytes_out;
764 GST_BUFFER_OFFSET_END (outbuf) = granulepos;
766 GST_LOG_OBJECT (enc, "encoded buffer of %d bytes", data_len);
771 /* push out the buffer and do internal bookkeeping */
773 gst_speex_enc_push_buffer (GstSpeexEnc * enc, GstBuffer * buffer)
777 size = gst_buffer_get_size (buffer);
778 enc->bytes_out += size;
780 GST_DEBUG_OBJECT (enc, "pushing output buffer of size %u", size);
782 return gst_pad_push (enc->srcpad, buffer);
786 gst_speex_enc_set_header_on_caps (GstCaps * caps, GstBuffer * buf1,
789 GstStructure *structure = NULL;
791 GValue array = { 0 };
792 GValue value = { 0 };
794 caps = gst_caps_make_writable (caps);
795 structure = gst_caps_get_structure (caps, 0);
797 g_assert (gst_buffer_is_writable (buf1));
798 g_assert (gst_buffer_is_writable (buf2));
801 GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS);
802 GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_IN_CAPS);
804 /* put buffers in a fixed list */
805 g_value_init (&array, GST_TYPE_ARRAY);
806 g_value_init (&value, GST_TYPE_BUFFER);
807 buf = gst_buffer_copy (buf1);
808 gst_value_set_buffer (&value, buf);
809 gst_buffer_unref (buf);
810 gst_value_array_append_value (&array, &value);
811 g_value_unset (&value);
812 g_value_init (&value, GST_TYPE_BUFFER);
813 buf = gst_buffer_copy (buf2);
814 gst_value_set_buffer (&value, buf);
815 gst_buffer_unref (buf);
816 gst_value_array_append_value (&array, &value);
817 gst_structure_set_value (structure, "streamheader", &array);
818 g_value_unset (&value);
819 g_value_unset (&array);
826 gst_speex_enc_sink_event (GstPad * pad, GstEvent * event)
831 enc = GST_SPEEX_ENC (gst_pad_get_parent (pad));
833 switch (GST_EVENT_TYPE (event)) {
838 gst_event_parse_caps (event, &caps);
839 res = gst_speex_enc_sink_setcaps (pad, caps);
840 gst_event_unref (event);
845 gst_speex_enc_encode (enc, TRUE);
846 res = gst_pad_event_default (pad, event);
853 gst_event_parse_tag (event, &list);
854 gst_tag_list_insert (enc->tags, list,
855 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (enc)));
857 g_assert_not_reached ();
859 res = gst_pad_event_default (pad, event);
863 res = gst_pad_event_default (pad, event);
867 gst_object_unref (enc);
873 gst_speex_enc_encode (GstSpeexEnc * enc, gboolean flush)
875 gint frame_size = enc->frame_size;
876 gint bytes = frame_size * 2 * enc->channels;
877 GstFlowReturn ret = GST_FLOW_OK;
879 if (flush && gst_adapter_available (enc->adapter) % bytes != 0) {
880 guint diff = gst_adapter_available (enc->adapter) % bytes;
881 GstBuffer *buf = gst_buffer_new_and_alloc (diff);
882 gst_buffer_memset (buf, 0, 0, diff);
883 gst_adapter_push (enc->adapter, buf);
886 while (gst_adapter_available (enc->adapter) >= bytes) {
888 gint outsize, written, dtx_ret;
892 data = (gint16 *) gst_adapter_take (enc->adapter, bytes);
894 enc->samples_in += frame_size;
896 GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", frame_size, bytes);
898 if (enc->channels == 2) {
899 speex_encode_stereo_int (data, frame_size, &enc->bits);
901 dtx_ret = speex_encode_int (enc->state, data, &enc->bits);
908 if ((enc->frameno % enc->nframes) != 0)
911 speex_bits_insert_terminator (&enc->bits);
912 outsize = speex_bits_nbytes (&enc->bits);
915 ret = gst_pad_alloc_buffer_and_set_caps (enc->srcpad,
916 GST_BUFFER_OFFSET_NONE, outsize, GST_PAD_CAPS (enc->srcpad), &outbuf);
917 if ((GST_FLOW_OK != ret))
920 outbuf = gst_buffer_new_allocate (NULL, outsize, 0);
922 outdata = gst_buffer_map (outbuf, NULL, NULL, GST_MAP_WRITE);
923 written = speex_bits_write (&enc->bits, outdata, outsize);
925 if (G_UNLIKELY (written != outsize)) {
926 GST_ERROR_OBJECT (enc, "short write: %d < %d bytes", written, outsize);
928 gst_buffer_unmap (outbuf, outdata, written);
930 speex_bits_reset (&enc->bits);
933 GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
935 GST_BUFFER_TIMESTAMP (outbuf) = enc->start_ts +
936 gst_util_uint64_scale_int ((enc->frameno_out -
937 enc->nframes) * frame_size - enc->lookahead, GST_SECOND, enc->rate);
938 GST_BUFFER_DURATION (outbuf) =
939 gst_util_uint64_scale_int (frame_size * enc->nframes, GST_SECOND,
941 /* set gp time and granulepos; see gst-plugins-base/ext/ogg/README */
942 GST_BUFFER_OFFSET_END (outbuf) = enc->granulepos_offset +
943 ((enc->frameno_out) * frame_size - enc->lookahead);
944 GST_BUFFER_OFFSET (outbuf) =
945 gst_util_uint64_scale_int (GST_BUFFER_OFFSET_END (outbuf), GST_SECOND,
948 ret = gst_speex_enc_push_buffer (enc, outbuf);
950 if ((GST_FLOW_OK != ret) && (GST_FLOW_NOT_LINKED != ret))
960 gst_speex_enc_chain (GstPad * pad, GstBuffer * buf)
963 GstFlowReturn ret = GST_FLOW_OK;
965 enc = GST_SPEEX_ENC (GST_PAD_PARENT (pad));
970 if (!enc->header_sent) {
971 /* Speex streams begin with two headers; the initial header (with
972 most of the codec setup parameters) which is mandated by the Ogg
973 bitstream spec. The second header holds any comment fields.
974 We merely need to make the headers, then pass them to libspeex
975 one at a time; libspeex handles the additional Ogg bitstream
977 GstBuffer *buf1, *buf2;
982 /* create header buffer */
983 data = (guint8 *) speex_header_to_packet (&enc->header, &data_len);
984 buf1 = gst_speex_enc_buffer_from_data (enc, data, data_len, 0);
987 /* create comment buffer */
988 buf2 = gst_speex_enc_create_metadata_buffer (enc);
990 /* mark and put on caps */
991 caps = gst_pad_get_caps (enc->srcpad, NULL);
992 caps = gst_speex_enc_set_header_on_caps (caps, buf1, buf2);
994 gst_caps_set_simple (caps,
995 "rate", G_TYPE_INT, enc->rate,
996 "channels", G_TYPE_INT, enc->channels, NULL);
998 /* negotiate with these caps */
999 GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps);
1000 gst_pad_set_caps (enc->srcpad, caps);
1001 gst_caps_unref (caps);
1003 /* push out buffers */
1004 ret = gst_speex_enc_push_buffer (enc, buf1);
1006 if (ret != GST_FLOW_OK) {
1007 gst_buffer_unref (buf2);
1011 ret = gst_speex_enc_push_buffer (enc, buf2);
1013 if (ret != GST_FLOW_OK)
1016 speex_bits_reset (&enc->bits);
1018 enc->header_sent = TRUE;
1021 /* Save the timestamp of the first buffer. This will be later
1022 * used as offset for all following buffers */
1023 if (enc->start_ts == GST_CLOCK_TIME_NONE) {
1024 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1025 enc->start_ts = GST_BUFFER_TIMESTAMP (buf);
1026 enc->granulepos_offset = gst_util_uint64_scale
1027 (GST_BUFFER_TIMESTAMP (buf), enc->rate, GST_SECOND);
1030 enc->granulepos_offset = 0;
1034 /* Check if we have a continous stream, if not drop some samples or the buffer or
1035 * insert some silence samples */
1036 if (enc->next_ts != GST_CLOCK_TIME_NONE &&
1037 GST_BUFFER_TIMESTAMP (buf) < enc->next_ts) {
1038 guint64 diff = enc->next_ts - GST_BUFFER_TIMESTAMP (buf);
1041 GST_WARNING_OBJECT (enc, "Buffer is older than previous "
1042 "timestamp + duration (%" GST_TIME_FORMAT "< %" GST_TIME_FORMAT
1043 "), cannot handle. Clipping buffer.",
1044 GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
1045 GST_TIME_ARGS (enc->next_ts));
1047 diff_bytes = GST_CLOCK_TIME_TO_FRAMES (diff, enc->rate) * enc->channels * 2;
1048 if (diff_bytes >= gst_buffer_get_size (buf)) {
1049 gst_buffer_unref (buf);
1052 buf = gst_buffer_make_writable (buf);
1053 gst_buffer_resize (buf, diff_bytes, -1);
1055 GST_BUFFER_TIMESTAMP (buf) += diff;
1056 if (GST_BUFFER_DURATION_IS_VALID (buf))
1057 GST_BUFFER_DURATION (buf) -= diff;
1060 if (enc->next_ts != GST_CLOCK_TIME_NONE
1061 && GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1063 gst_util_uint64_scale (enc->frame_size, GST_SECOND, enc->rate);
1065 if (GST_BUFFER_TIMESTAMP (buf) != enc->next_ts &&
1066 GST_BUFFER_TIMESTAMP (buf) - enc->next_ts > max_diff) {
1067 GST_WARNING_OBJECT (enc,
1068 "Discontinuity detected: %" G_GUINT64_FORMAT " > %" G_GUINT64_FORMAT,
1069 GST_BUFFER_TIMESTAMP (buf) - enc->next_ts, max_diff);
1071 gst_speex_enc_encode (enc, TRUE);
1073 enc->frameno_out = 0;
1074 enc->start_ts = GST_BUFFER_TIMESTAMP (buf);
1075 enc->granulepos_offset = gst_util_uint64_scale
1076 (GST_BUFFER_TIMESTAMP (buf), enc->rate, GST_SECOND);
1080 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)
1081 && GST_BUFFER_DURATION_IS_VALID (buf))
1082 enc->next_ts = GST_BUFFER_TIMESTAMP (buf) + GST_BUFFER_DURATION (buf);
1084 enc->next_ts = GST_CLOCK_TIME_NONE;
1086 GST_DEBUG_OBJECT (enc, "received buffer of %u bytes",
1087 gst_buffer_get_size (buf));
1089 /* push buffer to adapter */
1090 gst_adapter_push (enc->adapter, buf);
1093 ret = gst_speex_enc_encode (enc, FALSE);
1098 gst_buffer_unref (buf);
1105 GST_ELEMENT_ERROR (enc, CORE, NEGOTIATION, (NULL),
1106 ("encoder not initialized (input is not audio?)"));
1107 ret = GST_FLOW_NOT_NEGOTIATED;
1115 gst_speex_enc_get_property (GObject * object, guint prop_id, GValue * value,
1120 enc = GST_SPEEX_ENC (object);
1124 g_value_set_float (value, enc->quality);
1127 g_value_set_int (value, enc->bitrate);
1130 g_value_set_enum (value, enc->mode);
1133 g_value_set_boolean (value, enc->vbr);
1136 g_value_set_int (value, enc->abr);
1139 g_value_set_boolean (value, enc->vad);
1142 g_value_set_boolean (value, enc->dtx);
1144 case PROP_COMPLEXITY:
1145 g_value_set_int (value, enc->complexity);
1148 g_value_set_int (value, enc->nframes);
1150 case PROP_LAST_MESSAGE:
1151 g_value_set_string (value, enc->last_message);
1154 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1160 gst_speex_enc_set_property (GObject * object, guint prop_id,
1161 const GValue * value, GParamSpec * pspec)
1165 enc = GST_SPEEX_ENC (object);
1169 enc->quality = g_value_get_float (value);
1172 enc->bitrate = g_value_get_int (value);
1175 enc->mode = g_value_get_enum (value);
1178 enc->vbr = g_value_get_boolean (value);
1181 enc->abr = g_value_get_int (value);
1184 enc->vad = g_value_get_boolean (value);
1187 enc->dtx = g_value_get_boolean (value);
1189 case PROP_COMPLEXITY:
1190 enc->complexity = g_value_get_int (value);
1193 enc->nframes = g_value_get_int (value);
1196 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1201 static GstStateChangeReturn
1202 gst_speex_enc_change_state (GstElement * element, GstStateChange transition)
1204 GstSpeexEnc *enc = GST_SPEEX_ENC (element);
1205 GstStateChangeReturn res;
1207 switch (transition) {
1208 case GST_STATE_CHANGE_NULL_TO_READY:
1209 enc->tags = gst_tag_list_new ();
1211 case GST_STATE_CHANGE_READY_TO_PAUSED:
1212 speex_bits_init (&enc->bits);
1214 enc->frameno_out = 0;
1215 enc->samples_in = 0;
1216 enc->start_ts = GST_CLOCK_TIME_NONE;
1217 enc->next_ts = GST_CLOCK_TIME_NONE;
1218 enc->granulepos_offset = 0;
1220 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1226 res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1227 if (res == GST_STATE_CHANGE_FAILURE)
1230 switch (transition) {
1231 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1233 case GST_STATE_CHANGE_PAUSED_TO_READY:
1235 enc->header_sent = FALSE;
1237 speex_encoder_destroy (enc->state);
1240 speex_bits_destroy (&enc->bits);
1242 case GST_STATE_CHANGE_READY_TO_NULL:
1243 gst_tag_list_free (enc->tags);