Audioqueue: relicense to LGPL
[vlc.git] / modules / audio_output / audioqueue.c
blob8fb454d3eb95773e202b29fa3b121969c79c2031
1 /*****************************************************************************
2 * Copyright (C) 2000-2013 VLC authors and VideoLAN
3 * $Id$
5 * Authors: Felix Paul Kühne <fkuehne at videolan dot org>
6 * Rémi Denis-Courmont
7 * Rafaël Carré <funman at videolan dot org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program 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
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 #pragma mark includes
26 #ifdef HAVE_CONFIG_H
27 # import "config.h"
28 #endif
29 #import <vlc_common.h>
30 #import <vlc_plugin.h>
31 #import <vlc_aout.h>
32 #import <AudioToolBox/AudioQueue.h>
33 #import <TargetConditionals.h>
34 #if TARGET_OS_IPHONE
35 #import <AudioToolBox/AudioSession.h>
36 #else
37 #define AudioSessionSetActive(x)
38 #endif
40 #pragma mark -
41 #pragma mark private declarations
43 struct aout_sys_t
45 AudioQueueRef audioQueueRef;
46 AudioQueueTimelineRef timelineRef;
48 mtime_t i_played_length;
49 int i_rate;
50 float f_volume;
52 static int Open (vlc_object_t *);
53 static void Close (vlc_object_t *);
54 static void Play (audio_output_t *, block_t *);
55 static void Pause (audio_output_t *p_aout, bool pause, mtime_t date);
56 static void Flush (audio_output_t *p_aout, bool wait);
57 static int TimeGet (audio_output_t *aout, mtime_t *);
58 static void UnusedAudioQueueCallback (void *, AudioQueueRef, AudioQueueBufferRef);
59 static int Start(audio_output_t *, audio_sample_format_t *);
60 static void Stop(audio_output_t *);
61 static int VolumeSet(audio_output_t *, float );
62 vlc_module_begin ()
63 set_shortname("AudioQueue")
64 set_description(N_("AudioQueue (iOS / Mac OS) audio output"))
65 set_capability("audio output", 40)
66 set_category(CAT_AUDIO)
67 set_subcategory(SUBCAT_AUDIO_AOUT)
68 add_shortcut("audioqueue")
69 set_callbacks(Open, Close)
70 vlc_module_end ()
72 #pragma mark -
73 #pragma mark initialization
75 static int Open(vlc_object_t *obj)
77 audio_output_t *aout = (audio_output_t *)obj;
78 aout_sys_t *sys = malloc(sizeof (*sys));
80 if (unlikely(sys == NULL))
81 return VLC_ENOMEM;
83 aout->sys = sys;
84 aout->start = Start;
85 aout->stop = Stop;
86 aout->volume_set = VolumeSet;
88 /* reset volume */
89 aout_VolumeReport(aout, 1.0);
91 return VLC_SUCCESS;
94 static void Close(vlc_object_t *obj)
96 audio_output_t *aout = (audio_output_t *)obj;
97 msg_Dbg( aout, "audioqueue: Close");
98 aout_sys_t *sys = aout->sys;
100 free(sys);
103 static int VolumeSet(audio_output_t * p_aout, float volume)
105 struct aout_sys_t *p_sys = p_aout->sys;
106 OSStatus ostatus;
108 aout_VolumeReport(p_aout, volume);
109 p_sys->f_volume = volume;
111 /* Set volume for output unit */
112 ostatus = AudioQueueSetParameter(p_sys->audioQueueRef, kAudioQueueParam_Volume, volume * volume * volume);
114 return ostatus;
117 static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
119 aout_sys_t *p_sys = p_aout->sys;
120 OSStatus error = 0;
122 // prepare the format description for our output
123 AudioStreamBasicDescription streamDescription;
124 streamDescription.mSampleRate = fmt->i_rate;
125 streamDescription.mFormatID = kAudioFormatLinearPCM;
126 streamDescription.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; // FL32
127 streamDescription.mFramesPerPacket = 1;
128 streamDescription.mChannelsPerFrame = 2;
129 streamDescription.mBitsPerChannel = 32;
130 streamDescription.mBytesPerFrame = streamDescription.mBitsPerChannel * streamDescription.mChannelsPerFrame / 8;
131 streamDescription.mBytesPerPacket = streamDescription.mBytesPerFrame * streamDescription.mFramesPerPacket;
133 // init new output instance
134 error = AudioQueueNewOutput(&streamDescription, // Format
135 UnusedAudioQueueCallback, // Unused Callback, which needs to be provided to have a proper instance
136 NULL, // User data, passed to the callback
137 NULL, // RunLoop
138 kCFRunLoopCommonModes, // RunLoop mode
139 0, // Flags ; must be zero (per documentation)...
140 &(p_sys->audioQueueRef)); // Output
141 msg_Dbg(p_aout, "New AudioQueue instance created (status = %li)", error);
142 if (error != noErr)
143 return VLC_EGENERIC;
144 fmt->i_format = VLC_CODEC_FL32;
145 fmt->i_physical_channels = AOUT_CHANS_STEREO;
146 aout_FormatPrepare(fmt);
147 p_aout->sys->i_rate = fmt->i_rate;
149 // start queue
150 error = AudioQueueStart(p_sys->audioQueueRef, NULL);
151 msg_Dbg(p_aout, "Starting AudioQueue (status = %li)", error);
153 // start timeline for synchro
154 error = AudioQueueCreateTimeline(p_sys->audioQueueRef, &p_sys->timelineRef);
155 msg_Dbg(p_aout, "AudioQueue Timeline started (status = %li)", error);
156 if (error != noErr)
157 return VLC_EGENERIC;
159 #if TARGET_OS_IPHONE
160 // start audio session so playback continues if mute switch is on
161 AudioSessionInitialize (NULL,
162 kCFRunLoopCommonModes,
163 NULL,
164 NULL);
166 // Set audio session to mediaplayback
167 UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
168 AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(sessionCategory),&sessionCategory);
169 AudioSessionSetActive(true);
170 #endif
172 p_aout->time_get = TimeGet;
173 p_aout->play = Play;
174 p_aout->pause = Pause;
175 p_aout->flush = Flush;
176 return VLC_SUCCESS;
179 static void Stop(audio_output_t *p_aout)
181 AudioSessionSetActive(false);
183 p_aout->sys->i_played_length = 0;
184 AudioQueueDisposeTimeline(p_aout->sys->audioQueueRef, p_aout->sys->timelineRef);
185 AudioQueueStop(p_aout->sys->audioQueueRef, true);
186 AudioQueueDispose(p_aout->sys->audioQueueRef, true);
187 msg_Dbg(p_aout, "audioqueue stopped and disposed");
190 #pragma mark -
191 #pragma mark actual playback
193 static void Play(audio_output_t *p_aout, block_t *p_block)
195 AudioQueueBufferRef inBuffer = NULL;
196 OSStatus status;
198 status = AudioQueueAllocateBuffer(p_aout->sys->audioQueueRef, p_block->i_buffer, &inBuffer);
199 if (status == noErr) {
200 memcpy(inBuffer->mAudioData, p_block->p_buffer, p_block->i_buffer);
201 inBuffer->mAudioDataByteSize = p_block->i_buffer;
203 status = AudioQueueEnqueueBuffer(p_aout->sys->audioQueueRef, inBuffer, 0, NULL);
204 if (status == noErr)
205 p_aout->sys->i_played_length += p_block->i_length;
206 else
207 msg_Err(p_aout, "enqueuing buffer failed (%li)", status);
208 } else
209 msg_Err(p_aout, "buffer alloction failed (%li)", status);
211 block_Release(p_block);
214 void UnusedAudioQueueCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
215 /* this function does nothing, but needs to be here to make the AudioQueue API happy.
216 * additionally, we clean-up after empty buffers */
217 VLC_UNUSED(inUserData);
218 AudioQueueFreeBuffer(inAQ, inBuffer);
221 static void Pause(audio_output_t *p_aout, bool pause, mtime_t date)
223 VLC_UNUSED(date);
225 if (pause) {
226 AudioQueuePause(p_aout->sys->audioQueueRef);
227 AudioSessionSetActive(false);
228 } else {
229 AudioQueueStart(p_aout->sys->audioQueueRef, NULL);
230 AudioSessionSetActive(true);
234 static void Flush(audio_output_t *p_aout, bool wait)
236 if (!p_aout->sys->audioQueueRef)
237 return;
239 AudioQueueDisposeTimeline(p_aout->sys->audioQueueRef, p_aout->sys->timelineRef);
241 if (wait)
242 AudioQueueStop(p_aout->sys->audioQueueRef, false);
243 else
244 AudioQueueStop(p_aout->sys->audioQueueRef, true);
246 p_aout->sys->i_played_length = 0;
247 AudioQueueStart(p_aout->sys->audioQueueRef, NULL);
248 AudioQueueCreateTimeline(p_aout->sys->audioQueueRef, &p_aout->sys->timelineRef);
251 static int TimeGet(audio_output_t *p_aout, mtime_t *restrict delay)
253 AudioTimeStamp outTimeStamp;
254 Boolean b_discontinuity;
255 OSStatus status = AudioQueueGetCurrentTime(p_aout->sys->audioQueueRef, p_aout->sys->timelineRef, &outTimeStamp, &b_discontinuity);
257 if (status != noErr)
258 return -1;
260 if (b_discontinuity)
261 msg_Dbg(p_aout, "detected output discontinuity");
263 mtime_t i_pos = (mtime_t) outTimeStamp.mSampleTime * CLOCK_FREQ / p_aout->sys->i_rate;
264 *delay = p_aout->sys->i_played_length - i_pos;
266 return 0;