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