Mercurial > mplayer.hg
annotate libao2/ao_coreaudio.c @ 29946:54bacf6a38ca
Update the SuperH VEU vidix driver with code that calls fsync() after each
frame to make sure the frame is flushed in case of deferred io.
Patch by Magnus Damm, damm opensource se
author | cehoyos |
---|---|
date | Thu, 10 Dec 2009 23:16:08 +0000 |
parents | 02dec439f717 |
children | 9d09ff127c25 |
rev | line source |
---|---|
29209 | 1 /* |
2 * CoreAudio audio output driver for Mac OS X | |
3 * | |
4 * original copyright (C) Timothy J. Wood - Aug 2000 | |
5 * ported to MPlayer libao2 by Dan Christiansen | |
6 * | |
7 * The S/PDIF part of the code is based on the auhal audio output | |
8 * module from VideoLAN: | |
9 * Copyright (c) 2006 Derk-Jan Hartman <hartman at videolan dot org> | |
10 * | |
11 * This file is part of MPlayer. | |
12 * | |
13 * MPlayer is free software; you can redistribute it and/or modify | |
14 * it under the terms of the GNU General Public License as published by | |
15 * the Free Software Foundation; either version 2 of the License, or | |
16 * (at your option) any later version. | |
17 * | |
18 * MPlayer is distributed in the hope that it will be useful, | |
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
21 * GNU General Public License for more details. | |
22 * | |
23 * You should have received a copy of the GNU General Public License along | |
24 * along with MPlayer; if not, write to the Free Software | |
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
26 */ | |
27 | |
28 /* | |
29 * The MacOS X CoreAudio framework doesn't mesh as simply as some | |
30 * simpler frameworks do. This is due to the fact that CoreAudio pulls | |
31 * audio samples rather than having them pushed at it (which is nice | |
32 * when you are wanting to do good buffering of audio). | |
33 * | |
34 * AC-3 and MPEG audio passthrough is possible, but has never been tested | |
35 * due to lack of a soundcard that supports it. | |
36 */ | |
37 | |
38 #include <CoreServices/CoreServices.h> | |
39 #include <AudioUnit/AudioUnit.h> | |
40 #include <AudioToolbox/AudioToolbox.h> | |
41 #include <stdio.h> | |
42 #include <string.h> | |
43 #include <stdlib.h> | |
44 #include <inttypes.h> | |
45 #include <sys/types.h> | |
46 #include <unistd.h> | |
47 | |
48 #include "config.h" | |
49 #include "mp_msg.h" | |
50 | |
51 #include "audio_out.h" | |
52 #include "audio_out_internal.h" | |
53 #include "libaf/af_format.h" | |
54 #include "osdep/timer.h" | |
55 #include "libavutil/fifo.h" | |
56 | |
57 static const ao_info_t info = | |
58 { | |
59 "Darwin/Mac OS X native audio output", | |
60 "coreaudio", | |
61 "Timothy J. Wood & Dan Christiansen & Chris Roccati", | |
62 "" | |
63 }; | |
64 | |
65 LIBAO_EXTERN(coreaudio) | |
66 | |
67 /* Prefix for all mp_msg() calls */ | |
68 #define ao_msg(a, b, c...) mp_msg(a, b, "AO: [coreaudio] " c) | |
69 | |
70 typedef struct ao_coreaudio_s | |
71 { | |
72 AudioDeviceID i_selected_dev; /* Keeps DeviceID of the selected device. */ | |
73 int b_supports_digital; /* Does the currently selected device support digital mode? */ | |
74 int b_digital; /* Are we running in digital mode? */ | |
75 int b_muted; /* Are we muted in digital mode? */ | |
76 | |
77 /* AudioUnit */ | |
78 AudioUnit theOutputUnit; | |
79 | |
80 /* CoreAudio SPDIF mode specific */ | |
81 pid_t i_hog_pid; /* Keeps the pid of our hog status. */ | |
82 AudioStreamID i_stream_id; /* The StreamID that has a cac3 streamformat */ | |
83 int i_stream_index; /* The index of i_stream_id in an AudioBufferList */ | |
84 AudioStreamBasicDescription stream_format;/* The format we changed the stream to */ | |
85 AudioStreamBasicDescription sfmt_revert; /* The original format of the stream */ | |
86 int b_revert; /* Whether we need to revert the stream format */ | |
87 int b_changed_mixing; /* Whether we need to set the mixing mode back */ | |
88 int b_stream_format_changed; /* Flag for main thread to reset stream's format to digital and reset buffer */ | |
89 | |
90 /* Original common part */ | |
91 int packetSize; | |
92 int paused; | |
93 | |
94 /* Ring-buffer */ | |
95 AVFifoBuffer *buffer; | |
96 unsigned int buffer_len; ///< must always be num_chunks * chunk_size | |
97 unsigned int num_chunks; | |
98 unsigned int chunk_size; | |
99 } ao_coreaudio_t; | |
100 | |
101 static ao_coreaudio_t *ao = NULL; | |
102 | |
103 /** | |
104 * \brief add data to ringbuffer | |
105 */ | |
106 static int write_buffer(unsigned char* data, int len){ | |
107 int free = ao->buffer_len - av_fifo_size(ao->buffer); | |
108 if (len > free) len = free; | |
109 return av_fifo_generic_write(ao->buffer, data, len, NULL); | |
110 } | |
111 | |
112 /** | |
113 * \brief remove data from ringbuffer | |
114 */ | |
115 static int read_buffer(unsigned char* data,int len){ | |
116 int buffered = av_fifo_size(ao->buffer); | |
117 if (len > buffered) len = buffered; | |
29439
02dec439f717
100l, av_fifo_generic_read does not return anything useful, so ignore its
reimar
parents:
29401
diff
changeset
|
118 av_fifo_generic_read(ao->buffer, data, len, NULL); |
02dec439f717
100l, av_fifo_generic_read does not return anything useful, so ignore its
reimar
parents:
29401
diff
changeset
|
119 return len; |
29209 | 120 } |
121 | |
122 OSStatus theRenderProc(void *inRefCon, AudioUnitRenderActionFlags *inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumFrames, AudioBufferList *ioData) | |
123 { | |
124 int amt=av_fifo_size(ao->buffer); | |
125 int req=(inNumFrames)*ao->packetSize; | |
126 | |
127 if(amt>req) | |
128 amt=req; | |
129 | |
130 if(amt) | |
131 read_buffer((unsigned char *)ioData->mBuffers[0].mData, amt); | |
132 else audio_pause(); | |
133 ioData->mBuffers[0].mDataByteSize = amt; | |
134 | |
135 return noErr; | |
136 } | |
137 | |
138 static int control(int cmd,void *arg){ | |
139 ao_control_vol_t *control_vol; | |
140 OSStatus err; | |
141 Float32 vol; | |
142 switch (cmd) { | |
143 case AOCONTROL_GET_VOLUME: | |
144 control_vol = (ao_control_vol_t*)arg; | |
145 if (ao->b_digital) { | |
146 // Digital output has no volume adjust. | |
147 return CONTROL_FALSE; | |
148 } | |
149 err = AudioUnitGetParameter(ao->theOutputUnit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, &vol); | |
150 | |
151 if(err==0) { | |
152 // printf("GET VOL=%f\n", vol); | |
153 control_vol->left=control_vol->right=vol*100.0/4.0; | |
154 return CONTROL_TRUE; | |
155 } | |
156 else { | |
157 ao_msg(MSGT_AO, MSGL_WARN, "could not get HAL output volume: [%4.4s]\n", (char *)&err); | |
158 return CONTROL_FALSE; | |
159 } | |
160 | |
161 case AOCONTROL_SET_VOLUME: | |
162 control_vol = (ao_control_vol_t*)arg; | |
163 | |
164 if (ao->b_digital) { | |
165 // Digital output can not set volume. Here we have to return true | |
166 // to make mixer forget it. Else mixer will add a soft filter, | |
167 // that's not we expected and the filter not support ac3 stream | |
168 // will cause mplayer die. | |
169 | |
170 // Although not support set volume, but at least we support mute. | |
171 // MPlayer set mute by set volume to zero, we handle it. | |
172 if (control_vol->left == 0 && control_vol->right == 0) | |
173 ao->b_muted = 1; | |
174 else | |
175 ao->b_muted = 0; | |
176 return CONTROL_TRUE; | |
177 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
29212
diff
changeset
|
178 |
29209 | 179 vol=(control_vol->left+control_vol->right)*4.0/200.0; |
180 err = AudioUnitSetParameter(ao->theOutputUnit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, vol, 0); | |
181 if(err==0) { | |
182 // printf("SET VOL=%f\n", vol); | |
183 return CONTROL_TRUE; | |
184 } | |
185 else { | |
186 ao_msg(MSGT_AO, MSGL_WARN, "could not set HAL output volume: [%4.4s]\n", (char *)&err); | |
187 return CONTROL_FALSE; | |
188 } | |
189 /* Everything is currently unimplemented */ | |
190 default: | |
191 return CONTROL_FALSE; | |
192 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
29212
diff
changeset
|
193 |
29209 | 194 } |
195 | |
196 | |
197 static void print_format(int lev, const char* str, const AudioStreamBasicDescription *f){ | |
198 uint32_t flags=(uint32_t) f->mFormatFlags; | |
199 ao_msg(MSGT_AO,lev, "%s %7.1fHz %lubit [%c%c%c%c][%lu][%lu][%lu][%lu][%lu] %s %s %s%s%s%s\n", | |
200 str, f->mSampleRate, f->mBitsPerChannel, | |
201 (int)(f->mFormatID & 0xff000000) >> 24, | |
202 (int)(f->mFormatID & 0x00ff0000) >> 16, | |
203 (int)(f->mFormatID & 0x0000ff00) >> 8, | |
204 (int)(f->mFormatID & 0x000000ff) >> 0, | |
205 f->mFormatFlags, f->mBytesPerPacket, | |
206 f->mFramesPerPacket, f->mBytesPerFrame, | |
207 f->mChannelsPerFrame, | |
208 (flags&kAudioFormatFlagIsFloat) ? "float" : "int", | |
209 (flags&kAudioFormatFlagIsBigEndian) ? "BE" : "LE", | |
210 (flags&kAudioFormatFlagIsSignedInteger) ? "S" : "U", | |
211 (flags&kAudioFormatFlagIsPacked) ? " packed" : "", | |
212 (flags&kAudioFormatFlagIsAlignedHigh) ? " aligned" : "", | |
213 (flags&kAudioFormatFlagIsNonInterleaved) ? " ni" : "" ); | |
214 } | |
215 | |
216 | |
217 static int AudioDeviceSupportsDigital( AudioDeviceID i_dev_id ); | |
218 static int AudioStreamSupportsDigital( AudioStreamID i_stream_id ); | |
29212
eda346733b8c
Add missing 'void' to parameterless function declarations.
diego
parents:
29209
diff
changeset
|
219 static int OpenSPDIF(void); |
29209 | 220 static int AudioStreamChangeFormat( AudioStreamID i_stream_id, AudioStreamBasicDescription change_format ); |
221 static OSStatus RenderCallbackSPDIF( AudioDeviceID inDevice, | |
222 const AudioTimeStamp * inNow, | |
223 const void * inInputData, | |
224 const AudioTimeStamp * inInputTime, | |
225 AudioBufferList * outOutputData, | |
226 const AudioTimeStamp * inOutputTime, | |
227 void * threadGlobals ); | |
228 static OSStatus StreamListener( AudioStreamID inStream, | |
229 UInt32 inChannel, | |
230 AudioDevicePropertyID inPropertyID, | |
231 void * inClientData ); | |
232 static OSStatus DeviceListener( AudioDeviceID inDevice, | |
233 UInt32 inChannel, | |
234 Boolean isInput, | |
235 AudioDevicePropertyID inPropertyID, | |
236 void* inClientData ); | |
237 | |
238 static int init(int rate,int channels,int format,int flags) | |
239 { | |
240 AudioStreamBasicDescription inDesc; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
29212
diff
changeset
|
241 ComponentDescription desc; |
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
29212
diff
changeset
|
242 Component comp; |
29209 | 243 AURenderCallbackStruct renderCallback; |
244 OSStatus err; | |
245 UInt32 size, maxFrames, i_param_size; | |
246 char *psz_name; | |
247 AudioDeviceID devid_def = 0; | |
248 int b_alive; | |
249 | |
250 ao_msg(MSGT_AO,MSGL_V, "init([%dHz][%dch][%s][%d])\n", rate, channels, af_fmt2str_short(format), flags); | |
251 | |
252 ao = calloc(1, sizeof(ao_coreaudio_t)); | |
253 | |
254 ao->i_selected_dev = 0; | |
255 ao->b_supports_digital = 0; | |
256 ao->b_digital = 0; | |
257 ao->b_muted = 0; | |
258 ao->b_stream_format_changed = 0; | |
259 ao->i_hog_pid = -1; | |
260 ao->i_stream_id = 0; | |
261 ao->i_stream_index = -1; | |
262 ao->b_revert = 0; | |
263 ao->b_changed_mixing = 0; | |
264 | |
265 /* Probe whether device support S/PDIF stream output if input is AC3 stream. */ | |
266 if ((format & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_AC3) | |
267 { | |
268 /* Find the ID of the default Device. */ | |
269 i_param_size = sizeof(AudioDeviceID); | |
270 err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, | |
271 &i_param_size, &devid_def); | |
272 if (err != noErr) | |
273 { | |
274 ao_msg(MSGT_AO, MSGL_WARN, "could not get default audio device: [%4.4s]\n", (char *)&err); | |
275 goto err_out; | |
276 } | |
277 | |
278 /* Retrieve the length of the device name. */ | |
279 i_param_size = 0; | |
280 err = AudioDeviceGetPropertyInfo(devid_def, 0, 0, | |
281 kAudioDevicePropertyDeviceName, | |
282 &i_param_size, NULL); | |
283 if (err != noErr) | |
284 { | |
285 ao_msg(MSGT_AO, MSGL_WARN, "could not get default audio device name length: [%4.4s]\n", (char *)&err); | |
286 goto err_out; | |
287 } | |
288 | |
289 /* Retrieve the name of the device. */ | |
290 psz_name = (char *)malloc(i_param_size); | |
291 err = AudioDeviceGetProperty(devid_def, 0, 0, | |
292 kAudioDevicePropertyDeviceName, | |
293 &i_param_size, psz_name); | |
294 if (err != noErr) | |
295 { | |
296 ao_msg(MSGT_AO, MSGL_WARN, "could not get default audio device name: [%4.4s]\n", (char *)&err); | |
297 free( psz_name); | |
298 goto err_out; | |
299 } | |
300 | |
301 ao_msg(MSGT_AO,MSGL_V, "got default audio output device ID: %#lx Name: %s\n", devid_def, psz_name ); | |
302 | |
303 if (AudioDeviceSupportsDigital(devid_def)) | |
304 { | |
305 ao->b_supports_digital = 1; | |
306 ao->i_selected_dev = devid_def; | |
307 } | |
308 ao_msg(MSGT_AO,MSGL_V, "probe default audio output device whether support digital s/pdif output:%d\n", ao->b_supports_digital ); | |
309 | |
310 free( psz_name); | |
311 } | |
312 | |
313 // Build Description for the input format | |
314 inDesc.mSampleRate=rate; | |
315 inDesc.mFormatID=ao->b_supports_digital ? kAudioFormat60958AC3 : kAudioFormatLinearPCM; | |
316 inDesc.mChannelsPerFrame=channels; | |
317 switch(format&AF_FORMAT_BITS_MASK){ | |
318 case AF_FORMAT_8BIT: | |
319 inDesc.mBitsPerChannel=8; | |
320 break; | |
321 case AF_FORMAT_16BIT: | |
322 inDesc.mBitsPerChannel=16; | |
323 break; | |
324 case AF_FORMAT_24BIT: | |
325 inDesc.mBitsPerChannel=24; | |
326 break; | |
327 case AF_FORMAT_32BIT: | |
328 inDesc.mBitsPerChannel=32; | |
329 break; | |
330 default: | |
331 ao_msg(MSGT_AO, MSGL_WARN, "Unsupported format (0x%08x)\n", format); | |
332 goto err_out; | |
333 } | |
334 | |
335 if((format&AF_FORMAT_POINT_MASK)==AF_FORMAT_F) { | |
336 // float | |
337 inDesc.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked; | |
338 } | |
339 else if((format&AF_FORMAT_SIGN_MASK)==AF_FORMAT_SI) { | |
340 // signed int | |
341 inDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger|kAudioFormatFlagIsPacked; | |
342 } | |
343 else { | |
344 // unsigned int | |
345 inDesc.mFormatFlags = kAudioFormatFlagIsPacked; | |
346 } | |
347 if ((format & AF_FORMAT_SPECIAL_MASK) == AF_FORMAT_AC3) { | |
348 // Currently ac3 input (comes from hwac3) is always in native byte-order. | |
29401
f01023c524c3
Replace WORDS_BIGENDIAN by HAVE_BIGENDIAN in all internal code.
diego
parents:
29263
diff
changeset
|
349 #if HAVE_BIGENDIAN |
29209 | 350 inDesc.mFormatFlags |= kAudioFormatFlagIsBigEndian; |
351 #endif | |
352 } | |
353 else if ((format & AF_FORMAT_END_MASK) == AF_FORMAT_BE) | |
354 inDesc.mFormatFlags |= kAudioFormatFlagIsBigEndian; | |
355 | |
356 inDesc.mFramesPerPacket = 1; | |
357 ao->packetSize = inDesc.mBytesPerPacket = inDesc.mBytesPerFrame = inDesc.mFramesPerPacket*channels*(inDesc.mBitsPerChannel/8); | |
358 print_format(MSGL_V, "source:",&inDesc); | |
359 | |
360 if (ao->b_supports_digital) | |
361 { | |
362 b_alive = 1; | |
363 i_param_size = sizeof(b_alive); | |
364 err = AudioDeviceGetProperty(ao->i_selected_dev, 0, FALSE, | |
365 kAudioDevicePropertyDeviceIsAlive, | |
366 &i_param_size, &b_alive); | |
367 if (err != noErr) | |
368 ao_msg(MSGT_AO, MSGL_WARN, "could not check whether device is alive: [%4.4s]\n", (char *)&err); | |
369 if (!b_alive) | |
370 ao_msg(MSGT_AO, MSGL_WARN, "device is not alive\n" ); | |
371 /* S/PDIF output need device in HogMode. */ | |
372 i_param_size = sizeof(ao->i_hog_pid); | |
373 err = AudioDeviceGetProperty(ao->i_selected_dev, 0, FALSE, | |
374 kAudioDevicePropertyHogMode, | |
375 &i_param_size, &ao->i_hog_pid); | |
376 | |
377 if (err != noErr) | |
378 { | |
379 /* This is not a fatal error. Some drivers simply don't support this property. */ | |
380 ao_msg(MSGT_AO, MSGL_WARN, "could not check whether device is hogged: [%4.4s]\n", | |
381 (char *)&err); | |
382 ao->i_hog_pid = -1; | |
383 } | |
384 | |
385 if (ao->i_hog_pid != -1 && ao->i_hog_pid != getpid()) | |
386 { | |
387 ao_msg(MSGT_AO, MSGL_WARN, "Selected audio device is exclusively in use by another program.\n" ); | |
388 goto err_out; | |
389 } | |
390 ao->stream_format = inDesc; | |
391 return OpenSPDIF(); | |
392 } | |
393 | |
394 /* original analog output code */ | |
395 desc.componentType = kAudioUnitType_Output; | |
396 desc.componentSubType = kAudioUnitSubType_DefaultOutput; | |
397 desc.componentManufacturer = kAudioUnitManufacturer_Apple; | |
398 desc.componentFlags = 0; | |
399 desc.componentFlagsMask = 0; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
29212
diff
changeset
|
400 |
29209 | 401 comp = FindNextComponent(NULL, &desc); //Finds an component that meets the desc spec's |
402 if (comp == NULL) { | |
403 ao_msg(MSGT_AO, MSGL_WARN, "Unable to find Output Unit component\n"); | |
404 goto err_out; | |
405 } | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
29212
diff
changeset
|
406 |
29209 | 407 err = OpenAComponent(comp, &(ao->theOutputUnit)); //gains access to the services provided by the component |
408 if (err) { | |
409 ao_msg(MSGT_AO, MSGL_WARN, "Unable to open Output Unit component: [%4.4s]\n", (char *)&err); | |
410 goto err_out; | |
411 } | |
412 | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
29212
diff
changeset
|
413 // Initialize AudioUnit |
29209 | 414 err = AudioUnitInitialize(ao->theOutputUnit); |
415 if (err) { | |
416 ao_msg(MSGT_AO, MSGL_WARN, "Unable to initialize Output Unit component: [%4.4s]\n", (char *)&err); | |
417 goto err_out1; | |
418 } | |
419 | |
420 size = sizeof(AudioStreamBasicDescription); | |
421 err = AudioUnitSetProperty(ao->theOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &inDesc, size); | |
422 | |
423 if (err) { | |
424 ao_msg(MSGT_AO, MSGL_WARN, "Unable to set the input format: [%4.4s]\n", (char *)&err); | |
425 goto err_out2; | |
426 } | |
427 | |
428 size = sizeof(UInt32); | |
429 err = AudioUnitGetProperty(ao->theOutputUnit, kAudioDevicePropertyBufferSize, kAudioUnitScope_Input, 0, &maxFrames, &size); | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
29212
diff
changeset
|
430 |
29209 | 431 if (err) |
432 { | |
433 ao_msg(MSGT_AO,MSGL_WARN, "AudioUnitGetProperty returned [%4.4s] when getting kAudioDevicePropertyBufferSize\n", (char *)&err); | |
434 goto err_out2; | |
435 } | |
436 | |
437 ao->chunk_size = maxFrames;//*inDesc.mBytesPerFrame; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
29212
diff
changeset
|
438 |
29209 | 439 ao_data.samplerate = inDesc.mSampleRate; |
440 ao_data.channels = inDesc.mChannelsPerFrame; | |
441 ao_data.bps = ao_data.samplerate * inDesc.mBytesPerFrame; | |
442 ao_data.outburst = ao->chunk_size; | |
443 ao_data.buffersize = ao_data.bps; | |
444 | |
445 ao->num_chunks = (ao_data.bps+ao->chunk_size-1)/ao->chunk_size; | |
446 ao->buffer_len = ao->num_chunks * ao->chunk_size; | |
447 ao->buffer = av_fifo_alloc(ao->buffer_len); | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
29212
diff
changeset
|
448 |
29209 | 449 ao_msg(MSGT_AO,MSGL_V, "using %5d chunks of %d bytes (buffer len %d bytes)\n", (int)ao->num_chunks, (int)ao->chunk_size, (int)ao->buffer_len); |
450 | |
451 renderCallback.inputProc = theRenderProc; | |
452 renderCallback.inputProcRefCon = 0; | |
453 err = AudioUnitSetProperty(ao->theOutputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &renderCallback, sizeof(AURenderCallbackStruct)); | |
454 if (err) { | |
455 ao_msg(MSGT_AO, MSGL_WARN, "Unable to set the render callback: [%4.4s]\n", (char *)&err); | |
456 goto err_out2; | |
457 } | |
458 | |
459 reset(); | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
29212
diff
changeset
|
460 |
29209 | 461 return CONTROL_OK; |
462 | |
463 err_out2: | |
464 AudioUnitUninitialize(ao->theOutputUnit); | |
465 err_out1: | |
466 CloseComponent(ao->theOutputUnit); | |
467 err_out: | |
468 av_fifo_free(ao->buffer); | |
469 free(ao); | |
470 ao = NULL; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
29212
diff
changeset
|
471 return CONTROL_FALSE; |
29209 | 472 } |
473 | |
474 /***************************************************************************** | |
475 * Setup a encoded digital stream (SPDIF) | |
476 *****************************************************************************/ | |
29212
eda346733b8c
Add missing 'void' to parameterless function declarations.
diego
parents:
29209
diff
changeset
|
477 static int OpenSPDIF(void) |
29209 | 478 { |
479 OSStatus err = noErr; | |
480 UInt32 i_param_size, b_mix = 0; | |
481 Boolean b_writeable = 0; | |
482 AudioStreamID *p_streams = NULL; | |
483 int i, i_streams = 0; | |
484 | |
485 /* Start doing the SPDIF setup process. */ | |
486 ao->b_digital = 1; | |
487 | |
488 /* Hog the device. */ | |
489 i_param_size = sizeof(ao->i_hog_pid); | |
490 ao->i_hog_pid = getpid() ; | |
491 | |
492 err = AudioDeviceSetProperty(ao->i_selected_dev, 0, 0, FALSE, | |
493 kAudioDevicePropertyHogMode, i_param_size, &ao->i_hog_pid); | |
494 | |
495 if (err != noErr) | |
496 { | |
497 ao_msg(MSGT_AO, MSGL_WARN, "failed to set hogmode: [%4.4s]\n", (char *)&err); | |
498 ao->i_hog_pid = -1; | |
499 goto err_out; | |
500 } | |
501 | |
502 /* Set mixable to false if we are allowed to. */ | |
503 err = AudioDeviceGetPropertyInfo(ao->i_selected_dev, 0, FALSE, | |
504 kAudioDevicePropertySupportsMixing, | |
505 &i_param_size, &b_writeable); | |
506 err = AudioDeviceGetProperty(ao->i_selected_dev, 0, FALSE, | |
507 kAudioDevicePropertySupportsMixing, | |
508 &i_param_size, &b_mix); | |
509 if (err != noErr && b_writeable) | |
510 { | |
511 b_mix = 0; | |
512 err = AudioDeviceSetProperty(ao->i_selected_dev, 0, 0, FALSE, | |
513 kAudioDevicePropertySupportsMixing, | |
514 i_param_size, &b_mix); | |
515 ao->b_changed_mixing = 1; | |
516 } | |
517 if (err != noErr) | |
518 { | |
519 ao_msg(MSGT_AO, MSGL_WARN, "failed to set mixmode: [%4.4s]\n", (char *)&err); | |
520 goto err_out; | |
521 } | |
522 | |
523 /* Get a list of all the streams on this device. */ | |
524 err = AudioDeviceGetPropertyInfo(ao->i_selected_dev, 0, FALSE, | |
525 kAudioDevicePropertyStreams, | |
526 &i_param_size, NULL); | |
527 if (err != noErr) | |
528 { | |
529 ao_msg(MSGT_AO, MSGL_WARN, "could not get number of streams: [%4.4s]\n", (char *)&err); | |
530 goto err_out; | |
531 } | |
532 | |
533 i_streams = i_param_size / sizeof(AudioStreamID); | |
534 p_streams = (AudioStreamID *)malloc(i_param_size); | |
535 if (p_streams == NULL) | |
536 { | |
537 ao_msg(MSGT_AO, MSGL_WARN, "out of memory\n" ); | |
538 goto err_out; | |
539 } | |
540 | |
541 err = AudioDeviceGetProperty(ao->i_selected_dev, 0, FALSE, | |
542 kAudioDevicePropertyStreams, | |
543 &i_param_size, p_streams); | |
544 if (err != noErr) | |
545 { | |
546 ao_msg(MSGT_AO, MSGL_WARN, "could not get number of streams: [%4.4s]\n", (char *)&err); | |
547 if (p_streams) free(p_streams); | |
548 goto err_out; | |
549 } | |
550 | |
551 ao_msg(MSGT_AO, MSGL_V, "current device stream number: %d\n", i_streams); | |
552 | |
553 for (i = 0; i < i_streams && ao->i_stream_index < 0; ++i) | |
554 { | |
555 /* Find a stream with a cac3 stream. */ | |
556 AudioStreamBasicDescription *p_format_list = NULL; | |
557 int i_formats = 0, j = 0, b_digital = 0; | |
558 | |
559 /* Retrieve all the stream formats supported by each output stream. */ | |
560 err = AudioStreamGetPropertyInfo(p_streams[i], 0, | |
561 kAudioStreamPropertyPhysicalFormats, | |
562 &i_param_size, NULL); | |
563 if (err != noErr) | |
564 { | |
565 ao_msg(MSGT_AO, MSGL_WARN, "could not get number of streamformats: [%4.4s]\n", (char *)&err); | |
566 continue; | |
567 } | |
568 | |
569 i_formats = i_param_size / sizeof(AudioStreamBasicDescription); | |
570 p_format_list = (AudioStreamBasicDescription *)malloc(i_param_size); | |
571 if (p_format_list == NULL) | |
572 { | |
573 ao_msg(MSGT_AO, MSGL_WARN, "could not malloc the memory\n" ); | |
574 continue; | |
575 } | |
576 | |
577 err = AudioStreamGetProperty(p_streams[i], 0, | |
578 kAudioStreamPropertyPhysicalFormats, | |
579 &i_param_size, p_format_list); | |
580 if (err != noErr) | |
581 { | |
582 ao_msg(MSGT_AO, MSGL_WARN, "could not get the list of streamformats: [%4.4s]\n", (char *)&err); | |
583 if (p_format_list) free(p_format_list); | |
584 continue; | |
585 } | |
586 | |
587 /* Check if one of the supported formats is a digital format. */ | |
588 for (j = 0; j < i_formats; ++j) | |
589 { | |
590 if (p_format_list[j].mFormatID == 'IAC3' || | |
591 p_format_list[j].mFormatID == kAudioFormat60958AC3) | |
592 { | |
593 b_digital = 1; | |
594 break; | |
595 } | |
596 } | |
597 | |
598 if (b_digital) | |
599 { | |
600 /* If this stream supports a digital (cac3) format, then set it. */ | |
601 int i_requested_rate_format = -1; | |
602 int i_current_rate_format = -1; | |
603 int i_backup_rate_format = -1; | |
604 | |
605 ao->i_stream_id = p_streams[i]; | |
606 ao->i_stream_index = i; | |
607 | |
608 if (ao->b_revert == 0) | |
609 { | |
610 /* Retrieve the original format of this stream first if not done so already. */ | |
611 i_param_size = sizeof(ao->sfmt_revert); | |
612 err = AudioStreamGetProperty(ao->i_stream_id, 0, | |
613 kAudioStreamPropertyPhysicalFormat, | |
614 &i_param_size, | |
615 &ao->sfmt_revert); | |
616 if (err != noErr) | |
617 { | |
618 ao_msg(MSGT_AO, MSGL_WARN, "could not retrieve the original streamformat: [%4.4s]\n", (char *)&err); | |
619 if (p_format_list) free(p_format_list); | |
620 continue; | |
621 } | |
622 ao->b_revert = 1; | |
623 } | |
624 | |
625 for (j = 0; j < i_formats; ++j) | |
626 if (p_format_list[j].mFormatID == 'IAC3' || | |
627 p_format_list[j].mFormatID == kAudioFormat60958AC3) | |
628 { | |
629 if (p_format_list[j].mSampleRate == ao->stream_format.mSampleRate) | |
630 { | |
631 i_requested_rate_format = j; | |
632 break; | |
633 } | |
634 if (p_format_list[j].mSampleRate == ao->sfmt_revert.mSampleRate) | |
635 i_current_rate_format = j; | |
636 else if (i_backup_rate_format < 0 || p_format_list[j].mSampleRate > p_format_list[i_backup_rate_format].mSampleRate) | |
637 i_backup_rate_format = j; | |
638 } | |
639 | |
640 if (i_requested_rate_format >= 0) /* We prefer to output at the samplerate of the original audio. */ | |
641 ao->stream_format = p_format_list[i_requested_rate_format]; | |
642 else if (i_current_rate_format >= 0) /* If not possible, we will try to use the current samplerate of the device. */ | |
643 ao->stream_format = p_format_list[i_current_rate_format]; | |
644 else ao->stream_format = p_format_list[i_backup_rate_format]; /* And if we have to, any digital format will be just fine (highest rate possible). */ | |
645 } | |
646 if (p_format_list) free(p_format_list); | |
647 } | |
648 if (p_streams) free(p_streams); | |
649 | |
650 if (ao->i_stream_index < 0) | |
651 { | |
652 ao_msg(MSGT_AO, MSGL_WARN, "can not find any digital output stream format when OpenSPDIF().\n"); | |
653 goto err_out; | |
654 } | |
655 | |
656 print_format(MSGL_V, "original stream format:", &ao->sfmt_revert); | |
657 | |
658 if (!AudioStreamChangeFormat(ao->i_stream_id, ao->stream_format)) | |
659 goto err_out; | |
660 | |
661 err = AudioDeviceAddPropertyListener(ao->i_selected_dev, | |
662 kAudioPropertyWildcardChannel, | |
663 0, | |
664 kAudioDevicePropertyDeviceHasChanged, | |
665 DeviceListener, | |
666 NULL); | |
667 if (err != noErr) | |
668 ao_msg(MSGT_AO, MSGL_WARN, "AudioDeviceAddPropertyListener for kAudioDevicePropertyDeviceHasChanged failed: [%4.4s]\n", (char *)&err); | |
669 | |
670 | |
671 /* FIXME: If output stream is not native byte-order, we need change endian somewhere. */ | |
672 /* Although there's no such case reported. */ | |
29401
f01023c524c3
Replace WORDS_BIGENDIAN by HAVE_BIGENDIAN in all internal code.
diego
parents:
29263
diff
changeset
|
673 #if HAVE_BIGENDIAN |
29209 | 674 if (!(ao->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian)) |
675 #else | |
676 if (ao->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian) | |
677 #endif | |
678 ao_msg(MSGT_AO, MSGL_WARN, "output stream has a no-native byte-order, digital output may failed.\n"); | |
679 | |
680 /* For ac3/dts, just use packet size 6144 bytes as chunk size. */ | |
681 ao->chunk_size = ao->stream_format.mBytesPerPacket; | |
682 | |
683 ao_data.samplerate = ao->stream_format.mSampleRate; | |
684 ao_data.channels = ao->stream_format.mChannelsPerFrame; | |
685 ao_data.bps = ao_data.samplerate * (ao->stream_format.mBytesPerPacket/ao->stream_format.mFramesPerPacket); | |
686 ao_data.outburst = ao->chunk_size; | |
687 ao_data.buffersize = ao_data.bps; | |
688 | |
689 ao->num_chunks = (ao_data.bps+ao->chunk_size-1)/ao->chunk_size; | |
690 ao->buffer_len = ao->num_chunks * ao->chunk_size; | |
691 ao->buffer = av_fifo_alloc(ao->buffer_len); | |
692 | |
693 ao_msg(MSGT_AO,MSGL_V, "using %5d chunks of %d bytes (buffer len %d bytes)\n", (int)ao->num_chunks, (int)ao->chunk_size, (int)ao->buffer_len); | |
694 | |
695 | |
696 /* Add IOProc callback. */ | |
697 err = AudioDeviceAddIOProc(ao->i_selected_dev, | |
698 (AudioDeviceIOProc)RenderCallbackSPDIF, | |
699 (void *)ao); | |
700 if (err != noErr) | |
701 { | |
702 ao_msg(MSGT_AO, MSGL_WARN, "AudioDeviceAddIOProc failed: [%4.4s]\n", (char *)&err); | |
703 goto err_out1; | |
704 } | |
705 | |
706 reset(); | |
707 | |
708 return CONTROL_TRUE; | |
709 | |
710 err_out1: | |
711 if (ao->b_revert) | |
712 AudioStreamChangeFormat(ao->i_stream_id, ao->sfmt_revert); | |
713 err_out: | |
714 if (ao->b_changed_mixing && ao->sfmt_revert.mFormatID != kAudioFormat60958AC3) | |
715 { | |
716 int b_mix = 1; | |
717 err = AudioDeviceSetProperty(ao->i_selected_dev, 0, 0, FALSE, | |
718 kAudioDevicePropertySupportsMixing, | |
719 i_param_size, &b_mix); | |
720 if (err != noErr) | |
721 ao_msg(MSGT_AO, MSGL_WARN, "failed to set mixmode: [%4.4s]\n", | |
722 (char *)&err); | |
723 } | |
724 if (ao->i_hog_pid == getpid()) | |
725 { | |
726 ao->i_hog_pid = -1; | |
727 i_param_size = sizeof(ao->i_hog_pid); | |
728 err = AudioDeviceSetProperty(ao->i_selected_dev, 0, 0, FALSE, | |
729 kAudioDevicePropertyHogMode, | |
730 i_param_size, &ao->i_hog_pid); | |
731 if (err != noErr) | |
732 ao_msg(MSGT_AO, MSGL_WARN, "Could not release hogmode: [%4.4s]\n", | |
733 (char *)&err); | |
734 } | |
735 av_fifo_free(ao->buffer); | |
736 free(ao); | |
737 ao = NULL; | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
29212
diff
changeset
|
738 return CONTROL_FALSE; |
29209 | 739 } |
740 | |
741 /***************************************************************************** | |
742 * AudioDeviceSupportsDigital: Check i_dev_id for digital stream support. | |
743 *****************************************************************************/ | |
744 static int AudioDeviceSupportsDigital( AudioDeviceID i_dev_id ) | |
745 { | |
746 OSStatus err = noErr; | |
747 UInt32 i_param_size = 0; | |
748 AudioStreamID *p_streams = NULL; | |
749 int i = 0, i_streams = 0; | |
750 int b_return = CONTROL_FALSE; | |
751 | |
752 /* Retrieve all the output streams. */ | |
753 err = AudioDeviceGetPropertyInfo(i_dev_id, 0, FALSE, | |
754 kAudioDevicePropertyStreams, | |
755 &i_param_size, NULL); | |
756 if (err != noErr) | |
757 { | |
758 ao_msg(MSGT_AO,MSGL_V, "could not get number of streams: [%4.4s]\n", (char *)&err); | |
759 return CONTROL_FALSE; | |
760 } | |
761 | |
762 i_streams = i_param_size / sizeof(AudioStreamID); | |
763 p_streams = (AudioStreamID *)malloc(i_param_size); | |
764 if (p_streams == NULL) | |
765 { | |
766 ao_msg(MSGT_AO,MSGL_V, "out of memory\n"); | |
767 return CONTROL_FALSE; | |
768 } | |
769 | |
770 err = AudioDeviceGetProperty(i_dev_id, 0, FALSE, | |
771 kAudioDevicePropertyStreams, | |
772 &i_param_size, p_streams); | |
773 | |
774 if (err != noErr) | |
775 { | |
776 ao_msg(MSGT_AO,MSGL_V, "could not get number of streams: [%4.4s]\n", (char *)&err); | |
777 free(p_streams); | |
778 return CONTROL_FALSE; | |
779 } | |
780 | |
781 for (i = 0; i < i_streams; ++i) | |
782 { | |
783 if (AudioStreamSupportsDigital(p_streams[i])) | |
784 b_return = CONTROL_OK; | |
785 } | |
786 | |
787 free(p_streams); | |
788 return b_return; | |
789 } | |
790 | |
791 /***************************************************************************** | |
792 * AudioStreamSupportsDigital: Check i_stream_id for digital stream support. | |
793 *****************************************************************************/ | |
794 static int AudioStreamSupportsDigital( AudioStreamID i_stream_id ) | |
795 { | |
796 OSStatus err = noErr; | |
797 UInt32 i_param_size; | |
798 AudioStreamBasicDescription *p_format_list = NULL; | |
799 int i, i_formats, b_return = CONTROL_FALSE; | |
800 | |
801 /* Retrieve all the stream formats supported by each output stream. */ | |
802 err = AudioStreamGetPropertyInfo(i_stream_id, 0, | |
803 kAudioStreamPropertyPhysicalFormats, | |
804 &i_param_size, NULL); | |
805 if (err != noErr) | |
806 { | |
807 ao_msg(MSGT_AO,MSGL_V, "could not get number of streamformats: [%4.4s]\n", (char *)&err); | |
808 return CONTROL_FALSE; | |
809 } | |
810 | |
811 i_formats = i_param_size / sizeof(AudioStreamBasicDescription); | |
812 p_format_list = (AudioStreamBasicDescription *)malloc(i_param_size); | |
813 if (p_format_list == NULL) | |
814 { | |
815 ao_msg(MSGT_AO,MSGL_V, "could not malloc the memory\n" ); | |
816 return CONTROL_FALSE; | |
817 } | |
818 | |
819 err = AudioStreamGetProperty(i_stream_id, 0, | |
820 kAudioStreamPropertyPhysicalFormats, | |
821 &i_param_size, p_format_list); | |
822 if (err != noErr) | |
823 { | |
824 ao_msg(MSGT_AO,MSGL_V, "could not get the list of streamformats: [%4.4s]\n", (char *)&err); | |
825 free(p_format_list); | |
826 return CONTROL_FALSE; | |
827 } | |
828 | |
829 for (i = 0; i < i_formats; ++i) | |
830 { | |
831 print_format(MSGL_V, "supported format:", &p_format_list[i]); | |
832 | |
833 if (p_format_list[i].mFormatID == 'IAC3' || | |
834 p_format_list[i].mFormatID == kAudioFormat60958AC3) | |
835 b_return = CONTROL_OK; | |
836 } | |
837 | |
838 free(p_format_list); | |
839 return b_return; | |
840 } | |
841 | |
842 /***************************************************************************** | |
843 * AudioStreamChangeFormat: Change i_stream_id to change_format | |
844 *****************************************************************************/ | |
845 static int AudioStreamChangeFormat( AudioStreamID i_stream_id, AudioStreamBasicDescription change_format ) | |
846 { | |
847 OSStatus err = noErr; | |
848 UInt32 i_param_size = 0; | |
849 int i; | |
850 | |
851 static volatile int stream_format_changed; | |
852 stream_format_changed = 0; | |
853 | |
854 print_format(MSGL_V, "setting stream format:", &change_format); | |
855 | |
856 /* Install the callback. */ | |
857 err = AudioStreamAddPropertyListener(i_stream_id, 0, | |
858 kAudioStreamPropertyPhysicalFormat, | |
859 StreamListener, | |
860 (void *)&stream_format_changed); | |
861 if (err != noErr) | |
862 { | |
863 ao_msg(MSGT_AO, MSGL_WARN, "AudioStreamAddPropertyListener failed: [%4.4s]\n", (char *)&err); | |
864 return CONTROL_FALSE; | |
865 } | |
866 | |
867 /* Change the format. */ | |
868 err = AudioStreamSetProperty(i_stream_id, 0, 0, | |
869 kAudioStreamPropertyPhysicalFormat, | |
870 sizeof(AudioStreamBasicDescription), | |
871 &change_format); | |
872 if (err != noErr) | |
873 { | |
874 ao_msg(MSGT_AO, MSGL_WARN, "could not set the stream format: [%4.4s]\n", (char *)&err); | |
875 return CONTROL_FALSE; | |
876 } | |
877 | |
878 /* The AudioStreamSetProperty is not only asynchronious, | |
879 * it is also not Atomic, in its behaviour. | |
880 * Therefore we check 5 times before we really give up. | |
881 * FIXME: failing isn't actually implemented yet. */ | |
882 for (i = 0; i < 5; ++i) | |
883 { | |
884 AudioStreamBasicDescription actual_format; | |
885 int j; | |
886 for (j = 0; !stream_format_changed && j < 50; ++j) | |
887 usec_sleep(10000); | |
888 if (stream_format_changed) | |
889 stream_format_changed = 0; | |
890 else | |
891 ao_msg(MSGT_AO, MSGL_V, "reached timeout\n" ); | |
892 | |
893 i_param_size = sizeof(AudioStreamBasicDescription); | |
894 err = AudioStreamGetProperty(i_stream_id, 0, | |
895 kAudioStreamPropertyPhysicalFormat, | |
896 &i_param_size, | |
897 &actual_format); | |
898 | |
899 print_format(MSGL_V, "actual format in use:", &actual_format); | |
900 if (actual_format.mSampleRate == change_format.mSampleRate && | |
901 actual_format.mFormatID == change_format.mFormatID && | |
902 actual_format.mFramesPerPacket == change_format.mFramesPerPacket) | |
903 { | |
904 /* The right format is now active. */ | |
905 break; | |
906 } | |
907 /* We need to check again. */ | |
908 } | |
909 | |
910 /* Removing the property listener. */ | |
911 err = AudioStreamRemovePropertyListener(i_stream_id, 0, | |
912 kAudioStreamPropertyPhysicalFormat, | |
913 StreamListener); | |
914 if (err != noErr) | |
915 { | |
916 ao_msg(MSGT_AO, MSGL_WARN, "AudioStreamRemovePropertyListener failed: [%4.4s]\n", (char *)&err); | |
917 return CONTROL_FALSE; | |
918 } | |
919 | |
920 return CONTROL_TRUE; | |
921 } | |
922 | |
923 /***************************************************************************** | |
924 * RenderCallbackSPDIF: callback for SPDIF audio output | |
925 *****************************************************************************/ | |
926 static OSStatus RenderCallbackSPDIF( AudioDeviceID inDevice, | |
927 const AudioTimeStamp * inNow, | |
928 const void * inInputData, | |
929 const AudioTimeStamp * inInputTime, | |
930 AudioBufferList * outOutputData, | |
931 const AudioTimeStamp * inOutputTime, | |
932 void * threadGlobals ) | |
933 { | |
934 int amt = av_fifo_size(ao->buffer); | |
935 int req = outOutputData->mBuffers[ao->i_stream_index].mDataByteSize; | |
936 | |
937 if (amt > req) | |
938 amt = req; | |
939 if (amt) | |
940 read_buffer(ao->b_muted ? NULL : (unsigned char *)outOutputData->mBuffers[ao->i_stream_index].mData, amt); | |
941 | |
942 return noErr; | |
943 } | |
944 | |
945 | |
946 static int play(void* output_samples,int num_bytes,int flags) | |
29263
0f1b5b68af32
whitespace cosmetics: Remove all trailing whitespace.
diego
parents:
29212
diff
changeset
|
947 { |
29209 | 948 int wrote, b_digital; |
949 | |
950 // Check whether we need to reset the digital output stream. | |
951 if (ao->b_digital && ao->b_stream_format_changed) | |
952 { | |
953 ao->b_stream_format_changed = 0; | |
954 b_digital = AudioStreamSupportsDigital(ao->i_stream_id); | |
955 if (b_digital) | |
956 { | |
957 /* Current stream support digital format output, let's set it. */ | |
958 ao_msg(MSGT_AO, MSGL_V, "detected current stream support digital, try to restore digital output...\n"); | |
959 | |
960 if (!AudioStreamChangeFormat(ao->i_stream_id, ao->stream_format)) | |
961 { | |
962 ao_msg(MSGT_AO, MSGL_WARN, "restore digital output failed.\n"); | |
963 } | |
964 else | |
965 { | |
966 ao_msg(MSGT_AO, MSGL_WARN, "restore digital output succeed.\n"); | |
967 reset(); | |
968 } | |
969 } | |
970 else | |
971 ao_msg(MSGT_AO, MSGL_V, "detected current stream do not support digital.\n"); | |
972 } | |
973 | |
974 wrote=write_buffer(output_samples, num_bytes); | |
975 audio_resume(); | |
976 return wrote; | |
977 } | |
978 | |
979 /* set variables and buffer to initial state */ | |
980 static void reset(void) | |
981 { | |
982 audio_pause(); | |
983 av_fifo_reset(ao->buffer); | |
984 } | |
985 | |
986 | |
987 /* return available space */ | |
988 static int get_space(void) | |
989 { | |
990 return ao->buffer_len - av_fifo_size(ao->buffer); | |
991 } | |
992 | |
993 | |
994 /* return delay until audio is played */ | |
995 static float get_delay(void) | |
996 { | |
997 // inaccurate, should also contain the data buffered e.g. by the OS | |
998 return (float)av_fifo_size(ao->buffer)/(float)ao_data.bps; | |
999 } | |
1000 | |
1001 | |
1002 /* unload plugin and deregister from coreaudio */ | |
1003 static void uninit(int immed) | |
1004 { | |
1005 OSStatus err = noErr; | |
1006 UInt32 i_param_size = 0; | |
1007 | |
1008 if (!immed) { | |
1009 long long timeleft=(1000000LL*av_fifo_size(ao->buffer))/ao_data.bps; | |
1010 ao_msg(MSGT_AO,MSGL_DBG2, "%d bytes left @%d bps (%d usec)\n", av_fifo_size(ao->buffer), ao_data.bps, (int)timeleft); | |
1011 usec_sleep((int)timeleft); | |
1012 } | |
1013 | |
1014 if (!ao->b_digital) { | |
1015 AudioOutputUnitStop(ao->theOutputUnit); | |
1016 AudioUnitUninitialize(ao->theOutputUnit); | |
1017 CloseComponent(ao->theOutputUnit); | |
1018 } | |
1019 else { | |
1020 /* Stop device. */ | |
1021 err = AudioDeviceStop(ao->i_selected_dev, | |
1022 (AudioDeviceIOProc)RenderCallbackSPDIF); | |
1023 if (err != noErr) | |
1024 ao_msg(MSGT_AO, MSGL_WARN, "AudioDeviceStop failed: [%4.4s]\n", (char *)&err); | |
1025 | |
1026 /* Remove IOProc callback. */ | |
1027 err = AudioDeviceRemoveIOProc(ao->i_selected_dev, | |
1028 (AudioDeviceIOProc)RenderCallbackSPDIF); | |
1029 if (err != noErr) | |
1030 ao_msg(MSGT_AO, MSGL_WARN, "AudioDeviceRemoveIOProc failed: [%4.4s]\n", (char *)&err); | |
1031 | |
1032 if (ao->b_revert) | |
1033 AudioStreamChangeFormat(ao->i_stream_id, ao->sfmt_revert); | |
1034 | |
1035 if (ao->b_changed_mixing && ao->sfmt_revert.mFormatID != kAudioFormat60958AC3) | |
1036 { | |
1037 int b_mix; | |
1038 Boolean b_writeable; | |
1039 /* Revert mixable to true if we are allowed to. */ | |
1040 err = AudioDeviceGetPropertyInfo(ao->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing, | |
1041 &i_param_size, &b_writeable); | |
1042 err = AudioDeviceGetProperty(ao->i_selected_dev, 0, FALSE, kAudioDevicePropertySupportsMixing, | |
1043 &i_param_size, &b_mix); | |
1044 if (err != noErr && b_writeable) | |
1045 { | |
1046 b_mix = 1; | |
1047 err = AudioDeviceSetProperty(ao->i_selected_dev, 0, 0, FALSE, | |
1048 kAudioDevicePropertySupportsMixing, i_param_size, &b_mix); | |
1049 } | |
1050 if (err != noErr) | |
1051 ao_msg(MSGT_AO, MSGL_WARN, "failed to set mixmode: [%4.4s]\n", (char *)&err); | |
1052 } | |
1053 if (ao->i_hog_pid == getpid()) | |
1054 { | |
1055 ao->i_hog_pid = -1; | |
1056 i_param_size = sizeof(ao->i_hog_pid); | |
1057 err = AudioDeviceSetProperty(ao->i_selected_dev, 0, 0, FALSE, | |
1058 kAudioDevicePropertyHogMode, i_param_size, &ao->i_hog_pid); | |
1059 if (err != noErr) ao_msg(MSGT_AO, MSGL_WARN, "Could not release hogmode: [%4.4s]\n", (char *)&err); | |
1060 } | |
1061 } | |
1062 | |
1063 av_fifo_free(ao->buffer); | |
1064 free(ao); | |
1065 ao = NULL; | |
1066 } | |
1067 | |
1068 | |
1069 /* stop playing, keep buffers (for pause) */ | |
1070 static void audio_pause(void) | |
1071 { | |
1072 OSErr err=noErr; | |
1073 | |
1074 /* Stop callback. */ | |
1075 if (!ao->b_digital) | |
1076 { | |
1077 err=AudioOutputUnitStop(ao->theOutputUnit); | |
1078 if (err != noErr) | |
1079 ao_msg(MSGT_AO,MSGL_WARN, "AudioOutputUnitStop returned [%4.4s]\n", (char *)&err); | |
1080 } | |
1081 else | |
1082 { | |
1083 err = AudioDeviceStop(ao->i_selected_dev, (AudioDeviceIOProc)RenderCallbackSPDIF); | |
1084 if (err != noErr) | |
1085 ao_msg(MSGT_AO, MSGL_WARN, "AudioDeviceStop failed: [%4.4s]\n", (char *)&err); | |
1086 } | |
1087 ao->paused = 1; | |
1088 } | |
1089 | |
1090 | |
1091 /* resume playing, after audio_pause() */ | |
1092 static void audio_resume(void) | |
1093 { | |
1094 OSErr err=noErr; | |
1095 | |
1096 if (!ao->paused) | |
1097 return; | |
1098 | |
1099 /* Start callback. */ | |
1100 if (!ao->b_digital) | |
1101 { | |
1102 err = AudioOutputUnitStart(ao->theOutputUnit); | |
1103 if (err != noErr) | |
1104 ao_msg(MSGT_AO,MSGL_WARN, "AudioOutputUnitStart returned [%4.4s]\n", (char *)&err); | |
1105 } | |
1106 else | |
1107 { | |
1108 err = AudioDeviceStart(ao->i_selected_dev, (AudioDeviceIOProc)RenderCallbackSPDIF); | |
1109 if (err != noErr) | |
1110 ao_msg(MSGT_AO, MSGL_WARN, "AudioDeviceStart failed: [%4.4s]\n", (char *)&err); | |
1111 } | |
1112 ao->paused = 0; | |
1113 } | |
1114 | |
1115 /***************************************************************************** | |
1116 * StreamListener | |
1117 *****************************************************************************/ | |
1118 static OSStatus StreamListener( AudioStreamID inStream, | |
1119 UInt32 inChannel, | |
1120 AudioDevicePropertyID inPropertyID, | |
1121 void * inClientData ) | |
1122 { | |
1123 switch (inPropertyID) | |
1124 { | |
1125 case kAudioStreamPropertyPhysicalFormat: | |
1126 ao_msg(MSGT_AO, MSGL_V, "got notify kAudioStreamPropertyPhysicalFormat changed.\n"); | |
1127 if (inClientData) | |
1128 *(volatile int *)inClientData = 1; | |
1129 default: | |
1130 break; | |
1131 } | |
1132 return noErr; | |
1133 } | |
1134 | |
1135 static OSStatus DeviceListener( AudioDeviceID inDevice, | |
1136 UInt32 inChannel, | |
1137 Boolean isInput, | |
1138 AudioDevicePropertyID inPropertyID, | |
1139 void* inClientData ) | |
1140 { | |
1141 switch (inPropertyID) | |
1142 { | |
1143 case kAudioDevicePropertyDeviceHasChanged: | |
1144 ao_msg(MSGT_AO, MSGL_WARN, "got notify kAudioDevicePropertyDeviceHasChanged.\n"); | |
1145 ao->b_stream_format_changed = 1; | |
1146 default: | |
1147 break; | |
1148 } | |
1149 return noErr; | |
1150 } |