speexenc: initialise variable before adding to it
[platform/upstream/gstreamer.git] / ext / speex / gstspeexenc.c
1 /* GStreamer Speex Encoder
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 /**
21  * SECTION:element-speexenc
22  * @see_also: speexdec, oggmux
23  *
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
27  * Foundation</ulink>.
28  *
29  * <refsect2>
30  * <title>Example pipelines</title>
31  * |[
32  * gst-launch audiotestsrc num-buffers=100 ! speexenc ! oggmux ! filesink location=beep.ogg
33  * ]| Encode an Ogg/Speex file.
34  * </refsect2>
35  */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <math.h>
44 #include <speex/speex.h>
45 #include <speex/speex_stereo.h>
46
47 #include <gst/gsttagsetter.h>
48 #include <gst/tag/tag.h>
49 #include <gst/audio/audio.h>
50 #include "gstspeexenc.h"
51
52 GST_DEBUG_CATEGORY_STATIC (speexenc_debug);
53 #define GST_CAT_DEFAULT speexenc_debug
54
55 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
56     GST_PAD_SINK,
57     GST_PAD_ALWAYS,
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")
63     );
64
65 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
66     GST_PAD_SRC,
67     GST_PAD_ALWAYS,
68     GST_STATIC_CAPS ("audio/x-speex, "
69         "rate = (int) [ 6000, 48000 ], " "channels = (int) [ 1, 2]")
70     );
71
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
76 #define DEFAULT_ABR             0
77 #define DEFAULT_VAD             FALSE
78 #define DEFAULT_DTX             FALSE
79 #define DEFAULT_COMPLEXITY      3
80 #define DEFAULT_NFRAMES         1
81
82 enum
83 {
84   PROP_0,
85   PROP_QUALITY,
86   PROP_BITRATE,
87   PROP_MODE,
88   PROP_VBR,
89   PROP_ABR,
90   PROP_VAD,
91   PROP_DTX,
92   PROP_COMPLEXITY,
93   PROP_NFRAMES,
94   PROP_LAST_MESSAGE
95 };
96
97 #define GST_TYPE_SPEEX_ENC_MODE (gst_speex_enc_mode_get_type())
98 static GType
99 gst_speex_enc_mode_get_type (void)
100 {
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"},
107     {0, NULL, NULL},
108   };
109   if (G_UNLIKELY (speex_enc_mode_type == 0)) {
110     speex_enc_mode_type = g_enum_register_static ("GstSpeexEncMode",
111         speex_enc_modes);
112   }
113   return speex_enc_mode_type;
114 }
115
116 static void gst_speex_enc_finalize (GObject * object);
117
118 static gboolean gst_speex_enc_setup (GstSpeexEnc * enc);
119
120 static void gst_speex_enc_get_property (GObject * object, guint prop_id,
121     GValue * value, GParamSpec * pspec);
122 static void gst_speex_enc_set_property (GObject * object, guint prop_id,
123     const GValue * value, GParamSpec * pspec);
124
125 static GstFlowReturn gst_speex_enc_encode (GstSpeexEnc * enc, GstBuffer * buf);
126
127 static gboolean gst_speex_enc_start (GstAudioEncoder * enc);
128 static gboolean gst_speex_enc_stop (GstAudioEncoder * enc);
129 static gboolean gst_speex_enc_set_format (GstAudioEncoder * enc,
130     GstAudioInfo * info);
131 static GstFlowReturn gst_speex_enc_handle_frame (GstAudioEncoder * enc,
132     GstBuffer * in_buf);
133 static gboolean gst_speex_enc_sink_event (GstAudioEncoder * enc,
134     GstEvent * event);
135
136 static void
137 gst_speex_enc_setup_interfaces (GType speexenc_type)
138 {
139   static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL };
140
141   g_type_add_interface_static (speexenc_type, GST_TYPE_TAG_SETTER,
142       &tag_setter_info);
143
144   GST_DEBUG_CATEGORY_INIT (speexenc_debug, "speexenc", 0, "Speex encoder");
145 }
146
147 GST_BOILERPLATE_FULL (GstSpeexEnc, gst_speex_enc, GstAudioEncoder,
148     GST_TYPE_AUDIO_ENCODER, gst_speex_enc_setup_interfaces);
149
150 static void
151 gst_speex_enc_base_init (gpointer g_class)
152 {
153   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
154
155   gst_element_class_add_pad_template (element_class,
156       gst_static_pad_template_get (&src_factory));
157   gst_element_class_add_pad_template (element_class,
158       gst_static_pad_template_get (&sink_factory));
159   gst_element_class_set_details_simple (element_class, "Speex audio encoder",
160       "Codec/Encoder/Audio",
161       "Encodes audio in Speex format", "Wim Taymans <wim@fluendo.com>");
162 }
163
164 static void
165 gst_speex_enc_class_init (GstSpeexEncClass * klass)
166 {
167   GObjectClass *gobject_class;
168   GstAudioEncoderClass *base_class;
169
170   gobject_class = (GObjectClass *) klass;
171   base_class = (GstAudioEncoderClass *) klass;
172
173   gobject_class->set_property = gst_speex_enc_set_property;
174   gobject_class->get_property = gst_speex_enc_get_property;
175
176   base_class->start = GST_DEBUG_FUNCPTR (gst_speex_enc_start);
177   base_class->stop = GST_DEBUG_FUNCPTR (gst_speex_enc_stop);
178   base_class->set_format = GST_DEBUG_FUNCPTR (gst_speex_enc_set_format);
179   base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_speex_enc_handle_frame);
180   base_class->event = GST_DEBUG_FUNCPTR (gst_speex_enc_sink_event);
181
182   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_QUALITY,
183       g_param_spec_float ("quality", "Quality", "Encoding quality",
184           0.0, 10.0, DEFAULT_QUALITY,
185           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
186   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_BITRATE,
187       g_param_spec_int ("bitrate", "Encoding Bit-rate",
188           "Specify an encoding bit-rate (in bps). (0 = automatic)",
189           0, G_MAXINT, DEFAULT_BITRATE,
190           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
191   g_object_class_install_property (gobject_class, PROP_MODE,
192       g_param_spec_enum ("mode", "Mode", "The encoding mode",
193           GST_TYPE_SPEEX_ENC_MODE, GST_SPEEX_ENC_MODE_AUTO,
194           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
195   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VBR,
196       g_param_spec_boolean ("vbr", "VBR",
197           "Enable variable bit-rate", DEFAULT_VBR,
198           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
199   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_ABR,
200       g_param_spec_int ("abr", "ABR",
201           "Enable average bit-rate (0 = disabled)",
202           0, G_MAXINT, DEFAULT_ABR,
203           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
204   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_VAD,
205       g_param_spec_boolean ("vad", "VAD",
206           "Enable voice activity detection", DEFAULT_VAD,
207           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
208   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DTX,
209       g_param_spec_boolean ("dtx", "DTX",
210           "Enable discontinuous transmission", DEFAULT_DTX,
211           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
212   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_COMPLEXITY,
213       g_param_spec_int ("complexity", "Complexity",
214           "Set encoding complexity",
215           0, G_MAXINT, DEFAULT_COMPLEXITY,
216           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
217   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_NFRAMES,
218       g_param_spec_int ("nframes", "NFrames",
219           "Number of frames per buffer",
220           0, G_MAXINT, DEFAULT_NFRAMES,
221           G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
222   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_LAST_MESSAGE,
223       g_param_spec_string ("last-message", "last-message",
224           "The last status message", NULL,
225           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
226
227   gobject_class->finalize = gst_speex_enc_finalize;
228 }
229
230 static void
231 gst_speex_enc_finalize (GObject * object)
232 {
233   GstSpeexEnc *enc;
234
235   enc = GST_SPEEX_ENC (object);
236
237   g_free (enc->last_message);
238
239   G_OBJECT_CLASS (parent_class)->finalize (object);
240 }
241
242 static void
243 gst_speex_enc_init (GstSpeexEnc * enc, GstSpeexEncClass * klass)
244 {
245   GstAudioEncoder *benc = GST_AUDIO_ENCODER (enc);
246
247   /* arrange granulepos marking (and required perfect ts) */
248   gst_audio_encoder_set_mark_granule (benc, TRUE);
249   gst_audio_encoder_set_perfect_timestamp (benc, TRUE);
250 }
251
252 static gboolean
253 gst_speex_enc_start (GstAudioEncoder * benc)
254 {
255   GstSpeexEnc *enc = GST_SPEEX_ENC (benc);
256
257   GST_DEBUG_OBJECT (enc, "start");
258   speex_bits_init (&enc->bits);
259   enc->tags = gst_tag_list_new ();
260   enc->header_sent = FALSE;
261
262   return TRUE;
263 }
264
265 static gboolean
266 gst_speex_enc_stop (GstAudioEncoder * benc)
267 {
268   GstSpeexEnc *enc = GST_SPEEX_ENC (benc);
269
270   GST_DEBUG_OBJECT (enc, "stop");
271   enc->header_sent = FALSE;
272   if (enc->state) {
273     speex_encoder_destroy (enc->state);
274     enc->state = NULL;
275   }
276   speex_bits_destroy (&enc->bits);
277   gst_tag_list_free (enc->tags);
278   enc->tags = NULL;
279
280   return TRUE;
281 }
282
283 static gint64
284 gst_speex_enc_get_latency (GstSpeexEnc * enc)
285 {
286   /* See the Speex manual section "Latency and algorithmic delay" */
287   if (enc->rate == 8000)
288     return 30 * GST_MSECOND;
289   else
290     return 34 * GST_MSECOND;
291 }
292
293 static gboolean
294 gst_speex_enc_set_format (GstAudioEncoder * benc, GstAudioInfo * info)
295 {
296   GstSpeexEnc *enc;
297
298   enc = GST_SPEEX_ENC (benc);
299
300   enc->channels = GST_AUDIO_INFO_CHANNELS (info);
301   enc->rate = GST_AUDIO_INFO_RATE (info);
302
303   /* handle reconfigure */
304   if (enc->state) {
305     speex_encoder_destroy (enc->state);
306     enc->state = NULL;
307   }
308
309   if (!gst_speex_enc_setup (enc))
310     return FALSE;
311
312   /* feedback to base class */
313   gst_audio_encoder_set_latency (benc,
314       gst_speex_enc_get_latency (enc), gst_speex_enc_get_latency (enc));
315   gst_audio_encoder_set_lookahead (benc, enc->lookahead);
316
317   if (enc->nframes == 0) {
318     /* as many frames as available input allows */
319     gst_audio_encoder_set_frame_samples_min (benc, enc->frame_size);
320     gst_audio_encoder_set_frame_samples_max (benc, enc->frame_size);
321     gst_audio_encoder_set_frame_max (benc, 0);
322   } else {
323     /* exactly as many frames as configured */
324     gst_audio_encoder_set_frame_samples_min (benc,
325         enc->frame_size * enc->nframes);
326     gst_audio_encoder_set_frame_samples_max (benc,
327         enc->frame_size * enc->nframes);
328     gst_audio_encoder_set_frame_max (benc, 1);
329   }
330
331   return TRUE;
332 }
333
334 static GstBuffer *
335 gst_speex_enc_create_metadata_buffer (GstSpeexEnc * enc)
336 {
337   const GstTagList *user_tags;
338   GstTagList *merged_tags;
339   GstBuffer *comments = NULL;
340
341   user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc));
342
343   GST_DEBUG_OBJECT (enc, "upstream tags = %" GST_PTR_FORMAT, enc->tags);
344   GST_DEBUG_OBJECT (enc, "user-set tags = %" GST_PTR_FORMAT, user_tags);
345
346   /* gst_tag_list_merge() will handle NULL for either or both lists fine */
347   merged_tags = gst_tag_list_merge (user_tags, enc->tags,
348       gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (enc)));
349
350   if (merged_tags == NULL)
351     merged_tags = gst_tag_list_new ();
352
353   GST_DEBUG_OBJECT (enc, "merged   tags = %" GST_PTR_FORMAT, merged_tags);
354   comments = gst_tag_list_to_vorbiscomment_buffer (merged_tags, NULL,
355       0, "Encoded with GStreamer Speexenc");
356   gst_tag_list_free (merged_tags);
357
358   GST_BUFFER_OFFSET (comments) = 0;
359   GST_BUFFER_OFFSET_END (comments) = 0;
360
361   return comments;
362 }
363
364 static void
365 gst_speex_enc_set_last_msg (GstSpeexEnc * enc, const gchar * msg)
366 {
367   g_free (enc->last_message);
368   enc->last_message = g_strdup (msg);
369   GST_WARNING_OBJECT (enc, "%s", msg);
370   g_object_notify (G_OBJECT (enc), "last-message");
371 }
372
373 static gboolean
374 gst_speex_enc_setup (GstSpeexEnc * enc)
375 {
376   switch (enc->mode) {
377     case GST_SPEEX_ENC_MODE_UWB:
378       GST_LOG_OBJECT (enc, "configuring for requested UWB mode");
379       enc->speex_mode = speex_lib_get_mode (SPEEX_MODEID_UWB);
380       break;
381     case GST_SPEEX_ENC_MODE_WB:
382       GST_LOG_OBJECT (enc, "configuring for requested WB mode");
383       enc->speex_mode = speex_lib_get_mode (SPEEX_MODEID_WB);
384       break;
385     case GST_SPEEX_ENC_MODE_NB:
386       GST_LOG_OBJECT (enc, "configuring for requested NB mode");
387       enc->speex_mode = speex_lib_get_mode (SPEEX_MODEID_NB);
388       break;
389     case GST_SPEEX_ENC_MODE_AUTO:
390       /* fall through */
391       GST_LOG_OBJECT (enc, "finding best mode");
392     default:
393       break;
394   }
395
396   if (enc->rate > 25000) {
397     if (enc->mode == GST_SPEEX_ENC_MODE_AUTO) {
398       GST_LOG_OBJECT (enc, "selected UWB mode for samplerate %d", enc->rate);
399       enc->speex_mode = speex_lib_get_mode (SPEEX_MODEID_UWB);
400     } else {
401       if (enc->speex_mode != speex_lib_get_mode (SPEEX_MODEID_UWB)) {
402         gst_speex_enc_set_last_msg (enc,
403             "Warning: suggest to use ultra wide band mode for this rate");
404       }
405     }
406   } else if (enc->rate > 12500) {
407     if (enc->mode == GST_SPEEX_ENC_MODE_AUTO) {
408       GST_LOG_OBJECT (enc, "selected WB mode for samplerate %d", enc->rate);
409       enc->speex_mode = speex_lib_get_mode (SPEEX_MODEID_WB);
410     } else {
411       if (enc->speex_mode != speex_lib_get_mode (SPEEX_MODEID_WB)) {
412         gst_speex_enc_set_last_msg (enc,
413             "Warning: suggest to use wide band mode for this rate");
414       }
415     }
416   } else {
417     if (enc->mode == GST_SPEEX_ENC_MODE_AUTO) {
418       GST_LOG_OBJECT (enc, "selected NB mode for samplerate %d", enc->rate);
419       enc->speex_mode = speex_lib_get_mode (SPEEX_MODEID_NB);
420     } else {
421       if (enc->speex_mode != speex_lib_get_mode (SPEEX_MODEID_NB)) {
422         gst_speex_enc_set_last_msg (enc,
423             "Warning: suggest to use narrow band mode for this rate");
424       }
425     }
426   }
427
428   if (enc->rate != 8000 && enc->rate != 16000 && enc->rate != 32000) {
429     gst_speex_enc_set_last_msg (enc,
430         "Warning: speex is optimized for 8, 16 and 32 KHz");
431   }
432
433   speex_init_header (&enc->header, enc->rate, 1, enc->speex_mode);
434   enc->header.frames_per_packet = enc->nframes;
435   enc->header.vbr = enc->vbr;
436   enc->header.nb_channels = enc->channels;
437
438   /*Initialize Speex encoder */
439   enc->state = speex_encoder_init (enc->speex_mode);
440
441   speex_encoder_ctl (enc->state, SPEEX_GET_FRAME_SIZE, &enc->frame_size);
442   speex_encoder_ctl (enc->state, SPEEX_SET_COMPLEXITY, &enc->complexity);
443   speex_encoder_ctl (enc->state, SPEEX_SET_SAMPLING_RATE, &enc->rate);
444
445   if (enc->vbr)
446     speex_encoder_ctl (enc->state, SPEEX_SET_VBR_QUALITY, &enc->quality);
447   else {
448     gint tmp = floor (enc->quality);
449
450     speex_encoder_ctl (enc->state, SPEEX_SET_QUALITY, &tmp);
451   }
452   if (enc->bitrate) {
453     if (enc->quality >= 0.0 && enc->vbr) {
454       gst_speex_enc_set_last_msg (enc,
455           "Warning: bitrate option is overriding quality");
456     }
457     speex_encoder_ctl (enc->state, SPEEX_SET_BITRATE, &enc->bitrate);
458   }
459   if (enc->vbr) {
460     gint tmp = 1;
461
462     speex_encoder_ctl (enc->state, SPEEX_SET_VBR, &tmp);
463   } else if (enc->vad) {
464     gint tmp = 1;
465
466     speex_encoder_ctl (enc->state, SPEEX_SET_VAD, &tmp);
467   }
468
469   if (enc->dtx) {
470     gint tmp = 1;
471
472     speex_encoder_ctl (enc->state, SPEEX_SET_DTX, &tmp);
473   }
474
475   if (enc->dtx && !(enc->vbr || enc->abr || enc->vad)) {
476     gst_speex_enc_set_last_msg (enc,
477         "Warning: dtx is useless without vad, vbr or abr");
478   } else if ((enc->vbr || enc->abr) && (enc->vad)) {
479     gst_speex_enc_set_last_msg (enc,
480         "Warning: vad is already implied by vbr or abr");
481   }
482
483   if (enc->abr) {
484     speex_encoder_ctl (enc->state, SPEEX_SET_ABR, &enc->abr);
485   }
486
487   speex_encoder_ctl (enc->state, SPEEX_GET_LOOKAHEAD, &enc->lookahead);
488
489   GST_LOG_OBJECT (enc, "we have frame size %d, lookahead %d", enc->frame_size,
490       enc->lookahead);
491
492   return TRUE;
493 }
494
495 /* push out the buffer */
496 static GstFlowReturn
497 gst_speex_enc_push_buffer (GstSpeexEnc * enc, GstBuffer * buffer)
498 {
499   guint size;
500
501   size = GST_BUFFER_SIZE (buffer);
502   GST_DEBUG_OBJECT (enc, "pushing output buffer of size %u", size);
503
504   return gst_pad_push (GST_AUDIO_ENCODER_SRC_PAD (enc), buffer);
505 }
506
507 static gboolean
508 gst_speex_enc_sink_event (GstAudioEncoder * benc, GstEvent * event)
509 {
510   GstSpeexEnc *enc;
511
512   enc = GST_SPEEX_ENC (benc);
513
514   switch (GST_EVENT_TYPE (event)) {
515     case GST_EVENT_TAG:
516     {
517       if (enc->tags) {
518         GstTagList *list;
519
520         gst_event_parse_tag (event, &list);
521         gst_tag_list_insert (enc->tags, list,
522             gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (enc)));
523       } else {
524         g_assert_not_reached ();
525       }
526       break;
527     }
528     default:
529       break;
530   }
531
532   /* we only peeked, let base class handle it */
533   return FALSE;
534 }
535
536 static GstFlowReturn
537 gst_speex_enc_encode (GstSpeexEnc * enc, GstBuffer * buf)
538 {
539   gint frame_size = enc->frame_size;
540   gint bytes = frame_size * 2 * enc->channels, samples, size;
541   gint outsize, written, dtx_ret = 0;
542   guint8 *data;
543   GstBuffer *outbuf;
544   GstFlowReturn ret = GST_FLOW_OK;
545
546   if (G_LIKELY (buf)) {
547     data = GST_BUFFER_DATA (buf);
548     size = GST_BUFFER_SIZE (buf);
549
550     if (G_UNLIKELY (size % bytes)) {
551       GST_DEBUG_OBJECT (enc, "draining; adding silence samples");
552       size = ((size / bytes) + 1) * bytes;
553       data = g_malloc0 (size);
554       memcpy (data, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
555     }
556   } else {
557     GST_DEBUG_OBJECT (enc, "nothing to drain");
558     goto done;
559   }
560
561   samples = size / (2 * enc->channels);
562   speex_bits_reset (&enc->bits);
563
564   /* FIXME what about dropped samples if DTS enabled ?? */
565
566   while (size) {
567     GST_DEBUG_OBJECT (enc, "encoding %d samples (%d bytes)", frame_size, bytes);
568
569     if (enc->channels == 2) {
570       speex_encode_stereo_int ((gint16 *) data, frame_size, &enc->bits);
571     }
572     dtx_ret += speex_encode_int (enc->state, (gint16 *) data, &enc->bits);
573
574     data += bytes;
575     size -= bytes;
576   }
577
578   speex_bits_insert_terminator (&enc->bits);
579   outsize = speex_bits_nbytes (&enc->bits);
580
581   ret = gst_pad_alloc_buffer_and_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc),
582       GST_BUFFER_OFFSET_NONE, outsize,
583       GST_PAD_CAPS (GST_AUDIO_ENCODER_SRC_PAD (enc)), &outbuf);
584
585   if ((GST_FLOW_OK != ret))
586     goto done;
587
588   written = speex_bits_write (&enc->bits,
589       (gchar *) GST_BUFFER_DATA (outbuf), outsize);
590
591   if (G_UNLIKELY (written < outsize)) {
592     GST_ERROR_OBJECT (enc, "short write: %d < %d bytes", written, outsize);
593     GST_BUFFER_SIZE (outbuf) = written;
594   } else if (G_UNLIKELY (written > outsize)) {
595     GST_ERROR_OBJECT (enc, "overrun: %d > %d bytes", written, outsize);
596   }
597
598   if (!dtx_ret)
599     GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_GAP);
600
601   ret = gst_audio_encoder_finish_frame (GST_AUDIO_ENCODER (enc),
602       outbuf, samples);
603
604 done:
605   return ret;
606 }
607
608 /*
609  * (really really) FIXME: move into core (dixit tpm)
610  */
611 /**
612  * _gst_caps_set_buffer_array:
613  * @caps: a #GstCaps
614  * @field: field in caps to set
615  * @buf: header buffers
616  *
617  * Adds given buffers to an array of buffers set as the given @field
618  * on the given @caps.  List of buffer arguments must be NULL-terminated.
619  *
620  * Returns: input caps with a streamheader field added, or NULL if some error
621  */
622 static GstCaps *
623 _gst_caps_set_buffer_array (GstCaps * caps, const gchar * field,
624     GstBuffer * buf, ...)
625 {
626   GstStructure *structure = NULL;
627   va_list va;
628   GValue array = { 0 };
629   GValue value = { 0 };
630
631   g_return_val_if_fail (caps != NULL, NULL);
632   g_return_val_if_fail (gst_caps_is_fixed (caps), NULL);
633   g_return_val_if_fail (field != NULL, NULL);
634
635   caps = gst_caps_make_writable (caps);
636   structure = gst_caps_get_structure (caps, 0);
637
638   g_value_init (&array, GST_TYPE_ARRAY);
639
640   va_start (va, buf);
641   /* put buffers in a fixed list */
642   while (buf) {
643     g_assert (gst_buffer_is_metadata_writable (buf));
644
645     /* mark buffer */
646     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
647
648     g_value_init (&value, GST_TYPE_BUFFER);
649     buf = gst_buffer_copy (buf);
650     GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
651     gst_value_set_buffer (&value, buf);
652     gst_buffer_unref (buf);
653     gst_value_array_append_value (&array, &value);
654     g_value_unset (&value);
655
656     buf = va_arg (va, GstBuffer *);
657   }
658
659   gst_structure_set_value (structure, field, &array);
660   g_value_unset (&array);
661
662   return caps;
663 }
664
665 static GstFlowReturn
666 gst_speex_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf)
667 {
668   GstSpeexEnc *enc;
669   GstFlowReturn ret = GST_FLOW_OK;
670
671   enc = GST_SPEEX_ENC (benc);
672
673   if (!enc->header_sent) {
674     /* Speex streams begin with two headers; the initial header (with
675        most of the codec setup parameters) which is mandated by the Ogg
676        bitstream spec.  The second header holds any comment fields.
677        We merely need to make the headers, then pass them to libspeex 
678        one at a time; libspeex handles the additional Ogg bitstream 
679        constraints */
680     GstBuffer *buf1, *buf2;
681     GstCaps *caps;
682     guchar *data;
683     gint data_len;
684
685     /* create header buffer */
686     data = (guint8 *) speex_header_to_packet (&enc->header, &data_len);
687     buf1 = gst_buffer_new ();
688     GST_BUFFER_DATA (buf1) = GST_BUFFER_MALLOCDATA (buf1) = data;
689     GST_BUFFER_SIZE (buf1) = data_len;
690     GST_BUFFER_OFFSET_END (buf1) = 0;
691     GST_BUFFER_OFFSET (buf1) = 0;
692
693     /* create comment buffer */
694     buf2 = gst_speex_enc_create_metadata_buffer (enc);
695
696     /* mark and put on caps */
697     caps = gst_caps_new_simple ("audio/x-speex", "rate", G_TYPE_INT, enc->rate,
698         "channels", G_TYPE_INT, enc->channels, NULL);
699     caps = _gst_caps_set_buffer_array (caps, "streamheader", buf1, buf2, NULL);
700
701     /* negotiate with these caps */
702     GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps);
703
704     gst_buffer_set_caps (buf1, caps);
705     gst_buffer_set_caps (buf2, caps);
706     gst_caps_unref (caps);
707
708     /* push out buffers */
709     ret = gst_speex_enc_push_buffer (enc, buf1);
710
711     if (ret != GST_FLOW_OK) {
712       gst_buffer_unref (buf2);
713       goto done;
714     }
715
716     ret = gst_speex_enc_push_buffer (enc, buf2);
717
718     if (ret != GST_FLOW_OK)
719       goto done;
720
721     enc->header_sent = TRUE;
722   }
723
724   GST_DEBUG_OBJECT (enc, "received buffer %p of %u bytes", buf,
725       buf ? GST_BUFFER_SIZE (buf) : 0);
726
727   ret = gst_speex_enc_encode (enc, buf);
728
729 done:
730   return ret;
731 }
732
733
734 static void
735 gst_speex_enc_get_property (GObject * object, guint prop_id, GValue * value,
736     GParamSpec * pspec)
737 {
738   GstSpeexEnc *enc;
739
740   enc = GST_SPEEX_ENC (object);
741
742   switch (prop_id) {
743     case PROP_QUALITY:
744       g_value_set_float (value, enc->quality);
745       break;
746     case PROP_BITRATE:
747       g_value_set_int (value, enc->bitrate);
748       break;
749     case PROP_MODE:
750       g_value_set_enum (value, enc->mode);
751       break;
752     case PROP_VBR:
753       g_value_set_boolean (value, enc->vbr);
754       break;
755     case PROP_ABR:
756       g_value_set_int (value, enc->abr);
757       break;
758     case PROP_VAD:
759       g_value_set_boolean (value, enc->vad);
760       break;
761     case PROP_DTX:
762       g_value_set_boolean (value, enc->dtx);
763       break;
764     case PROP_COMPLEXITY:
765       g_value_set_int (value, enc->complexity);
766       break;
767     case PROP_NFRAMES:
768       g_value_set_int (value, enc->nframes);
769       break;
770     case PROP_LAST_MESSAGE:
771       g_value_set_string (value, enc->last_message);
772       break;
773     default:
774       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
775       break;
776   }
777 }
778
779 static void
780 gst_speex_enc_set_property (GObject * object, guint prop_id,
781     const GValue * value, GParamSpec * pspec)
782 {
783   GstSpeexEnc *enc;
784
785   enc = GST_SPEEX_ENC (object);
786
787   switch (prop_id) {
788     case PROP_QUALITY:
789       enc->quality = g_value_get_float (value);
790       break;
791     case PROP_BITRATE:
792       enc->bitrate = g_value_get_int (value);
793       break;
794     case PROP_MODE:
795       enc->mode = g_value_get_enum (value);
796       break;
797     case PROP_VBR:
798       enc->vbr = g_value_get_boolean (value);
799       break;
800     case PROP_ABR:
801       enc->abr = g_value_get_int (value);
802       break;
803     case PROP_VAD:
804       enc->vad = g_value_get_boolean (value);
805       break;
806     case PROP_DTX:
807       enc->dtx = g_value_get_boolean (value);
808       break;
809     case PROP_COMPLEXITY:
810       enc->complexity = g_value_get_int (value);
811       break;
812     case PROP_NFRAMES:
813       enc->nframes = g_value_get_int (value);
814       break;
815     default:
816       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
817       break;
818   }
819 }