1 /*
2 * GStreamer
3 *
4 * Copyright (C) 2005 Wim Taymans <wim@fluendo.com>
5 * Copyright (C) 2006 Tim-Philipp Müller <tim centricular net>
6 * Copyright (C) 2009-2010 Chris Robinson <chris.kcat@gmail.com>
7 * Copyright (C) 2013 Juan Manuel Borges Caño <juanmabcmail@gmail.com>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25 /**
26 * SECTION:element-openalsink
27 * @title: openalsink
28 * @see_also: openalsrc
29 * @short_description: capture raw audio samples through OpenAL
30 *
31 * This element plays raw audio samples through OpenAL.
32 *
33 * Unfortunately the capture API doesn't have a format enumeration/check. all you can do is try opening it and see if it works.
34 *
35 * ## Example pipelines
36 * |[
37 * gst-launch-1.0 audiotestsrc ! audioconvert ! volume volume=0.5 ! openalsink
38 * ]| will play a sine wave (continuous beep sound) through OpenAL.
39 * |[
40 * gst-launch-1.0 filesrc location=stream.wav ! decodebin ! audioconvert ! openalsink
41 * ]| will play a wav audio file through OpenAL.
42 * |[
43 * gst-launch-1.0 openalsrc ! "audio/x-raw,format=S16LE,rate=44100" ! audioconvert ! volume volume=0.25 ! openalsink
44 * ]| will capture and play audio through OpenAL.
45 *
46 */
47
48 /*
49 * DEV:
50 * To get better timing/delay information you may also be interested in this:
51 * http://kcat.strangesoft.net/openal-extensions/SOFT_source_latency.txt
52 */
53
54 #ifdef HAVE_CONFIG_H
55 #include "config.h"
56 #endif
57
58 #include <gst/gst.h>
59 #include <gst/gsterror.h>
60
61 GST_DEBUG_CATEGORY_EXTERN (openal_debug);
62 #define GST_CAT_DEFAULT openal_debug
63
64 #include "gstopenalsink.h"
65
66 static void gst_openal_sink_dispose (GObject * object);
67 static void gst_openal_sink_finalize (GObject * object);
68
69 static void gst_openal_sink_get_property (GObject * object, guint prop_id,
70 GValue * value, GParamSpec * pspec);
71 static void gst_openal_sink_set_property (GObject * object, guint prop_id,
72 const GValue * value, GParamSpec * pspec);
73 static GstCaps *gst_openal_sink_getcaps (GstBaseSink * basesink,
74 GstCaps * filter);
75 static gboolean gst_openal_sink_open (GstAudioSink * audiosink);
76 static gboolean gst_openal_sink_close (GstAudioSink * audiosink);
77 static gboolean gst_openal_sink_prepare (GstAudioSink * audiosink,
78 GstAudioRingBufferSpec * spec);
79 static gboolean gst_openal_sink_unprepare (GstAudioSink * audiosink);
80 static gint gst_openal_sink_write (GstAudioSink * audiosink, gpointer data,
81 guint length);
82 static guint gst_openal_sink_delay (GstAudioSink * audiosink);
83 static void gst_openal_sink_reset (GstAudioSink * audiosink);
84
85 #define OPENAL_DEFAULT_DEVICE NULL
86
87 #define OPENAL_MIN_RATE 8000
88 #define OPENAL_MAX_RATE 192000
89
90 enum
91 {
92 PROP_0,
93
94 PROP_DEVICE,
95 PROP_DEVICE_NAME,
96
97 PROP_USER_DEVICE,
98 PROP_USER_CONTEXT,
99 PROP_USER_SOURCE
100 };
101
102 static GstStaticPadTemplate openalsink_factory =
103 GST_STATIC_PAD_TEMPLATE ("sink",
104 GST_PAD_SINK,
105 GST_PAD_ALWAYS,
106 GST_STATIC_CAPS ("audio/x-raw, " "format = (string) " GST_AUDIO_NE (F64)
107 ", " "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
108 "audio/x-raw, " "format = (string) " GST_AUDIO_NE (F32) ", "
109 "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; "
110 "audio/x-raw, " "format = (string) " GST_AUDIO_NE (S16) ", "
111 "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; "
112 "audio/x-raw, " "format = (string) " G_STRINGIFY (U8) ", "
113 "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]; "
114 /* These caps do not work on my card */
115 // "audio/x-adpcm, " "layout = (string) ima, "
116 // "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]; "
117 // "audio/x-alaw, " "rate = (int) [ 1, MAX ], "
118 // "channels = (int) [ 1, 2 ]; "
119 // "audio/x-mulaw, " "rate = (int) [ 1, MAX ], "
120 // "channels = (int) [ 1, MAX ]"
121 )
122 );
123
124 static PFNALCSETTHREADCONTEXTPROC palcSetThreadContext;
125 static PFNALCGETTHREADCONTEXTPROC palcGetThreadContext;
126
127 static inline ALCcontext *
pushContext(ALCcontext * context)128 pushContext (ALCcontext * context)
129 {
130 ALCcontext *old;
131 if (!palcGetThreadContext || !palcSetThreadContext)
132 return NULL;
133
134 old = palcGetThreadContext ();
135 if (old != context)
136 palcSetThreadContext (context);
137 return old;
138 }
139
140 static inline void
popContext(ALCcontext * old,ALCcontext * context)141 popContext (ALCcontext * old, ALCcontext * context)
142 {
143 if (!palcGetThreadContext || !palcSetThreadContext)
144 return;
145
146 if (old != context)
147 palcSetThreadContext (old);
148 }
149
150 static inline ALenum
checkALError(const char * fname,unsigned int fline)151 checkALError (const char *fname, unsigned int fline)
152 {
153 ALenum err = alGetError ();
154 if (err != AL_NO_ERROR)
155 g_warning ("%s:%u: context error: %s", fname, fline, alGetString (err));
156 return err;
157 }
158
159 #define checkALError() checkALError(__FILE__, __LINE__)
160
161 G_DEFINE_TYPE (GstOpenALSink, gst_openal_sink, GST_TYPE_AUDIO_SINK);
162
163 static void
gst_openal_sink_dispose(GObject * object)164 gst_openal_sink_dispose (GObject * object)
165 {
166 GstOpenALSink *sink = GST_OPENAL_SINK (object);
167
168 if (sink->probed_caps)
169 gst_caps_unref (sink->probed_caps);
170 sink->probed_caps = NULL;
171
172 G_OBJECT_CLASS (gst_openal_sink_parent_class)->dispose (object);
173 }
174
175 static void
gst_openal_sink_class_init(GstOpenALSinkClass * klass)176 gst_openal_sink_class_init (GstOpenALSinkClass * klass)
177 {
178 GObjectClass *gobject_class = (GObjectClass *) klass;
179 GstElementClass *gstelement_class = (GstElementClass *) klass;
180 GstBaseSinkClass *gstbasesink_class = (GstBaseSinkClass *) klass;
181 GstAudioSinkClass *gstaudiosink_class = (GstAudioSinkClass *) klass;
182
183 if (alcIsExtensionPresent (NULL, "ALC_EXT_thread_local_context")) {
184 palcSetThreadContext = alcGetProcAddress (NULL, "alcSetThreadContext");
185 palcGetThreadContext = alcGetProcAddress (NULL, "alcGetThreadContext");
186 }
187
188 gobject_class->dispose = GST_DEBUG_FUNCPTR (gst_openal_sink_dispose);
189 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_openal_sink_finalize);
190 gobject_class->set_property =
191 GST_DEBUG_FUNCPTR (gst_openal_sink_set_property);
192 gobject_class->get_property =
193 GST_DEBUG_FUNCPTR (gst_openal_sink_get_property);
194
195 gst_openal_sink_parent_class = g_type_class_peek_parent (klass);
196
197 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_openal_sink_getcaps);
198
199 gstaudiosink_class->open = GST_DEBUG_FUNCPTR (gst_openal_sink_open);
200 gstaudiosink_class->close = GST_DEBUG_FUNCPTR (gst_openal_sink_close);
201 gstaudiosink_class->prepare = GST_DEBUG_FUNCPTR (gst_openal_sink_prepare);
202 gstaudiosink_class->unprepare = GST_DEBUG_FUNCPTR (gst_openal_sink_unprepare);
203 gstaudiosink_class->write = GST_DEBUG_FUNCPTR (gst_openal_sink_write);
204 gstaudiosink_class->delay = GST_DEBUG_FUNCPTR (gst_openal_sink_delay);
205 gstaudiosink_class->reset = GST_DEBUG_FUNCPTR (gst_openal_sink_reset);
206
207 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
208 g_param_spec_string ("device-name", "Device name",
209 "Human-readable name of the opened device", "", G_PARAM_READABLE));
210
211 g_object_class_install_property (gobject_class, PROP_DEVICE,
212 g_param_spec_string ("device", "Device",
213 "Human-readable name of the device", OPENAL_DEFAULT_DEVICE,
214 G_PARAM_READWRITE));
215
216 g_object_class_install_property (gobject_class, PROP_USER_DEVICE,
217 g_param_spec_pointer ("user-device", "ALCdevice", "User device",
218 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
219
220 g_object_class_install_property (gobject_class, PROP_USER_CONTEXT,
221 g_param_spec_pointer ("user-context", "ALCcontext", "User context",
222 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
223
224 g_object_class_install_property (gobject_class, PROP_USER_SOURCE,
225 g_param_spec_uint ("user-source", "ALsource", "User source", 0, UINT_MAX,
226 0, G_PARAM_READWRITE));
227
228 gst_element_class_set_static_metadata (gstelement_class, "OpenAL Audio Sink",
229 "Sink/Audio", "Output audio through OpenAL",
230 "Juan Manuel Borges Caño <juanmabcmail@gmail.com>");
231
232 gst_element_class_add_static_pad_template (gstelement_class,
233 &openalsink_factory);
234
235 }
236
237 static void
gst_openal_sink_init(GstOpenALSink * sink)238 gst_openal_sink_init (GstOpenALSink * sink)
239 {
240 GST_DEBUG_OBJECT (sink, "initializing");
241
242 sink->device_name = g_strdup (OPENAL_DEFAULT_DEVICE);
243
244 sink->user_device = NULL;
245 sink->user_context = NULL;
246 sink->user_source = 0;
247
248 sink->default_device = NULL;
249 sink->default_context = NULL;
250 sink->default_source = 0;
251
252 sink->buffer_idx = 0;
253 sink->buffer_count = 0;
254 sink->buffers = NULL;
255 sink->buffer_length = 0;
256
257 sink->write_reset = AL_FALSE;
258 sink->probed_caps = NULL;
259
260 g_mutex_init (&sink->openal_lock);
261 }
262
263 static void
gst_openal_sink_finalize(GObject * object)264 gst_openal_sink_finalize (GObject * object)
265 {
266 GstOpenALSink *sink = GST_OPENAL_SINK (object);
267
268 g_free (sink->device_name);
269 sink->device_name = NULL;
270 g_mutex_clear (&sink->openal_lock);
271
272 G_OBJECT_CLASS (gst_openal_sink_parent_class)->finalize (object);
273 }
274
275 static void
gst_openal_sink_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)276 gst_openal_sink_set_property (GObject * object, guint prop_id,
277 const GValue * value, GParamSpec * pspec)
278 {
279 GstOpenALSink *sink = GST_OPENAL_SINK (object);
280
281 switch (prop_id) {
282 case PROP_DEVICE:
283 g_free (sink->device_name);
284 sink->device_name = g_value_dup_string (value);
285 if (sink->probed_caps)
286 gst_caps_unref (sink->probed_caps);
287 sink->probed_caps = NULL;
288 break;
289 case PROP_USER_DEVICE:
290 if (!sink->default_device)
291 sink->user_device = g_value_get_pointer (value);
292 break;
293 case PROP_USER_CONTEXT:
294 if (!sink->default_device)
295 sink->user_context = g_value_get_pointer (value);
296 break;
297 case PROP_USER_SOURCE:
298 if (!sink->default_device)
299 sink->user_source = g_value_get_uint (value);
300 break;
301
302 default:
303 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
304 break;
305 }
306 }
307
308 static void
gst_openal_sink_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)309 gst_openal_sink_get_property (GObject * object, guint prop_id, GValue * value,
310 GParamSpec * pspec)
311 {
312 GstOpenALSink *sink = GST_OPENAL_SINK (object);
313 const ALCchar *device_name = sink->device_name;
314 ALCdevice *device = sink->default_device;
315 ALCcontext *context = sink->default_context;
316 ALuint source = sink->default_source;
317
318 switch (prop_id) {
319 case PROP_DEVICE_NAME:
320 device_name = "";
321 if (device)
322 device_name = alcGetString (device, ALC_DEVICE_SPECIFIER);
323 /* fall-through */
324 case PROP_DEVICE:
325 g_value_set_string (value, device_name);
326 break;
327 case PROP_USER_DEVICE:
328 if (!device)
329 device = sink->user_device;
330 g_value_set_pointer (value, device);
331 break;
332 case PROP_USER_CONTEXT:
333 if (!context)
334 context = sink->user_context;
335 g_value_set_pointer (value, context);
336 break;
337 case PROP_USER_SOURCE:
338 if (!source)
339 source = sink->user_source;
340 g_value_set_uint (value, source);
341 break;
342
343 default:
344 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
345 break;
346 }
347 }
348
349 static GstCaps *
gst_openal_helper_probe_caps(ALCcontext * context)350 gst_openal_helper_probe_caps (ALCcontext * context)
351 {
352 static const struct
353 {
354 gint count;
355 GstAudioChannelPosition positions[8];
356 } chans[] = {
357 {
358 1, {
359 GST_AUDIO_CHANNEL_POSITION_MONO}
360 }, {
361 2, {
362 GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
363 GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}
364 }, {
365 4, {
366 GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
367 GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
368 GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
369 GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}
370 }, {
371 6, {
372 GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
373 GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
374 GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
375 GST_AUDIO_CHANNEL_POSITION_LFE1,
376 GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
377 GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}
378 }, {
379 7, {
380 GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
381 GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
382 GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
383 GST_AUDIO_CHANNEL_POSITION_LFE1,
384 GST_AUDIO_CHANNEL_POSITION_REAR_CENTER,
385 GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
386 GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}
387 }, {
388 8, {
389 GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT,
390 GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT,
391 GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER,
392 GST_AUDIO_CHANNEL_POSITION_LFE1,
393 GST_AUDIO_CHANNEL_POSITION_REAR_LEFT,
394 GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT,
395 GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT,
396 GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}
397 },};
398 GstStructure *structure;
399 guint64 channel_mask;
400 GstCaps *caps;
401 ALCcontext *old;
402
403 old = pushContext (context);
404
405 caps = gst_caps_new_empty ();
406
407 if (alIsExtensionPresent ("AL_EXT_MCFORMATS")) {
408 const char *fmt32[] = {
409 "AL_FORMAT_MONO_FLOAT32",
410 "AL_FORMAT_STEREO_FLOAT32",
411 "AL_FORMAT_QUAD32",
412 "AL_FORMAT_51CHN32",
413 "AL_FORMAT_61CHN32",
414 "AL_FORMAT_71CHN32",
415 NULL
416 }, *fmt16[] = {
417 "AL_FORMAT_MONO16",
418 "AL_FORMAT_STEREO16",
419 "AL_FORMAT_QUAD16",
420 "AL_FORMAT_51CHN16",
421 "AL_FORMAT_61CHN16", "AL_FORMAT_71CHN16", NULL}, *fmt8[] = {
422 "AL_FORMAT_MONO8",
423 "AL_FORMAT_STEREO8",
424 "AL_FORMAT_QUAD8",
425 "AL_FORMAT_51CHN8", "AL_FORMAT_61CHN8", "AL_FORMAT_71CHN8", NULL};
426 int i;
427
428 if (alIsExtensionPresent ("AL_EXT_FLOAT32")) {
429 for (i = 0; fmt32[i]; i++) {
430 ALenum value = alGetEnumValue (fmt32[i]);
431 if (checkALError () != AL_NO_ERROR || value == 0 || value == -1)
432 continue;
433
434 structure =
435 gst_structure_new ("audio/x-raw", "format", G_TYPE_STRING,
436 GST_AUDIO_NE (F32), "rate", GST_TYPE_INT_RANGE, OPENAL_MIN_RATE,
437 OPENAL_MAX_RATE, "channels", G_TYPE_INT, chans[i].count, NULL);
438 if (chans[i].count > 2) {
439 gst_audio_channel_positions_to_mask (chans[i].positions,
440 chans[i].count, FALSE, &channel_mask);
441 gst_structure_set (structure, "channel-mask", GST_TYPE_BITMASK,
442 channel_mask, NULL);
443 }
444 gst_caps_append_structure (caps, structure);
445 }
446 }
447
448 for (i = 0; fmt16[i]; i++) {
449 ALenum value = alGetEnumValue (fmt16[i]);
450 if (checkALError () != AL_NO_ERROR || value == 0 || value == -1)
451 continue;
452
453 structure =
454 gst_structure_new ("audio/x-raw", "format", G_TYPE_STRING,
455 GST_AUDIO_NE (S16), "rate", GST_TYPE_INT_RANGE, OPENAL_MIN_RATE,
456 OPENAL_MAX_RATE, "channels", G_TYPE_INT, chans[i].count, NULL);
457 if (chans[i].count > 2) {
458 gst_audio_channel_positions_to_mask (chans[i].positions, chans[i].count,
459 FALSE, &channel_mask);
460 gst_structure_set (structure, "channel-mask", GST_TYPE_BITMASK,
461 channel_mask, NULL);
462 }
463 gst_caps_append_structure (caps, structure);
464 }
465 for (i = 0; fmt8[i]; i++) {
466 ALenum value = alGetEnumValue (fmt8[i]);
467 if (checkALError () != AL_NO_ERROR || value == 0 || value == -1)
468 continue;
469
470 structure =
471 gst_structure_new ("audio/x-raw", "format", G_TYPE_STRING,
472 G_STRINGIFY (U8), "rate", GST_TYPE_INT_RANGE, OPENAL_MIN_RATE,
473 OPENAL_MAX_RATE, "channels", G_TYPE_INT, chans[i].count, NULL);
474 if (chans[i].count > 2) {
475 gst_audio_channel_positions_to_mask (chans[i].positions, chans[i].count,
476 FALSE, &channel_mask);
477 gst_structure_set (structure, "channel-mask", GST_TYPE_BITMASK,
478 channel_mask, NULL);
479 }
480 gst_caps_append_structure (caps, structure);
481 }
482 } else {
483 if (alIsExtensionPresent ("AL_EXT_FLOAT32")) {
484 structure =
485 gst_structure_new ("audio/x-raw", "format", G_TYPE_STRING,
486 GST_AUDIO_NE (F32), "rate", GST_TYPE_INT_RANGE, OPENAL_MIN_RATE,
487 OPENAL_MAX_RATE, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL);
488 gst_caps_append_structure (caps, structure);
489 }
490
491 structure =
492 gst_structure_new ("audio/x-raw", "format", G_TYPE_STRING,
493 GST_AUDIO_NE (S16), "rate", GST_TYPE_INT_RANGE, OPENAL_MIN_RATE,
494 OPENAL_MAX_RATE, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL);
495 gst_caps_append_structure (caps, structure);
496
497 structure =
498 gst_structure_new ("audio/x-raw", "format", G_TYPE_STRING,
499 G_STRINGIFY (U8), "rate", GST_TYPE_INT_RANGE, OPENAL_MIN_RATE,
500 OPENAL_MAX_RATE, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL);
501 gst_caps_append_structure (caps, structure);
502 }
503
504 if (alIsExtensionPresent ("AL_EXT_double")) {
505 structure =
506 gst_structure_new ("audio/x-raw", "format", G_TYPE_STRING,
507 GST_AUDIO_NE (F64), "rate", GST_TYPE_INT_RANGE, OPENAL_MIN_RATE,
508 OPENAL_MAX_RATE, "channels", GST_TYPE_INT_RANGE, 1, 2, NULL);
509 gst_caps_append_structure (caps, structure);
510 }
511
512 if (alIsExtensionPresent ("AL_EXT_IMA4")) {
513 structure =
514 gst_structure_new ("audio/x-adpcm", "layout", G_TYPE_STRING, "ima",
515 "rate", GST_TYPE_INT_RANGE, OPENAL_MIN_RATE, OPENAL_MAX_RATE,
516 "channels", GST_TYPE_INT_RANGE, 1, 2, NULL);
517 gst_caps_append_structure (caps, structure);
518 }
519
520 if (alIsExtensionPresent ("AL_EXT_ALAW")) {
521 structure =
522 gst_structure_new ("audio/x-alaw", "rate", GST_TYPE_INT_RANGE,
523 OPENAL_MIN_RATE, OPENAL_MAX_RATE, "channels", GST_TYPE_INT_RANGE, 1, 2,
524 NULL);
525 gst_caps_append_structure (caps, structure);
526 }
527
528 if (alIsExtensionPresent ("AL_EXT_MULAW_MCFORMATS")) {
529 const char *fmtmulaw[] = {
530 "AL_FORMAT_MONO_MULAW",
531 "AL_FORMAT_STEREO_MULAW",
532 "AL_FORMAT_QUAD_MULAW",
533 "AL_FORMAT_51CHN_MULAW",
534 "AL_FORMAT_61CHN_MULAW",
535 "AL_FORMAT_71CHN_MULAW",
536 NULL
537 };
538 int i;
539
540 for (i = 0; fmtmulaw[i]; i++) {
541 ALenum value = alGetEnumValue (fmtmulaw[i]);
542 if (checkALError () != AL_NO_ERROR || value == 0 || value == -1)
543 continue;
544
545 structure =
546 gst_structure_new ("audio/x-mulaw", "rate", GST_TYPE_INT_RANGE,
547 OPENAL_MIN_RATE, OPENAL_MAX_RATE, "channels", G_TYPE_INT,
548 chans[i].count, NULL);
549 if (chans[i].count > 2) {
550 gst_audio_channel_positions_to_mask (chans[i].positions, chans[i].count,
551 FALSE, &channel_mask);
552 gst_structure_set (structure, "channel-mask", GST_TYPE_BITMASK,
553 channel_mask, NULL);
554 }
555 gst_caps_append_structure (caps, structure);
556 }
557 } else if (alIsExtensionPresent ("AL_EXT_MULAW")) {
558 structure =
559 gst_structure_new ("audio/x-mulaw", "rate", GST_TYPE_INT_RANGE,
560 OPENAL_MIN_RATE, OPENAL_MAX_RATE, "channels", GST_TYPE_INT_RANGE, 1, 2,
561 NULL);
562 gst_caps_append_structure (caps, structure);
563 }
564
565 popContext (old, context);
566
567 return caps;
568 }
569
570 static GstCaps *
gst_openal_sink_getcaps(GstBaseSink * basesink,GstCaps * filter)571 gst_openal_sink_getcaps (GstBaseSink * basesink, GstCaps * filter)
572 {
573 GstOpenALSink *sink = GST_OPENAL_SINK (basesink);
574 GstCaps *caps;
575
576 if (sink->default_device == NULL) {
577 GstPad *pad = GST_BASE_SINK_PAD (basesink);
578 GstCaps *tcaps = gst_pad_get_pad_template_caps (pad);
579 caps = gst_caps_copy (tcaps);
580 gst_caps_unref (tcaps);
581 } else if (sink->probed_caps)
582 caps = gst_caps_copy (sink->probed_caps);
583 else {
584 if (sink->default_context)
585 caps = gst_openal_helper_probe_caps (sink->default_context);
586 else if (sink->user_context)
587 caps = gst_openal_helper_probe_caps (sink->user_context);
588 else {
589 ALCcontext *context = alcCreateContext (sink->default_device, NULL);
590 if (context) {
591 caps = gst_openal_helper_probe_caps (context);
592 alcDestroyContext (context);
593 } else {
594 GST_ELEMENT_WARNING (sink, RESOURCE, FAILED,
595 ("Could not create temporary context."),
596 GST_ALC_ERROR (sink->default_device));
597 caps = NULL;
598 }
599 }
600
601 if (caps && !gst_caps_is_empty (caps))
602 sink->probed_caps = gst_caps_copy (caps);
603 }
604
605 if (filter) {
606 GstCaps *intersection;
607
608 intersection =
609 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
610 return intersection;
611 } else {
612 return caps;
613 }
614 }
615
616 static gboolean
gst_openal_sink_open(GstAudioSink * audiosink)617 gst_openal_sink_open (GstAudioSink * audiosink)
618 {
619 GstOpenALSink *sink = GST_OPENAL_SINK (audiosink);
620
621 if (sink->user_device) {
622 ALCint value = -1;
623 alcGetIntegerv (sink->user_device, ALC_ATTRIBUTES_SIZE, 1, &value);
624 if (value > 0) {
625 if (!sink->user_context
626 || alcGetContextsDevice (sink->user_context) == sink->user_device)
627 sink->default_device = sink->user_device;
628 }
629 } else if (sink->user_context)
630 sink->default_device = alcGetContextsDevice (sink->user_context);
631 else
632 sink->default_device = alcOpenDevice (sink->device_name);
633 if (!sink->default_device) {
634 GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE,
635 ("Could not open device."), GST_ALC_ERROR (sink->default_device));
636 return FALSE;
637 }
638
639 return TRUE;
640 }
641
642 static gboolean
gst_openal_sink_close(GstAudioSink * audiosink)643 gst_openal_sink_close (GstAudioSink * audiosink)
644 {
645 GstOpenALSink *sink = GST_OPENAL_SINK (audiosink);
646
647 if (!sink->user_device && !sink->user_context) {
648 if (alcCloseDevice (sink->default_device) == ALC_FALSE) {
649 GST_ELEMENT_ERROR (sink, RESOURCE, CLOSE,
650 ("Could not close device."), GST_ALC_ERROR (sink->default_device));
651 return FALSE;
652 }
653 }
654 sink->default_device = NULL;
655
656 if (sink->probed_caps)
657 gst_caps_unref (sink->probed_caps);
658 sink->probed_caps = NULL;
659
660 return TRUE;
661 }
662
663 static void
gst_openal_sink_parse_spec(GstOpenALSink * sink,const GstAudioRingBufferSpec * spec)664 gst_openal_sink_parse_spec (GstOpenALSink * sink,
665 const GstAudioRingBufferSpec * spec)
666 {
667 ALuint format = AL_NONE;
668
669 GST_DEBUG_OBJECT (sink,
670 "looking up format for type %d, gst-format %d, and %d channels",
671 spec->type, GST_AUDIO_INFO_FORMAT (&spec->info),
672 GST_AUDIO_INFO_CHANNELS (&spec->info));
673
674 /* Don't need to verify supported formats, since the probed caps will only
675 * report what was detected and we shouldn't get anything different */
676 switch (spec->type) {
677 case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW:
678 switch (GST_AUDIO_INFO_FORMAT (&spec->info)) {
679 case GST_AUDIO_FORMAT_U8:
680 switch (GST_AUDIO_INFO_CHANNELS (&spec->info)) {
681 case 1:
682 format = AL_FORMAT_MONO8;
683 break;
684 case 2:
685 format = AL_FORMAT_STEREO8;
686 break;
687 case 4:
688 format = AL_FORMAT_QUAD8;
689 break;
690 case 6:
691 format = AL_FORMAT_51CHN8;
692 break;
693 case 7:
694 format = AL_FORMAT_61CHN8;
695 break;
696 case 8:
697 format = AL_FORMAT_71CHN8;
698 break;
699 default:
700 break;
701 }
702 break;
703
704 case GST_AUDIO_FORMAT_S16:
705 switch (GST_AUDIO_INFO_CHANNELS (&spec->info)) {
706 case 1:
707 format = AL_FORMAT_MONO16;
708 break;
709 case 2:
710 format = AL_FORMAT_STEREO16;
711 break;
712 case 4:
713 format = AL_FORMAT_QUAD16;
714 break;
715 case 6:
716 format = AL_FORMAT_51CHN16;
717 break;
718 case 7:
719 format = AL_FORMAT_61CHN16;
720 break;
721 case 8:
722 format = AL_FORMAT_71CHN16;
723 break;
724 default:
725 break;
726 }
727 break;
728
729 case GST_AUDIO_FORMAT_F32:
730 switch (GST_AUDIO_INFO_CHANNELS (&spec->info)) {
731 case 1:
732 format = AL_FORMAT_MONO_FLOAT32;
733 break;
734 case 2:
735 format = AL_FORMAT_STEREO_FLOAT32;
736 break;
737 case 4:
738 format = AL_FORMAT_QUAD32;
739 break;
740 case 6:
741 format = AL_FORMAT_51CHN32;
742 break;
743 case 7:
744 format = AL_FORMAT_61CHN32;
745 break;
746 case 8:
747 format = AL_FORMAT_71CHN32;
748 break;
749 default:
750 break;
751 }
752 break;
753
754 case GST_AUDIO_FORMAT_F64:
755 switch (GST_AUDIO_INFO_CHANNELS (&spec->info)) {
756 case 1:
757 format = AL_FORMAT_MONO_DOUBLE_EXT;
758 break;
759 case 2:
760 format = AL_FORMAT_STEREO_DOUBLE_EXT;
761 break;
762 default:
763 break;
764 }
765 break;
766 default:
767 break;
768 }
769 break;
770
771 case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_IMA_ADPCM:
772 switch (GST_AUDIO_INFO_CHANNELS (&spec->info)) {
773 case 1:
774 format = AL_FORMAT_MONO_IMA4;
775 break;
776 case 2:
777 format = AL_FORMAT_STEREO_IMA4;
778 break;
779 default:
780 break;
781 }
782 break;
783
784 case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_A_LAW:
785 switch (GST_AUDIO_INFO_CHANNELS (&spec->info)) {
786 case 1:
787 format = AL_FORMAT_MONO_ALAW_EXT;
788 break;
789 case 2:
790 format = AL_FORMAT_STEREO_ALAW_EXT;
791 break;
792 default:
793 break;
794 }
795 break;
796
797 case GST_AUDIO_RING_BUFFER_FORMAT_TYPE_MU_LAW:
798 switch (GST_AUDIO_INFO_CHANNELS (&spec->info)) {
799 case 1:
800 format = AL_FORMAT_MONO_MULAW;
801 break;
802 case 2:
803 format = AL_FORMAT_STEREO_MULAW;
804 break;
805 case 4:
806 format = AL_FORMAT_QUAD_MULAW;
807 break;
808 case 6:
809 format = AL_FORMAT_51CHN_MULAW;
810 break;
811 case 7:
812 format = AL_FORMAT_61CHN_MULAW;
813 break;
814 case 8:
815 format = AL_FORMAT_71CHN_MULAW;
816 break;
817 default:
818 break;
819 }
820 break;
821
822 default:
823 break;
824 }
825
826 sink->bytes_per_sample = GST_AUDIO_INFO_BPS (&spec->info);
827 sink->rate = GST_AUDIO_INFO_RATE (&spec->info);
828 sink->channels = GST_AUDIO_INFO_CHANNELS (&spec->info);
829 sink->format = format;
830 sink->buffer_count = spec->segtotal;
831 sink->buffer_length = spec->segsize;
832 }
833
834 static gboolean
gst_openal_sink_prepare(GstAudioSink * audiosink,GstAudioRingBufferSpec * spec)835 gst_openal_sink_prepare (GstAudioSink * audiosink,
836 GstAudioRingBufferSpec * spec)
837 {
838 GstOpenALSink *sink = GST_OPENAL_SINK (audiosink);
839 ALCcontext *context, *old;
840
841 if (sink->default_context && !gst_openal_sink_unprepare (audiosink))
842 return FALSE;
843
844 if (sink->user_context)
845 context = sink->user_context;
846 else {
847 ALCint attribs[3] = { 0, 0, 0 };
848
849 /* Don't try to change the playback frequency of an app's device */
850 if (!sink->user_device) {
851 attribs[0] = ALC_FREQUENCY;
852 attribs[1] = GST_AUDIO_INFO_RATE (&spec->info);
853 attribs[2] = 0;
854 }
855
856 context = alcCreateContext (sink->default_device, attribs);
857 if (!context) {
858 GST_ELEMENT_ERROR (sink, RESOURCE, FAILED,
859 ("Unable to prepare device."), GST_ALC_ERROR (sink->default_device));
860 return FALSE;
861 }
862 }
863
864 old = pushContext (context);
865
866 if (sink->user_source) {
867 if (!sink->user_context || !alIsSource (sink->user_source)) {
868 GST_ELEMENT_ERROR (sink, RESOURCE, NOT_FOUND, (NULL),
869 ("Invalid source specified for context"));
870 goto fail;
871 }
872 sink->default_source = sink->user_source;
873 } else {
874 ALuint source;
875
876 alGenSources (1, &source);
877 if (checkALError () != AL_NO_ERROR) {
878 GST_ELEMENT_ERROR (sink, RESOURCE, NO_SPACE_LEFT, (NULL),
879 ("Unable to generate source"));
880 goto fail;
881 }
882 sink->default_source = source;
883 }
884
885 gst_openal_sink_parse_spec (sink, spec);
886 if (sink->format == AL_NONE) {
887 GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, (NULL),
888 ("Unable to get type %d, format %d, and %d channels", spec->type,
889 GST_AUDIO_INFO_FORMAT (&spec->info),
890 GST_AUDIO_INFO_CHANNELS (&spec->info)));
891 goto fail;
892 }
893
894 sink->buffers = g_malloc (sink->buffer_count * sizeof (*sink->buffers));
895 if (!sink->buffers) {
896 GST_ELEMENT_ERROR (sink, RESOURCE, FAILED, ("Out of memory."),
897 ("Unable to allocate buffers"));
898 goto fail;
899 }
900
901 alGenBuffers (sink->buffer_count, sink->buffers);
902 if (checkALError () != AL_NO_ERROR) {
903 GST_ELEMENT_ERROR (sink, RESOURCE, NO_SPACE_LEFT, (NULL),
904 ("Unable to generate %d buffers", sink->buffer_count));
905 goto fail;
906 }
907 sink->buffer_idx = 0;
908
909 popContext (old, context);
910 sink->default_context = context;
911 return TRUE;
912
913 fail:
914 if (!sink->user_source && sink->default_source)
915 alDeleteSources (1, &sink->default_source);
916 sink->default_source = 0;
917
918 g_free (sink->buffers);
919 sink->buffers = NULL;
920 sink->buffer_count = 0;
921 sink->buffer_length = 0;
922
923 popContext (old, context);
924 if (!sink->user_context)
925 alcDestroyContext (context);
926 return FALSE;
927 }
928
929 static gboolean
gst_openal_sink_unprepare(GstAudioSink * audiosink)930 gst_openal_sink_unprepare (GstAudioSink * audiosink)
931 {
932 GstOpenALSink *sink = GST_OPENAL_SINK (audiosink);
933 ALCcontext *old;
934
935 if (!sink->default_context)
936 return TRUE;
937
938 old = pushContext (sink->default_context);
939
940 alSourceStop (sink->default_source);
941 alSourcei (sink->default_source, AL_BUFFER, 0);
942
943 if (!sink->user_source)
944 alDeleteSources (1, &sink->default_source);
945 sink->default_source = 0;
946
947 alDeleteBuffers (sink->buffer_count, sink->buffers);
948 g_free (sink->buffers);
949 sink->buffers = NULL;
950 sink->buffer_idx = 0;
951 sink->buffer_count = 0;
952 sink->buffer_length = 0;
953
954 checkALError ();
955 popContext (old, sink->default_context);
956 if (!sink->user_context)
957 alcDestroyContext (sink->default_context);
958 sink->default_context = NULL;
959
960 return TRUE;
961 }
962
963 static gint
gst_openal_sink_write(GstAudioSink * audiosink,gpointer data,guint length)964 gst_openal_sink_write (GstAudioSink * audiosink, gpointer data, guint length)
965 {
966 GstOpenALSink *sink = GST_OPENAL_SINK (audiosink);
967 ALint processed, queued, state;
968 ALCcontext *old;
969 gulong rest_us;
970
971 g_assert (length == sink->buffer_length);
972
973 old = pushContext (sink->default_context);
974
975 rest_us =
976 (guint64) (sink->buffer_length / sink->bytes_per_sample) *
977 G_USEC_PER_SEC / sink->rate / sink->channels;
978 do {
979 alGetSourcei (sink->default_source, AL_SOURCE_STATE, &state);
980 alGetSourcei (sink->default_source, AL_BUFFERS_QUEUED, &queued);
981 alGetSourcei (sink->default_source, AL_BUFFERS_PROCESSED, &processed);
982 if (checkALError () != AL_NO_ERROR) {
983 GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
984 ("Source state error detected"));
985 length = 0;
986 goto out_nolock;
987 }
988
989 if (processed > 0 || queued < sink->buffer_count)
990 break;
991 if (state != AL_PLAYING)
992 alSourcePlay (sink->default_source);
993 g_usleep (rest_us);
994 }
995 while (1);
996
997 GST_OPENAL_SINK_LOCK (sink);
998 if (sink->write_reset != AL_FALSE) {
999 sink->write_reset = AL_FALSE;
1000 length = 0;
1001 goto out;
1002 }
1003
1004 queued -= processed;
1005 while (processed-- > 0) {
1006 ALuint bid;
1007 alSourceUnqueueBuffers (sink->default_source, 1, &bid);
1008 }
1009 if (state == AL_STOPPED) {
1010 /* "Restore" from underruns (not actually needed, but it keeps delay
1011 * calculations correct while rebuffering) */
1012 alSourceRewind (sink->default_source);
1013 }
1014
1015 alBufferData (sink->buffers[sink->buffer_idx], sink->format,
1016 data, sink->buffer_length, sink->rate);
1017 alSourceQueueBuffers (sink->default_source, 1,
1018 &sink->buffers[sink->buffer_idx]);
1019 sink->buffer_idx = (sink->buffer_idx + 1) % sink->buffer_count;
1020 queued++;
1021
1022 if (state != AL_PLAYING && queued == sink->buffer_count)
1023 alSourcePlay (sink->default_source);
1024
1025 if (checkALError () != AL_NO_ERROR) {
1026 GST_ELEMENT_ERROR (sink, RESOURCE, WRITE, (NULL),
1027 ("Source queue error detected"));
1028 goto out;
1029 }
1030
1031 out:
1032 GST_OPENAL_SINK_UNLOCK (sink);
1033 out_nolock:
1034 popContext (old, sink->default_context);
1035 return length;
1036 }
1037
1038 static guint
gst_openal_sink_delay(GstAudioSink * audiosink)1039 gst_openal_sink_delay (GstAudioSink * audiosink)
1040 {
1041 GstOpenALSink *sink = GST_OPENAL_SINK (audiosink);
1042 ALint queued, state, offset, delay;
1043 ALCcontext *old;
1044
1045 if (!sink->default_context)
1046 return 0;
1047
1048 GST_OPENAL_SINK_LOCK (sink);
1049 old = pushContext (sink->default_context);
1050
1051 delay = 0;
1052 alGetSourcei (sink->default_source, AL_BUFFERS_QUEUED, &queued);
1053 /* Order here is important. If the offset is queried after the state and an
1054 * underrun occurs in between the two calls, it can end up with a 0 offset
1055 * in a playing state, incorrectly reporting a len*queued/bps delay. */
1056 alGetSourcei (sink->default_source, AL_BYTE_OFFSET, &offset);
1057 alGetSourcei (sink->default_source, AL_SOURCE_STATE, &state);
1058
1059 /* Note: state=stopped is an underrun, meaning all buffers are processed
1060 * and there's no delay when writing the next buffer. Pre-buffering is
1061 * state=initial, which will introduce a delay while writing. */
1062 if (checkALError () == AL_NO_ERROR && state != AL_STOPPED)
1063 delay =
1064 ((queued * sink->buffer_length) -
1065 offset) / sink->bytes_per_sample / sink->channels / GST_MSECOND;
1066
1067 popContext (old, sink->default_context);
1068 GST_OPENAL_SINK_UNLOCK (sink);
1069
1070 if (G_UNLIKELY (delay < 0)) {
1071 /* make sure we never return a negative delay */
1072 GST_WARNING_OBJECT (openal_debug, "negative delay");
1073 delay = 0;
1074 }
1075
1076 return delay;
1077 }
1078
1079 static void
gst_openal_sink_reset(GstAudioSink * audiosink)1080 gst_openal_sink_reset (GstAudioSink * audiosink)
1081 {
1082 GstOpenALSink *sink = GST_OPENAL_SINK (audiosink);
1083 ALCcontext *old;
1084
1085 GST_OPENAL_SINK_LOCK (sink);
1086 old = pushContext (sink->default_context);
1087
1088 sink->write_reset = AL_TRUE;
1089 alSourceStop (sink->default_source);
1090 alSourceRewind (sink->default_source);
1091 alSourcei (sink->default_source, AL_BUFFER, 0);
1092 checkALError ();
1093
1094 popContext (old, sink->default_context);
1095 GST_OPENAL_SINK_UNLOCK (sink);
1096 }
1097