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