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