• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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