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