1 /*****************************************************************************
2 * Copyright (C) 2000-2013 VLC authors and VideoLAN
5 * Authors: Felix Paul Kühne <fkuehne at videolan dot org>
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 *****************************************************************************/
29 #import <vlc_common.h>
30 #import <vlc_plugin.h>
32 #import <AudioToolBox/AudioQueue.h>
33 #import <TargetConditionals.h>
35 #import <AudioToolBox/AudioSession.h>
37 #define AudioSessionSetActive(x)
41 #pragma mark private declarations
45 AudioQueueRef audioQueueRef
;
46 AudioQueueTimelineRef timelineRef
;
48 mtime_t i_played_length
;
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 );
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
)
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
))
86 aout
->volume_set
= VolumeSet
;
89 aout_VolumeReport(aout
, 1.0);
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
;
103 static int VolumeSet(audio_output_t
* p_aout
, float volume
)
105 struct aout_sys_t
*p_sys
= p_aout
->sys
;
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
);
117 static int Start(audio_output_t
*p_aout
, audio_sample_format_t
*restrict fmt
)
119 aout_sys_t
*p_sys
= p_aout
->sys
;
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
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
);
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
;
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
);
160 // start audio session so playback continues if mute switch is on
161 AudioSessionInitialize (NULL
,
162 kCFRunLoopCommonModes
,
166 // Set audio session to mediaplayback
167 UInt32 sessionCategory
= kAudioSessionCategory_MediaPlayback
;
168 AudioSessionSetProperty(kAudioSessionProperty_AudioCategory
, sizeof(sessionCategory
),&sessionCategory
);
169 AudioSessionSetActive(true);
172 p_aout
->time_get
= TimeGet
;
174 p_aout
->pause
= Pause
;
175 p_aout
->flush
= Flush
;
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");
191 #pragma mark actual playback
193 static void Play(audio_output_t
*p_aout
, block_t
*p_block
)
195 AudioQueueBufferRef inBuffer
= NULL
;
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
);
205 p_aout
->sys
->i_played_length
+= p_block
->i_length
;
207 msg_Err(p_aout
, "enqueuing buffer failed (%li)", status
);
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
)
226 AudioQueuePause(p_aout
->sys
->audioQueueRef
);
227 AudioSessionSetActive(false);
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
)
239 AudioQueueDisposeTimeline(p_aout
->sys
->audioQueueRef
, p_aout
->sys
->timelineRef
);
242 AudioQueueStop(p_aout
->sys
->audioQueueRef
, false);
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
);
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
;