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