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.
28 #include <speex/speex.h>
29 #include <speex/speex_stereo.h>
31 #include <gst/gsttagsetter.h>
32 #include <gst/tag/tag.h>
33 #include "gstspeexenc.h"
35 GST_DEBUG_CATEGORY (speexenc_debug);
36 #define GST_CAT_DEFAULT speexenc_debug
38 static GstPadTemplate *gst_speexenc_src_template, *gst_speexenc_sink_template;
40 /* elementfactory information */
41 GstElementDetails speexenc_details = {
43 "Codec/Encoder/Audio",
44 "Encodes audio in Speex format",
45 "Wim Taymans <wim@fluendo.com>",
48 /* GstSpeexEnc signals and args */
55 #define DEFAULT_QUALITY 8.0
56 #define DEFAULT_BITRATE 0
57 #define DEFAULT_VBR FALSE
59 #define DEFAULT_VAD FALSE
60 #define DEFAULT_DTX FALSE
61 #define DEFAULT_COMPLEXITY 3
62 #define DEFAULT_NFRAMES 1
79 static const GstFormat *
80 gst_speexenc_get_formats (GstPad * pad)
82 static const GstFormat src_formats[] = {
87 static const GstFormat sink_formats[] = {
94 return (GST_PAD_IS_SRC (pad) ? src_formats : sink_formats);
98 static void gst_speexenc_base_init (gpointer g_class);
99 static void gst_speexenc_class_init (GstSpeexEncClass * klass);
100 static void gst_speexenc_init (GstSpeexEnc * speexenc);
102 static gboolean gst_speexenc_sinkevent (GstPad * pad, GstEvent * event);
103 static GstFlowReturn gst_speexenc_chain (GstPad * pad, GstBuffer * buf);
104 static gboolean gst_speexenc_setup (GstSpeexEnc * speexenc);
106 static void gst_speexenc_get_property (GObject * object, guint prop_id,
107 GValue * value, GParamSpec * pspec);
108 static void gst_speexenc_set_property (GObject * object, guint prop_id,
109 const GValue * value, GParamSpec * pspec);
110 static GstStateChangeReturn gst_speexenc_change_state (GstElement * element,
111 GstStateChange transition);
113 static GstElementClass *parent_class = NULL;
115 /*static guint gst_speexenc_signals[LAST_SIGNAL] = { 0 }; */
118 gst_speexenc_get_type (void)
120 static GType speexenc_type = 0;
122 if (!speexenc_type) {
123 static const GTypeInfo speexenc_info = {
124 sizeof (GstSpeexEncClass),
125 gst_speexenc_base_init,
127 (GClassInitFunc) gst_speexenc_class_init,
130 sizeof (GstSpeexEnc),
132 (GInstanceInitFunc) gst_speexenc_init,
134 static const GInterfaceInfo tag_setter_info = {
141 g_type_register_static (GST_TYPE_ELEMENT, "GstSpeexEnc", &speexenc_info,
144 g_type_add_interface_static (speexenc_type, GST_TYPE_TAG_SETTER,
147 GST_DEBUG_CATEGORY_INIT (speexenc_debug, "speexenc", 0, "Speex encoder");
149 return speexenc_type;
153 speex_caps_factory (void)
155 return gst_caps_new_simple ("audio/x-speex", NULL);
159 raw_caps_factory (void)
162 gst_caps_new_simple ("audio/x-raw-int",
163 "rate", GST_TYPE_INT_RANGE, 6000, 48000,
164 "channels", GST_TYPE_INT_RANGE, 1, 2,
165 "endianness", G_TYPE_INT, G_BYTE_ORDER,
166 "signed", G_TYPE_BOOLEAN, TRUE,
167 "width", G_TYPE_INT, 16, "depth", G_TYPE_INT, 16, NULL);
171 gst_speexenc_base_init (gpointer g_class)
173 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
174 GstCaps *raw_caps, *speex_caps;
176 raw_caps = raw_caps_factory ();
177 speex_caps = speex_caps_factory ();
179 gst_speexenc_sink_template = gst_pad_template_new ("sink", GST_PAD_SINK,
180 GST_PAD_ALWAYS, raw_caps);
181 gst_speexenc_src_template = gst_pad_template_new ("src", GST_PAD_SRC,
182 GST_PAD_ALWAYS, speex_caps);
183 gst_element_class_add_pad_template (element_class,
184 gst_speexenc_sink_template);
185 gst_element_class_add_pad_template (element_class, gst_speexenc_src_template);
186 gst_element_class_set_details (element_class, &speexenc_details);
190 gst_speexenc_class_init (GstSpeexEncClass * klass)
192 GObjectClass *gobject_class;
193 GstElementClass *gstelement_class;
195 gobject_class = (GObjectClass *) klass;
196 gstelement_class = (GstElementClass *) klass;
198 gobject_class->set_property = gst_speexenc_set_property;
199 gobject_class->get_property = gst_speexenc_get_property;
201 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_QUALITY,
202 g_param_spec_float ("quality", "Quality", "Encoding quality",
203 0.0, 10.0, DEFAULT_QUALITY, G_PARAM_READWRITE));
204 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_BITRATE,
205 g_param_spec_int ("bitrate", "Encoding Bit-rate",
206 "Specify an encoding bit-rate (in bps). (0 = automatic)",
207 0, G_MAXINT, DEFAULT_BITRATE, G_PARAM_READWRITE));
208 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_VBR,
209 g_param_spec_boolean ("vbr", "VBR",
210 "Enable variable bit-rate", DEFAULT_VBR, G_PARAM_READWRITE));
211 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_ABR,
212 g_param_spec_int ("abr", "ABR",
213 "Enable average bit-rate (0 = disabled)",
214 0, G_MAXINT, DEFAULT_ABR, G_PARAM_READWRITE));
215 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_VAD,
216 g_param_spec_boolean ("vad", "VAD",
217 "Enable voice activity detection", DEFAULT_VAD, G_PARAM_READWRITE));
218 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DTX,
219 g_param_spec_boolean ("dtx", "DTX",
220 "Enable discontinuous transmission", DEFAULT_DTX, G_PARAM_READWRITE));
221 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_COMPLEXITY,
222 g_param_spec_int ("complexity", "Complexity",
223 "Set encoding complexity",
224 0, G_MAXINT, DEFAULT_COMPLEXITY, G_PARAM_READWRITE));
225 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_NFRAMES,
226 g_param_spec_int ("nframes", "NFrames",
227 "Number of frames per buffer",
228 0, G_MAXINT, DEFAULT_NFRAMES, G_PARAM_READWRITE));
229 g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LAST_MESSAGE,
230 g_param_spec_string ("last-message", "last-message",
231 "The last status message", NULL, G_PARAM_READABLE));
233 parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
235 gstelement_class->change_state = gst_speexenc_change_state;
239 gst_speexenc_sink_setcaps (GstPad * pad, GstCaps * caps)
241 GstSpeexEnc *speexenc;
242 GstStructure *structure;
244 speexenc = GST_SPEEXENC (GST_PAD_PARENT (pad));
245 speexenc->setup = FALSE;
247 structure = gst_caps_get_structure (caps, 0);
248 gst_structure_get_int (structure, "channels", &speexenc->channels);
249 gst_structure_get_int (structure, "rate", &speexenc->rate);
251 gst_speexenc_setup (speexenc);
260 gst_speexenc_convert_src (GstPad * pad, GstFormat src_format, gint64 src_value,
261 GstFormat * dest_format, gint64 * dest_value)
264 GstSpeexEnc *speexenc;
267 speexenc = GST_SPEEXENC (GST_PAD_PARENT (pad));
269 if (speexenc->samples_in == 0 ||
270 speexenc->bytes_out == 0 || speexenc->rate == 0)
273 avg = (speexenc->bytes_out * speexenc->rate) / (speexenc->samples_in);
275 switch (src_format) {
276 case GST_FORMAT_BYTES:
277 switch (*dest_format) {
278 case GST_FORMAT_TIME:
279 *dest_value = src_value * GST_SECOND / avg;
285 case GST_FORMAT_TIME:
286 switch (*dest_format) {
287 case GST_FORMAT_BYTES:
288 *dest_value = src_value * avg / GST_SECOND;
301 gst_speexenc_convert_sink (GstPad * pad, GstFormat src_format,
302 gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
306 gint bytes_per_sample;
307 GstSpeexEnc *speexenc;
309 speexenc = GST_SPEEXENC (GST_PAD_PARENT (pad));
311 bytes_per_sample = speexenc->channels * 2;
313 switch (src_format) {
314 case GST_FORMAT_BYTES:
315 switch (*dest_format) {
316 case GST_FORMAT_DEFAULT:
317 if (bytes_per_sample == 0)
319 *dest_value = src_value / bytes_per_sample;
321 case GST_FORMAT_TIME:
323 gint byterate = bytes_per_sample * speexenc->rate;
327 *dest_value = src_value * GST_SECOND / byterate;
334 case GST_FORMAT_DEFAULT:
335 switch (*dest_format) {
336 case GST_FORMAT_BYTES:
337 *dest_value = src_value * bytes_per_sample;
339 case GST_FORMAT_TIME:
340 if (speexenc->rate == 0)
342 *dest_value = src_value * GST_SECOND / speexenc->rate;
348 case GST_FORMAT_TIME:
349 switch (*dest_format) {
350 case GST_FORMAT_BYTES:
351 scale = bytes_per_sample;
353 case GST_FORMAT_DEFAULT:
354 *dest_value = src_value * scale * speexenc->rate / GST_SECOND;
366 static const GstQueryType *
367 gst_speexenc_get_query_types (GstPad * pad)
369 static const GstQueryType gst_speexenc_src_query_types[] = {
376 return gst_speexenc_src_query_types;
380 gst_speexenc_src_query (GstPad * pad, GstQuery * query)
383 GstSpeexEnc *speexenc;
386 speexenc = GST_SPEEXENC (gst_pad_get_parent (pad));
387 peerpad = gst_pad_get_peer (GST_PAD (speexenc->sinkpad));
389 switch (GST_QUERY_TYPE (query)) {
390 case GST_QUERY_POSITION:
392 GstFormat fmt, req_fmt;
395 gst_query_parse_position (query, &req_fmt, NULL);
396 if ((res = gst_pad_query_position (peerpad, &req_fmt, &val))) {
397 gst_query_set_position (query, req_fmt, val);
401 fmt = GST_FORMAT_TIME;
402 if (!(res = gst_pad_query_position (peerpad, &fmt, &pos)))
405 if ((res = gst_pad_query_convert (peerpad, fmt, pos, &req_fmt, &val)))
406 gst_query_set_position (query, req_fmt, val);
410 case GST_QUERY_DURATION:
412 GstFormat fmt, req_fmt;
415 gst_query_parse_duration (query, &req_fmt, NULL);
416 if ((res = gst_pad_query_duration (peerpad, &req_fmt, &val))) {
417 gst_query_set_duration (query, req_fmt, val);
421 fmt = GST_FORMAT_TIME;
422 if (!(res = gst_pad_query_duration (peerpad, &fmt, &dur)))
425 if ((res = gst_pad_query_convert (peerpad, fmt, dur, &req_fmt, &val))) {
426 gst_query_set_duration (query, req_fmt, val);
430 case GST_QUERY_CONVERT:
432 GstFormat src_fmt, dest_fmt;
433 gint64 src_val, dest_val;
435 gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
436 if (!(res = gst_speexenc_convert_src (pad, src_fmt, src_val, &dest_fmt,
439 gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
443 res = gst_pad_query_default (pad, query);
448 gst_object_unref (peerpad);
449 gst_object_unref (speexenc);
454 gst_speexenc_sink_query (GstPad * pad, GstQuery * query)
457 GstSpeexEnc *speexenc;
459 speexenc = GST_SPEEXENC (GST_PAD_PARENT (pad));
461 switch (GST_QUERY_TYPE (query)) {
462 case GST_QUERY_CONVERT:
464 GstFormat src_fmt, dest_fmt;
465 gint64 src_val, dest_val;
467 gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, &dest_val);
469 gst_speexenc_convert_sink (pad, src_fmt, src_val, &dest_fmt,
472 gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
476 res = gst_pad_query_default (pad, query);
485 gst_speexenc_init (GstSpeexEnc * speexenc)
488 gst_pad_new_from_template (gst_speexenc_sink_template, "sink");
489 gst_element_add_pad (GST_ELEMENT (speexenc), speexenc->sinkpad);
490 gst_pad_set_event_function (speexenc->sinkpad, gst_speexenc_sinkevent);
491 gst_pad_set_chain_function (speexenc->sinkpad, gst_speexenc_chain);
492 gst_pad_set_setcaps_function (speexenc->sinkpad, gst_speexenc_sink_setcaps);
493 gst_pad_set_query_function (speexenc->sinkpad,
494 GST_DEBUG_FUNCPTR (gst_speexenc_sink_query));
497 gst_pad_new_from_template (gst_speexenc_src_template, "src");
498 gst_pad_set_query_function (speexenc->srcpad,
499 GST_DEBUG_FUNCPTR (gst_speexenc_src_query));
500 gst_pad_set_query_type_function (speexenc->srcpad,
501 GST_DEBUG_FUNCPTR (gst_speexenc_get_query_types));
502 gst_element_add_pad (GST_ELEMENT (speexenc), speexenc->srcpad);
504 speexenc->channels = -1;
507 speexenc->quality = DEFAULT_QUALITY;
508 speexenc->bitrate = DEFAULT_BITRATE;
509 speexenc->vbr = DEFAULT_VBR;
510 speexenc->abr = DEFAULT_ABR;
511 speexenc->vad = DEFAULT_VAD;
512 speexenc->dtx = DEFAULT_DTX;
513 speexenc->complexity = DEFAULT_COMPLEXITY;
514 speexenc->nframes = DEFAULT_NFRAMES;
516 speexenc->setup = FALSE;
517 speexenc->header_sent = FALSE;
519 speexenc->adapter = gst_adapter_new ();
523 /* FIXME: why are we not using the from/to vorbiscomment
524 * functions that are in -lgsttagedit-0.9 here? */
527 gst_speexenc_get_tag_value (const GstTagList * list, const gchar * tag,
531 gchar *speexvalue = NULL;
536 tag_type = gst_tag_get_type (tag);
538 /* get tag name right */
539 if ((strcmp (tag, GST_TAG_TRACK_NUMBER) == 0)
540 || (strcmp (tag, GST_TAG_ALBUM_VOLUME_NUMBER) == 0)
541 || (strcmp (tag, GST_TAG_TRACK_COUNT) == 0)
542 || (strcmp (tag, GST_TAG_ALBUM_VOLUME_COUNT) == 0)) {
545 if (gst_tag_list_get_uint_index (list, tag, index, &track_no)) {
546 speexvalue = g_strdup_printf ("%u", track_no);
548 GST_WARNING ("Failed to extract int tag %d for '%s'", index, tag);
550 } else if (tag_type == GST_TYPE_DATE) {
551 /* FIXME: how are dates represented in speex files? */
554 if (gst_tag_list_get_date_index (list, tag, index, &date)) {
556 g_strdup_printf ("%04d-%02d-%02d", (gint) g_date_get_year (date),
557 (gint) g_date_get_month (date), (gint) g_date_get_day (date));
560 GST_WARNING ("Failed to extract date tag %d for '%s'", index, tag);
562 } else if (tag_type == G_TYPE_STRING) {
563 if (!gst_tag_list_get_string_index (list, tag, index, &speexvalue))
564 GST_WARNING ("Failed to extract string tag %d for '%s'", index, tag);
571 * Comments will be stored in the Vorbis style.
572 * It is describled in the "Structure" section of
573 * http://www.xiph.org/ogg/vorbis/doc/v-comment.html
575 * The comment header is decoded as follows:
576 * 1) [vendor_length] = read an unsigned integer of 32 bits
577 * 2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
578 * 3) [user_comment_list_length] = read an unsigned integer of 32 bits
579 * 4) iterate [user_comment_list_length] times {
580 * 5) [length] = read an unsigned integer of 32 bits
581 * 6) this iteration's user comment = read a UTF-8 vector as [length] octets
583 * 7) [framing_bit] = read a single bit as boolean
584 * 8) if ( [framing_bit] unset or end of packet ) then ERROR
587 * If you have troubles, please write to ymnk@jcraft.com.
590 comment_init (guint8 ** comments, int *length, char *vendor_string)
592 int vendor_length = strlen (vendor_string);
593 int user_comment_list_length = 0;
594 int len = 4 + vendor_length + 4;
595 guint8 *p = g_malloc (len);
597 GST_WRITE_UINT32_LE (p, vendor_length);
598 memcpy (p + 4, vendor_string, vendor_length);
599 GST_WRITE_UINT32_LE (p + 4 + vendor_length, user_comment_list_length);
604 comment_add (guint8 ** comments, int *length, const char *tag, char *val)
606 guint8 *p = *comments;
607 int vendor_length = GST_READ_UINT32_LE (p);
608 int user_comment_list_length = GST_READ_UINT32_LE (p + 4 + vendor_length);
609 int tag_len = (tag ? strlen (tag) : 0);
610 int val_len = strlen (val);
611 int len = (*length) + 4 + tag_len + val_len;
613 p = g_realloc (p, len);
615 GST_WRITE_UINT32_LE (p + *length, tag_len + val_len); /* length of comment */
617 memcpy (p + *length + 4, (guint8 *) tag, tag_len); /* comment */
618 memcpy (p + *length + 4 + tag_len, val, val_len); /* comment */
619 GST_WRITE_UINT32_LE (p + 4 + vendor_length, user_comment_list_length + 1);
626 gst_speexenc_metadata_set1 (const GstTagList * list, const gchar * tag,
629 const gchar *speextag = NULL;
630 gchar *speexvalue = NULL;
632 GstSpeexEnc *enc = GST_SPEEXENC (speexenc);
634 speextag = gst_tag_to_vorbis_tag (tag);
635 if (speextag == NULL) {
639 count = gst_tag_list_get_tag_size (list, tag);
640 for (i = 0; i < count; i++) {
641 speexvalue = gst_speexenc_get_tag_value (list, tag, i);
643 if (speexvalue != NULL) {
644 comment_add (&enc->comments, &enc->comment_len, speextag, speexvalue);
650 gst_speexenc_set_metadata (GstSpeexEnc * speexenc)
653 const GstTagList *user_tags;
655 user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (speexenc));
656 if (!(speexenc->tags || user_tags))
659 comment_init (&speexenc->comments, &speexenc->comment_len,
660 "Encoded with GStreamer Speexenc");
662 gst_tag_list_merge (user_tags, speexenc->tags,
663 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (speexenc)));
664 gst_tag_list_foreach (copy, gst_speexenc_metadata_set1, speexenc);
665 gst_tag_list_free (copy);
669 gst_speexenc_setup (GstSpeexEnc * speexenc)
671 speexenc->setup = FALSE;
673 switch (speexenc->mode) {
674 case GST_SPEEXENC_MODE_UWB:
675 speexenc->speex_mode = (SpeexMode *) & speex_uwb_mode;
677 case GST_SPEEXENC_MODE_WB:
678 speexenc->speex_mode = (SpeexMode *) & speex_wb_mode;
680 case GST_SPEEXENC_MODE_NB:
681 speexenc->speex_mode = (SpeexMode *) & speex_nb_mode;
683 case GST_SPEEXENC_MODE_AUTO:
689 if (speexenc->rate > 25000) {
690 if (speexenc->mode == GST_SPEEXENC_MODE_AUTO) {
691 speexenc->speex_mode = (SpeexMode *) & speex_uwb_mode;
693 if (speexenc->speex_mode != &speex_uwb_mode) {
694 speexenc->last_message =
696 ("Warning: suggest to use ultra wide band mode for this rate");
697 g_object_notify (G_OBJECT (speexenc), "last_message");
700 } else if (speexenc->rate > 12500) {
701 if (speexenc->mode == GST_SPEEXENC_MODE_AUTO) {
702 speexenc->speex_mode = (SpeexMode *) & speex_wb_mode;
704 if (speexenc->speex_mode != &speex_wb_mode) {
705 speexenc->last_message =
707 ("Warning: suggest to use wide band mode for this rate");
708 g_object_notify (G_OBJECT (speexenc), "last_message");
712 if (speexenc->mode == GST_SPEEXENC_MODE_AUTO) {
713 speexenc->speex_mode = (SpeexMode *) & speex_nb_mode;
715 if (speexenc->speex_mode != &speex_nb_mode) {
716 speexenc->last_message =
718 ("Warning: suggest to use narrow band mode for this rate");
719 g_object_notify (G_OBJECT (speexenc), "last_message");
724 if (speexenc->rate != 8000 && speexenc->rate != 16000
725 && speexenc->rate != 32000) {
726 speexenc->last_message =
727 g_strdup_printf ("Warning: speex is optimized for 8, 16 and 32 KHz");
728 g_object_notify (G_OBJECT (speexenc), "last_message");
731 speex_init_header (&speexenc->header, speexenc->rate, 1,
732 speexenc->speex_mode);
733 speexenc->header.frames_per_packet = speexenc->nframes;
734 speexenc->header.vbr = speexenc->vbr;
735 speexenc->header.nb_channels = speexenc->channels;
737 /*Initialize Speex encoder */
738 speexenc->state = speex_encoder_init (speexenc->speex_mode);
740 speex_encoder_ctl (speexenc->state, SPEEX_GET_FRAME_SIZE,
741 &speexenc->frame_size);
742 speex_encoder_ctl (speexenc->state, SPEEX_SET_COMPLEXITY,
743 &speexenc->complexity);
744 speex_encoder_ctl (speexenc->state, SPEEX_SET_SAMPLING_RATE, &speexenc->rate);
747 speex_encoder_ctl (speexenc->state, SPEEX_SET_VBR_QUALITY,
750 gint tmp = floor (speexenc->quality);
752 speex_encoder_ctl (speexenc->state, SPEEX_SET_QUALITY, &tmp);
754 if (speexenc->bitrate) {
755 if (speexenc->quality >= 0.0 && speexenc->vbr) {
756 speexenc->last_message =
757 g_strdup_printf ("Warning: bitrate option is overriding quality");
758 g_object_notify (G_OBJECT (speexenc), "last_message");
760 speex_encoder_ctl (speexenc->state, SPEEX_SET_BITRATE, &speexenc->bitrate);
765 speex_encoder_ctl (speexenc->state, SPEEX_SET_VBR, &tmp);
766 } else if (speexenc->vad) {
769 speex_encoder_ctl (speexenc->state, SPEEX_SET_VAD, &tmp);
775 speex_encoder_ctl (speexenc->state, SPEEX_SET_DTX, &tmp);
778 if (speexenc->dtx && !(speexenc->vbr || speexenc->abr || speexenc->vad)) {
779 speexenc->last_message =
780 g_strdup_printf ("Warning: dtx is useless without vad, vbr or abr");
781 g_object_notify (G_OBJECT (speexenc), "last_message");
782 } else if ((speexenc->vbr || speexenc->abr) && (speexenc->vad)) {
783 speexenc->last_message =
784 g_strdup_printf ("Warning: vad is already implied by vbr or abr");
785 g_object_notify (G_OBJECT (speexenc), "last_message");
789 speex_encoder_ctl (speexenc->state, SPEEX_SET_ABR, &speexenc->abr);
792 speex_encoder_ctl (speexenc->state, SPEEX_GET_LOOKAHEAD,
793 &speexenc->lookahead);
795 speexenc->setup = TRUE;
800 /* prepare a buffer for transmission */
802 gst_speexenc_buffer_from_data (GstSpeexEnc * speexenc, guchar * data,
803 gint data_len, guint64 granulepos)
807 outbuf = gst_buffer_new_and_alloc (data_len);
808 memcpy (GST_BUFFER_DATA (outbuf), data, data_len);
809 GST_BUFFER_OFFSET (outbuf) = speexenc->bytes_out;
810 GST_BUFFER_OFFSET_END (outbuf) = granulepos;
812 GST_DEBUG ("encoded buffer of %d bytes", GST_BUFFER_SIZE (outbuf));
817 /* push out the buffer and do internal bookkeeping */
819 gst_speexenc_push_buffer (GstSpeexEnc * speexenc, GstBuffer * buffer)
821 speexenc->bytes_out += GST_BUFFER_SIZE (buffer);
823 return gst_pad_push (speexenc->srcpad, buffer);
828 gst_speexenc_set_header_on_caps (GstCaps * caps, GstBuffer * buf1,
831 caps = gst_caps_make_writable (caps);
832 GstStructure *structure = gst_caps_get_structure (caps, 0);
833 GValue array = { 0 };
834 GValue value = { 0 };
837 GST_BUFFER_FLAG_SET (buf1, GST_BUFFER_FLAG_IN_CAPS);
838 GST_BUFFER_FLAG_SET (buf2, GST_BUFFER_FLAG_IN_CAPS);
840 /* put buffers in a fixed list */
841 g_value_init (&array, GST_TYPE_ARRAY);
842 g_value_init (&value, GST_TYPE_BUFFER);
843 gst_value_set_buffer (&value, buf1);
844 gst_value_array_append_value (&array, &value);
845 g_value_unset (&value);
846 g_value_init (&value, GST_TYPE_BUFFER);
847 gst_value_set_buffer (&value, buf2);
848 gst_value_array_append_value (&array, &value);
849 gst_structure_set_value (structure, "streamheader", &array);
850 g_value_unset (&value);
851 g_value_unset (&array);
858 gst_speexenc_sinkevent (GstPad * pad, GstEvent * event)
861 GstSpeexEnc *speexenc;
863 speexenc = GST_SPEEXENC (GST_PAD_PARENT (pad));
865 switch (GST_EVENT_TYPE (event)) {
867 speexenc->eos = TRUE;
868 res = gst_pad_event_default (pad, event);
874 gst_event_parse_tag (event, &list);
875 if (speexenc->tags) {
876 gst_tag_list_insert (speexenc->tags, list,
877 gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (speexenc)));
879 g_assert_not_reached ();
881 res = gst_pad_event_default (pad, event);
885 res = gst_pad_event_default (pad, event);
893 gst_speexenc_chain (GstPad * pad, GstBuffer * buf)
895 GstSpeexEnc *speexenc;
896 GstFlowReturn ret = GST_FLOW_OK;
898 speexenc = GST_SPEEXENC (gst_pad_get_parent (pad));
900 if (!speexenc->setup) {
901 gst_buffer_unref (buf);
902 GST_ELEMENT_ERROR (speexenc, CORE, NEGOTIATION, (NULL),
903 ("encoder not initialized (input is not audio?)"));
904 ret = GST_FLOW_UNEXPECTED;
908 if (!speexenc->header_sent) {
909 /* Speex streams begin with two headers; the initial header (with
910 most of the codec setup parameters) which is mandated by the Ogg
911 bitstream spec. The second header holds any comment fields.
912 We merely need to make the headers, then pass them to libspeex
913 one at a time; libspeex handles the additional Ogg bitstream
915 GstBuffer *buf1, *buf2;
920 gst_speexenc_set_metadata (speexenc);
922 /* create header buffer */
923 data = (guint8 *) speex_header_to_packet (&speexenc->header, &data_len);
924 buf1 = gst_speexenc_buffer_from_data (speexenc, data, data_len, 0);
926 /* create comment buffer */
928 gst_speexenc_buffer_from_data (speexenc, speexenc->comments,
929 speexenc->comment_len, 0);
931 /* mark and put on caps */
932 caps = gst_pad_get_caps (speexenc->srcpad);
933 caps = gst_speexenc_set_header_on_caps (caps, buf1, buf2);
935 /* negotiate with these caps */
936 GST_DEBUG ("here are the caps: %" GST_PTR_FORMAT, caps);
937 gst_pad_set_caps (speexenc->srcpad, caps);
939 gst_buffer_set_caps (buf1, caps);
940 gst_buffer_set_caps (buf2, caps);
942 /* push out buffers */
943 ret = gst_speexenc_push_buffer (speexenc, buf1);
945 if ((GST_FLOW_OK != ret) && (GST_FLOW_NOT_LINKED != ret)) {
946 gst_buffer_unref (buf1);
950 ret = gst_speexenc_push_buffer (speexenc, buf2);
952 if ((GST_FLOW_OK != ret) && (GST_FLOW_NOT_LINKED != ret)) {
954 gst_buffer_unref (buf2);
958 speex_bits_init (&speexenc->bits);
959 speex_bits_reset (&speexenc->bits);
961 speexenc->header_sent = TRUE;
965 gint frame_size = speexenc->frame_size;
966 gint bytes = frame_size * 2 * speexenc->channels;
968 /* push buffer to adapter */
969 gst_adapter_push (speexenc->adapter, buf);
971 while (gst_adapter_available (speexenc->adapter) >= bytes) {
974 gint outsize, written;
977 data = (gint16 *) gst_adapter_peek (speexenc->adapter, bytes);
979 for (i = 0; i < frame_size * speexenc->channels; i++) {
980 speexenc->input[i] = (gfloat) data[i];
982 gst_adapter_flush (speexenc->adapter, bytes);
984 speexenc->samples_in += frame_size;
986 if (speexenc->channels == 2) {
987 speex_encode_stereo (speexenc->input, frame_size, &speexenc->bits);
989 speex_encode (speexenc->state, speexenc->input, &speexenc->bits);
993 if ((speexenc->frameno % speexenc->nframes) != 0)
996 speex_bits_insert_terminator (&speexenc->bits);
997 outsize = speex_bits_nbytes (&speexenc->bits);
999 ret = gst_pad_alloc_buffer_and_set_caps (speexenc->srcpad,
1000 GST_BUFFER_OFFSET_NONE, outsize, GST_PAD_CAPS (speexenc->srcpad),
1003 if ((GST_FLOW_OK != ret)) {
1007 written = speex_bits_write (&speexenc->bits,
1008 (gchar *) GST_BUFFER_DATA (outbuf), outsize);
1009 g_assert (written == outsize);
1010 speex_bits_reset (&speexenc->bits);
1012 GST_BUFFER_TIMESTAMP (outbuf) =
1013 (speexenc->frameno * frame_size -
1014 speexenc->lookahead) * GST_SECOND / speexenc->rate;
1015 GST_BUFFER_DURATION (outbuf) = frame_size * GST_SECOND / speexenc->rate;
1016 /* set gp time and granulepos; see gst-plugins-base/ext/ogg/README */
1017 GST_BUFFER_OFFSET_END (outbuf) =
1018 ((speexenc->frameno + 1) * frame_size - speexenc->lookahead);
1019 GST_BUFFER_OFFSET (outbuf) =
1020 gst_util_uint64_scale (GST_BUFFER_OFFSET_END (outbuf), GST_SECOND,
1023 ret = gst_speexenc_push_buffer (speexenc, outbuf);
1025 if ((GST_FLOW_OK != ret) && (GST_FLOW_NOT_LINKED != ret)) {
1026 gst_buffer_unref (outbuf);
1034 gst_object_unref (speexenc);
1040 gst_speexenc_get_property (GObject * object, guint prop_id, GValue * value,
1043 GstSpeexEnc *speexenc;
1045 g_return_if_fail (GST_IS_SPEEXENC (object));
1047 speexenc = GST_SPEEXENC (object);
1051 g_value_set_float (value, speexenc->quality);
1054 g_value_set_int (value, speexenc->bitrate);
1057 g_value_set_boolean (value, speexenc->vbr);
1060 g_value_set_int (value, speexenc->abr);
1063 g_value_set_boolean (value, speexenc->vad);
1066 g_value_set_boolean (value, speexenc->dtx);
1068 case ARG_COMPLEXITY:
1069 g_value_set_int (value, speexenc->complexity);
1072 g_value_set_int (value, speexenc->nframes);
1074 case ARG_LAST_MESSAGE:
1075 g_value_set_string (value, speexenc->last_message);
1078 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1084 gst_speexenc_set_property (GObject * object, guint prop_id,
1085 const GValue * value, GParamSpec * pspec)
1087 GstSpeexEnc *speexenc;
1089 g_return_if_fail (GST_IS_SPEEXENC (object));
1091 speexenc = GST_SPEEXENC (object);
1095 speexenc->quality = g_value_get_float (value);
1098 speexenc->bitrate = g_value_get_int (value);
1101 speexenc->vbr = g_value_get_boolean (value);
1104 speexenc->abr = g_value_get_int (value);
1107 speexenc->vad = g_value_get_boolean (value);
1110 speexenc->dtx = g_value_get_boolean (value);
1112 case ARG_COMPLEXITY:
1113 speexenc->complexity = g_value_get_int (value);
1116 speexenc->nframes = g_value_get_int (value);
1119 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1124 static GstStateChangeReturn
1125 gst_speexenc_change_state (GstElement * element, GstStateChange transition)
1127 GstSpeexEnc *speexenc = GST_SPEEXENC (element);
1128 GstStateChangeReturn res;
1130 switch (transition) {
1131 case GST_STATE_CHANGE_NULL_TO_READY:
1132 speexenc->tags = gst_tag_list_new ();
1134 case GST_STATE_CHANGE_READY_TO_PAUSED:
1135 speexenc->frameno = 0;
1136 speexenc->samples_in = 0;
1138 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1144 res = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1146 switch (transition) {
1147 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1149 case GST_STATE_CHANGE_PAUSED_TO_READY:
1150 speexenc->setup = FALSE;
1151 speexenc->header_sent = FALSE;
1153 case GST_STATE_CHANGE_READY_TO_NULL:
1154 gst_tag_list_free (speexenc->tags);
1155 speexenc->tags = NULL;