1 /*
2  * GStreamer
3  * Copyright (C) 2006 Zaheer Abbas Merali <zaheerabbas at merali dot org>
4  * Copyright (C) 2008 Pioneers of the Inevitable <songbird@songbirdnest.com>
5  * Copyright (C) 2012 Fluendo S.A. <support@fluendo.com>
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the "Software"),
9  * to deal in the Software without restriction, including without limitation
10  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11  * and/or sell copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  *
25  * Alternatively, the contents of this file may be used under the
26  * GNU Lesser General Public License Version 2.1 (the "LGPL"), in
27  * which case the following provisions apply instead of the ones
28  * mentioned above:
29  *
30  * This library is free software; you can redistribute it and/or
31  * modify it under the terms of the GNU Library General Public
32  * License as published by the Free Software Foundation; either
33  * version 2 of the License, or (at your option) any later version.
34  *
35  * This library is distributed in the hope that it will be useful,
36  * but WITHOUT ANY WARRANTY; without even the implied warranty of
37  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
38  * Library General Public License for more details.
39  *
40  * You should have received a copy of the GNU Library General Public
41  * License along with this library; if not, write to the
42  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
43  * Boston, MA 02110-1301, USA.
44  */
45 
46 #ifdef HAVE_CONFIG_H
47 #  include <config.h>
48 #endif
49 
50 #include <gst/gst.h>
51 #include <gst/gst-i18n-plugin.h>
52 #include <gst/audio/audio-channels.h>
53 #include "gstosxaudioringbuffer.h"
54 #include "gstosxaudiosink.h"
55 #include "gstosxaudiosrc.h"
56 
57 #include <unistd.h>             /* for getpid() */
58 
59 GST_DEBUG_CATEGORY_STATIC (osx_audio_debug);
60 #define GST_CAT_DEFAULT osx_audio_debug
61 
62 #include "gstosxcoreaudio.h"
63 
64 static void gst_osx_audio_ring_buffer_dispose (GObject * object);
65 static gboolean gst_osx_audio_ring_buffer_open_device (GstAudioRingBuffer *
66     buf);
67 static gboolean gst_osx_audio_ring_buffer_close_device (GstAudioRingBuffer *
68     buf);
69 
70 static gboolean gst_osx_audio_ring_buffer_acquire (GstAudioRingBuffer * buf,
71     GstAudioRingBufferSpec * spec);
72 static gboolean gst_osx_audio_ring_buffer_release (GstAudioRingBuffer * buf);
73 
74 static gboolean gst_osx_audio_ring_buffer_start (GstAudioRingBuffer * buf);
75 static gboolean gst_osx_audio_ring_buffer_pause (GstAudioRingBuffer * buf);
76 static gboolean gst_osx_audio_ring_buffer_stop (GstAudioRingBuffer * buf);
77 static guint gst_osx_audio_ring_buffer_delay (GstAudioRingBuffer * buf);
78 static GstAudioRingBufferClass *ring_parent_class = NULL;
79 
80 #define gst_osx_audio_ring_buffer_do_init \
81   GST_DEBUG_CATEGORY_INIT (osx_audio_debug, "osxaudio", 0, "OSX Audio Elements");
82 
83 G_DEFINE_TYPE_WITH_CODE (GstOsxAudioRingBuffer, gst_osx_audio_ring_buffer,
84     GST_TYPE_AUDIO_RING_BUFFER, gst_osx_audio_ring_buffer_do_init);
85 
86 static void
gst_osx_audio_ring_buffer_class_init(GstOsxAudioRingBufferClass * klass)87 gst_osx_audio_ring_buffer_class_init (GstOsxAudioRingBufferClass * klass)
88 {
89   GObjectClass *gobject_class;
90   GstAudioRingBufferClass *gstringbuffer_class;
91 
92   gobject_class = (GObjectClass *) klass;
93   gstringbuffer_class = (GstAudioRingBufferClass *) klass;
94 
95   ring_parent_class = g_type_class_peek_parent (klass);
96 
97   gobject_class->dispose = gst_osx_audio_ring_buffer_dispose;
98 
99   gstringbuffer_class->open_device =
100       GST_DEBUG_FUNCPTR (gst_osx_audio_ring_buffer_open_device);
101   gstringbuffer_class->close_device =
102       GST_DEBUG_FUNCPTR (gst_osx_audio_ring_buffer_close_device);
103   gstringbuffer_class->acquire =
104       GST_DEBUG_FUNCPTR (gst_osx_audio_ring_buffer_acquire);
105   gstringbuffer_class->release =
106       GST_DEBUG_FUNCPTR (gst_osx_audio_ring_buffer_release);
107   gstringbuffer_class->start =
108       GST_DEBUG_FUNCPTR (gst_osx_audio_ring_buffer_start);
109   gstringbuffer_class->pause =
110       GST_DEBUG_FUNCPTR (gst_osx_audio_ring_buffer_pause);
111   gstringbuffer_class->resume =
112       GST_DEBUG_FUNCPTR (gst_osx_audio_ring_buffer_start);
113   gstringbuffer_class->stop =
114       GST_DEBUG_FUNCPTR (gst_osx_audio_ring_buffer_stop);
115   gstringbuffer_class->delay =
116       GST_DEBUG_FUNCPTR (gst_osx_audio_ring_buffer_delay);
117 
118   GST_DEBUG ("osx audio ring buffer class init");
119 }
120 
121 static void
gst_osx_audio_ring_buffer_init(GstOsxAudioRingBuffer * ringbuffer)122 gst_osx_audio_ring_buffer_init (GstOsxAudioRingBuffer * ringbuffer)
123 {
124   ringbuffer->core_audio = gst_core_audio_new (GST_OBJECT (ringbuffer));
125 }
126 
127 static void
gst_osx_audio_ring_buffer_dispose(GObject * object)128 gst_osx_audio_ring_buffer_dispose (GObject * object)
129 {
130   GstOsxAudioRingBuffer *osxbuf;
131 
132   osxbuf = GST_OSX_AUDIO_RING_BUFFER (object);
133 
134   if (osxbuf->core_audio) {
135     g_object_unref (osxbuf->core_audio);
136     osxbuf->core_audio = NULL;
137   }
138   G_OBJECT_CLASS (ring_parent_class)->dispose (object);
139 }
140 
141 static gboolean
gst_osx_audio_ring_buffer_open_device(GstAudioRingBuffer * buf)142 gst_osx_audio_ring_buffer_open_device (GstAudioRingBuffer * buf)
143 {
144   GstObject *osxel = GST_OBJECT_PARENT (buf);
145   GstOsxAudioRingBuffer *osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
146 
147   if (!gst_core_audio_select_device (osxbuf->core_audio)) {
148     GST_ELEMENT_ERROR (osxel, RESOURCE, NOT_FOUND,
149         (_("CoreAudio device not found")), (NULL));
150     return FALSE;
151   }
152 
153   if (!gst_core_audio_open (osxbuf->core_audio)) {
154     GST_ELEMENT_ERROR (osxel, RESOURCE, OPEN_READ,
155         (_("CoreAudio device could not be opened")), (NULL));
156     return FALSE;
157   }
158 
159   return TRUE;
160 }
161 
162 static gboolean
gst_osx_audio_ring_buffer_close_device(GstAudioRingBuffer * buf)163 gst_osx_audio_ring_buffer_close_device (GstAudioRingBuffer * buf)
164 {
165   GstOsxAudioRingBuffer *osxbuf;
166   osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
167 
168   return gst_core_audio_close (osxbuf->core_audio);
169 }
170 
171 static gboolean
gst_osx_audio_ring_buffer_acquire(GstAudioRingBuffer * buf,GstAudioRingBufferSpec * spec)172 gst_osx_audio_ring_buffer_acquire (GstAudioRingBuffer * buf,
173     GstAudioRingBufferSpec * spec)
174 {
175   gboolean ret = FALSE, is_passthrough = FALSE;
176   GstOsxAudioRingBuffer *osxbuf;
177   AudioStreamBasicDescription format;
178 
179   osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
180 
181   if (RINGBUFFER_IS_SPDIF (spec->type)) {
182     format.mFormatID = kAudioFormat60958AC3;
183     format.mSampleRate = (double) GST_AUDIO_INFO_RATE (&spec->info);
184     format.mChannelsPerFrame = 2;
185     format.mFormatFlags = kAudioFormatFlagIsSignedInteger |
186         kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonMixable;
187     format.mBytesPerFrame = 0;
188     format.mBitsPerChannel = 16;
189     format.mBytesPerPacket = 6144;
190     format.mFramesPerPacket = 1536;
191     format.mReserved = 0;
192     spec->segsize = 6144;
193     spec->segtotal = 10;
194     is_passthrough = TRUE;
195   } else {
196     int width, depth;
197     /* Fill out the audio description we're going to be using */
198     format.mFormatID = kAudioFormatLinearPCM;
199     format.mSampleRate = (double) GST_AUDIO_INFO_RATE (&spec->info);
200     format.mChannelsPerFrame = GST_AUDIO_INFO_CHANNELS (&spec->info);
201     if (GST_AUDIO_INFO_IS_FLOAT (&spec->info)) {
202       format.mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
203       width = depth = GST_AUDIO_INFO_WIDTH (&spec->info);
204     } else {
205       format.mFormatFlags = kAudioFormatFlagIsSignedInteger;
206       width = GST_AUDIO_INFO_WIDTH (&spec->info);
207       depth = GST_AUDIO_INFO_DEPTH (&spec->info);
208       if (width == depth) {
209         format.mFormatFlags |= kAudioFormatFlagIsPacked;
210       } else {
211         format.mFormatFlags |= kAudioFormatFlagIsAlignedHigh;
212       }
213     }
214 
215     if (GST_AUDIO_INFO_IS_BIG_ENDIAN (&spec->info)) {
216       format.mFormatFlags |= kAudioFormatFlagIsBigEndian;
217     }
218 
219     format.mBytesPerFrame = GST_AUDIO_INFO_BPF (&spec->info);
220     format.mBitsPerChannel = depth;
221     format.mBytesPerPacket = GST_AUDIO_INFO_BPF (&spec->info);
222     format.mFramesPerPacket = 1;
223     format.mReserved = 0;
224     spec->segsize =
225         (spec->latency_time * GST_AUDIO_INFO_RATE (&spec->info) /
226         G_USEC_PER_SEC) * GST_AUDIO_INFO_BPF (&spec->info);
227     spec->segtotal = spec->buffer_time / spec->latency_time;
228     is_passthrough = FALSE;
229   }
230 
231   GST_DEBUG_OBJECT (osxbuf, "Format: " CORE_AUDIO_FORMAT,
232       CORE_AUDIO_FORMAT_ARGS (format));
233 
234   /* gst_audio_ring_buffer_set_channel_positions is not called
235    * since the AUs perform channel reordering themselves.
236    * (see gst_core_audio_set_channel_layout) */
237 
238   buf->size = spec->segtotal * spec->segsize;
239   buf->memory = g_malloc0 (buf->size);
240 
241   ret = gst_core_audio_initialize (osxbuf->core_audio, format, spec->caps,
242       is_passthrough);
243 
244   if (!ret) {
245     g_free (buf->memory);
246     buf->memory = NULL;
247     buf->size = 0;
248   }
249 
250   osxbuf->segoffset = 0;
251 
252   return ret;
253 }
254 
255 static gboolean
gst_osx_audio_ring_buffer_release(GstAudioRingBuffer * buf)256 gst_osx_audio_ring_buffer_release (GstAudioRingBuffer * buf)
257 {
258   GstOsxAudioRingBuffer *osxbuf;
259 
260   osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
261 
262   gst_core_audio_uninitialize (osxbuf->core_audio);
263 
264   g_free (buf->memory);
265   buf->memory = NULL;
266   buf->size = 0;
267 
268   return TRUE;
269 }
270 
271 static gboolean
gst_osx_audio_ring_buffer_start(GstAudioRingBuffer * buf)272 gst_osx_audio_ring_buffer_start (GstAudioRingBuffer * buf)
273 {
274   GstOsxAudioRingBuffer *osxbuf;
275 
276   osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
277 
278   return gst_core_audio_start_processing (osxbuf->core_audio);
279 }
280 
281 static gboolean
gst_osx_audio_ring_buffer_pause(GstAudioRingBuffer * buf)282 gst_osx_audio_ring_buffer_pause (GstAudioRingBuffer * buf)
283 {
284   GstOsxAudioRingBuffer *osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
285 
286   return gst_core_audio_pause_processing (osxbuf->core_audio);
287 }
288 
289 
290 static gboolean
gst_osx_audio_ring_buffer_stop(GstAudioRingBuffer * buf)291 gst_osx_audio_ring_buffer_stop (GstAudioRingBuffer * buf)
292 {
293   GstOsxAudioRingBuffer *osxbuf;
294 
295   osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
296 
297   gst_core_audio_stop_processing (osxbuf->core_audio);
298 
299   return TRUE;
300 }
301 
302 static guint
gst_osx_audio_ring_buffer_delay(GstAudioRingBuffer * buf)303 gst_osx_audio_ring_buffer_delay (GstAudioRingBuffer * buf)
304 {
305   GstOsxAudioRingBuffer *osxbuf;
306   double latency;
307   guint samples;
308 
309   osxbuf = GST_OSX_AUDIO_RING_BUFFER (buf);
310 
311   if (!gst_core_audio_get_samples_and_latency (osxbuf->core_audio,
312           GST_AUDIO_INFO_RATE (&buf->spec.info), &samples, &latency)) {
313     return 0;
314   }
315   GST_DEBUG_OBJECT (buf, "Got latency: %f seconds -> %d samples",
316       latency, samples);
317   return samples;
318 }
319