]> git.sesse.net Git - vlc/blob - modules/audio_output/audioqueue.c
AudioQueue: fix 'Flush' implementation
[vlc] / modules / audio_output / audioqueue.c
1 /*****************************************************************************
2  * audioqueue.c : AudioQueue audio output plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2010-2012 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Romain Goyet <romain.goyet@likid.org>
8  *          Felix Paul Kühne <fkuehne@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_aout.h>
35
36 #include <AudioToolBox/AudioToolBox.h>
37
38 #define FRAME_SIZE 2048
39
40 /*****************************************************************************
41  * aout_sys_t: AudioQueue audio output method descriptor
42  *****************************************************************************
43  * This structure is part of the audio output thread descriptor.
44  * It describes the specific properties of an audio device.
45  *****************************************************************************/
46 struct aout_sys_t
47 {
48     AudioQueueRef audioQueue;
49     bool          b_stopped;
50 };
51
52 /*****************************************************************************
53  * Local prototypes
54  *****************************************************************************/
55 static int  Open               (vlc_object_t *);
56 static void Close              (vlc_object_t *);
57 static void Play               (audio_output_t *, block_t *);
58 static void Pause              (audio_output_t *p_aout, bool pause, mtime_t date);
59 static void Flush              (audio_output_t *p_aout, bool wait);
60 static int  TimeGet            (audio_output_t *aout, mtime_t *);
61 static void AudioQueueCallback (void *, AudioQueueRef, AudioQueueBufferRef);
62
63 /*****************************************************************************
64  * Module descriptor
65  *****************************************************************************/
66 vlc_module_begin ()
67 set_shortname("AudioQueue")
68 set_description(N_("AudioQueue (iOS / Mac OS) audio output"))
69 set_capability("audio output", 40)
70 set_category(CAT_AUDIO)
71 set_subcategory(SUBCAT_AUDIO_AOUT)
72 add_shortcut("audioqueue")
73 set_callbacks(Open, Close)
74 vlc_module_end ()
75
76 /*****************************************************************************
77  * Start: open the audio device
78  *****************************************************************************/
79
80 static int Start(audio_output_t *p_aout, audio_sample_format_t *restrict fmt)
81 {
82     aout_sys_t *p_sys = p_aout->sys;
83     OSStatus status = 0;
84
85     // Setup the audio device.
86     AudioStreamBasicDescription deviceFormat;
87     deviceFormat.mSampleRate = 44100;
88     deviceFormat.mFormatID = kAudioFormatLinearPCM;
89     deviceFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; // Signed integer, little endian
90     deviceFormat.mBytesPerPacket = 4;
91     deviceFormat.mFramesPerPacket = 1;
92     deviceFormat.mBytesPerFrame = 4;
93     deviceFormat.mChannelsPerFrame = 2;
94     deviceFormat.mBitsPerChannel = 16;
95     deviceFormat.mReserved = 0;
96
97     // Create a new output AudioQueue for the device.
98     status = AudioQueueNewOutput(&deviceFormat,         // Format
99                                  AudioQueueCallback,    // Callback
100                                  NULL,                  // User data, passed to the callback
101                                  CFRunLoopGetMain(),    // RunLoop
102                                  kCFRunLoopCommonModes, // RunLoop mode
103                                  0,                     // Flags ; must be zero (per documentation)...
104                                  &(p_sys->audioQueue)); // Output
105
106     msg_Dbg(p_aout, "New AudioQueue output created (status = %i)", status);
107     if (status != noErr)
108         return VLC_EGENERIC;
109
110     fmt->i_format = VLC_CODEC_S16L;
111     fmt->i_physical_channels = AOUT_CHANS_STEREO;
112     fmt->i_rate = 44100;
113     aout_FormatPrepare(fmt);
114
115     p_aout->sys->b_stopped = false;
116
117     status = AudioQueueStart(p_sys->audioQueue, NULL);
118     msg_Dbg(p_aout, "Starting AudioQueue (status = %i)", status);
119
120     p_aout->time_get = TimeGet;
121     p_aout->play = Play;
122     p_aout->pause = Pause;
123     p_aout->flush = Flush;
124
125     if (status != noErr)
126         return VLC_EGENERIC;
127
128     return VLC_SUCCESS;
129 }
130
131 /*****************************************************************************
132  * Stop: close the audio device
133  *****************************************************************************/
134
135 static void Stop (audio_output_t *p_aout)
136 {
137     p_aout->sys->b_stopped = true;
138
139     msg_Dbg(p_aout, "Stopping AudioQueue");
140     AudioQueueStop(p_aout->sys->audioQueue, true);
141     msg_Dbg(p_aout, "Disposing AudioQueue");
142     AudioQueueDispose(p_aout->sys->audioQueue, true);
143 }
144
145 /*****************************************************************************
146  * actual playback
147  *****************************************************************************/
148
149 static void Play (audio_output_t *p_aout, block_t *p_block)
150 {
151     if (p_block != NULL) {
152         AudioQueueBufferRef inBuffer = NULL;
153         OSStatus status;
154
155         status = AudioQueueAllocateBuffer(p_aout->sys->audioQueue, FRAME_SIZE * 2, &inBuffer);
156         if (status != noErr) {
157             msg_Err(p_aout, "buffer alloction failed (%i)", status);
158             return;
159         }
160
161         memcpy(inBuffer->mAudioData, p_block->p_buffer, p_block->i_buffer);
162         inBuffer->mAudioDataByteSize = p_block->i_buffer;
163         block_Release(p_block);
164
165         status = AudioQueueEnqueueBuffer(p_aout->sys->audioQueue, inBuffer, 0, NULL);
166         if (status != noErr)
167             msg_Err(p_aout, "enqueuing buffer failed (%i)", status);
168     }
169 }
170
171 void AudioQueueCallback(void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer) {
172     /* this function does nothing, but needs to be here to make the AudioQueue API happy.
173      * without a callback, it will refuse to create an AudioQueue instance. */
174     VLC_UNUSED(inUserData);
175     VLC_UNUSED(inAQ);
176     VLC_UNUSED(inBuffer);
177 }
178
179 static void Pause (audio_output_t *p_aout, bool pause, mtime_t date)
180 {
181     VLC_UNUSED(date);
182
183     if (pause)
184         AudioQueuePause(p_aout->sys->audioQueue);
185     else
186         AudioQueueStart(p_aout->sys->audioQueue, NULL);
187 }
188
189 static void Flush (audio_output_t *p_aout, bool wait)
190 {
191     if (p_aout->sys->b_stopped || !p_aout->sys->audioQueue)
192         return;
193
194     if (wait)
195         AudioQueueFlush(p_aout->sys->audioQueue);
196     else
197         AudioQueueReset(p_aout->sys->audioQueue);
198 }
199
200 static int TimeGet (audio_output_t *p_aout, mtime_t *restrict delay)
201 {
202     // TODO
203
204     VLC_UNUSED(p_aout);
205     VLC_UNUSED(delay);
206
207     return -1;
208 }
209
210 /*****************************************************************************
211  * Module management
212  *****************************************************************************/
213
214 static int Open(vlc_object_t *obj)
215 {
216     audio_output_t *aout = (audio_output_t *)obj;
217     aout_sys_t *sys = malloc(sizeof (*sys));
218
219     if (unlikely(sys == NULL))
220         return VLC_ENOMEM;
221
222     aout->sys = sys;
223     aout->start = Start;
224     aout->stop = Stop;
225
226     return VLC_SUCCESS;
227 }
228
229 static void Close(vlc_object_t *obj)
230 {
231     audio_output_t *aout = (audio_output_t *)obj;
232     msg_Dbg( aout, "audioqueue: Close");
233     aout_sys_t *sys = aout->sys;
234
235     free(sys);
236 }