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