1 /*****************************************************************************
2 * aout.m: CoreAudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2002-2003 VideoLAN
5 * $Id: aout.m,v 1.25 2003/03/14 00:06:02 massiot Exp $
7 * Authors: Colin Delacroix <colin@zoy.org>
8 * Jon Lech Johansen <jon-vl@nanocrew.net>
9 * Christophe Massiot <massiot@via.ecp.fr>
10 * Heiko Panther <heiko.panther@web.de>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
36 #include "aout_internal.h"
38 #include <Carbon/Carbon.h>
39 #include <CoreAudio/HostTime.h>
40 #include <CoreAudio/AudioHardware.h>
42 #define A52_FRAME_NB 1536
44 #define STREAM_FORMAT_MSG( pre, sfm ) \
45 pre ": [%ld][%4.4s][%ld][%ld][%ld][%ld][%ld][%ld]", \
46 (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
47 sfm.mFormatFlags, sfm.mBytesPerPacket, \
48 sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
49 sfm.mChannelsPerFrame, sfm.mBitsPerChannel
51 /*****************************************************************************
53 ****************************************************************************/
56 AudioDeviceClassA52 = 1 << 0,
57 AudioDeviceClassPCM2 = 1 << 1,
58 AudioDeviceClassPCM4 = 1 << 2,
59 AudioDeviceClassPCM6 = 1 << 3,
60 AudioDeviceClassPCM8 = 1 << 4
63 static struct aout_class_t
66 UInt32 mChannelsPerFrame;
67 enum AudioDeviceClass class;
68 const char * psz_class;
72 { /* old A/52 format type */
79 { /* new A/52 format type */
87 kAudioFormatLinearPCM,
94 kAudioFormatLinearPCM,
101 kAudioFormatLinearPCM,
103 AudioDeviceClassPCM6,
108 kAudioFormatLinearPCM,
110 AudioDeviceClassPCM8,
115 #define N_AOUT_CLASSES (sizeof(aout_classes)/sizeof(aout_classes[0]))
117 /*****************************************************************************
119 ****************************************************************************/
128 /*****************************************************************************
130 ****************************************************************************/
134 char * psz_device_name;
137 AudioStreamBasicDescription ** pp_streams;
140 /*****************************************************************************
141 * aout_sys_t: private audio output method descriptor
142 *****************************************************************************
143 * This structure is part of the audio output thread descriptor.
144 * It describes the CoreAudio specific properties of an output thread.
145 *****************************************************************************/
152 struct aout_dev_t * p_devices;
155 struct aout_option_t * p_options;
158 AudioStreamBasicDescription stream_format;
161 UInt32 i_buffer_size;
165 /*****************************************************************************
167 *****************************************************************************/
168 static int InitHardwareInfo ( aout_instance_t * p_aout );
169 static int InitDeviceInfo ( UInt32 i_dev, aout_instance_t * p_aout );
170 static void FreeDeviceInfo ( UInt32 i_dev, aout_instance_t * p_aout );
171 static void FreeHardwareInfo ( aout_instance_t * p_aout );
172 static int InitDevice ( aout_instance_t * p_aout );
173 static void FreeDevice ( aout_instance_t * p_aout );
174 static int GetStreamID ( AudioDeviceID devid, UInt32 i_idx,
175 AudioStreamID * p_sid );
176 static int InitStreamInfo ( UInt32 i_dev, aout_instance_t * p_aout,
178 static void FreeStreamInfo ( UInt32 i_dev, aout_instance_t * p_aout,
180 static void InitDeviceVar ( aout_instance_t * p_aout, int i_option,
181 vlc_bool_t b_change );
183 static void Play ( aout_instance_t * p_aout );
185 static OSStatus IOCallback ( AudioDeviceID inDevice,
186 const AudioTimeStamp * inNow,
187 const void * inInputData,
188 const AudioTimeStamp * inInputTime,
189 AudioBufferList * outOutputData,
190 const AudioTimeStamp * inOutputTime,
191 void * threadGlobals );
193 static OSStatus HardwareListener ( AudioHardwarePropertyID inPropertyID,
194 void * inClientData );
196 static OSStatus DeviceListener ( AudioDeviceID inDevice,
199 AudioDevicePropertyID inPropertyID,
200 void * inClientData );
202 static OSStatus StreamListener ( AudioStreamID inStream,
204 AudioDevicePropertyID inPropertyID,
205 void * inClientData );
207 /*****************************************************************************
208 * Open: open a CoreAudio HAL device
209 *****************************************************************************/
210 int E_(OpenAudio)( vlc_object_t * p_this )
214 struct aout_sys_t * p_sys;
215 aout_instance_t * p_aout = (aout_instance_t *)p_this;
217 /* Allocate structure */
218 p_sys = (struct aout_sys_t *)malloc( sizeof( struct aout_sys_t ) );
221 msg_Err( p_aout, "out of memory" );
222 return( VLC_ENOMEM );
225 memset( p_sys, 0, sizeof( struct aout_sys_t ) );
227 p_aout->output.p_sys = p_sys;
228 p_aout->output.pf_play = Play;
230 vlc_mutex_init( p_aout, &p_sys->lock );
232 if( InitHardwareInfo( p_aout ) )
234 msg_Err( p_aout, "InitHardwareInfo failed" );
235 vlc_mutex_destroy( &p_sys->lock );
236 free( (void *)p_sys );
237 return( VLC_EGENERIC );
240 if( var_Type( p_aout, "audio-device" ) == 0 )
242 InitDeviceVar( p_aout, config_GetInt( p_aout, "macosx-adev" ),
246 if( InitDevice( p_aout ) )
248 msg_Err( p_aout, "InitDevice failed" );
249 FreeHardwareInfo( p_aout );
250 vlc_mutex_destroy( &p_sys->lock );
251 free( (void *)p_sys );
252 return( VLC_EGENERIC );
255 /* Get a description of the stream format */
256 i_param_size = sizeof( AudioStreamBasicDescription );
257 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
258 kAudioDevicePropertyStreamFormat,
259 &i_param_size, &p_sys->stream_format );
262 msg_Err( p_aout, "failed to get stream format: [%4.4s]",
264 FreeDevice( p_aout );
265 FreeHardwareInfo( p_aout );
266 vlc_mutex_destroy( &p_sys->lock );
267 free( (void *)p_sys );
268 return( VLC_EGENERIC );
271 /* Set the output sample rate */
272 p_aout->output.output.i_rate =
273 (unsigned int)p_sys->stream_format.mSampleRate;
275 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "using format",
276 p_sys->stream_format ) );
278 /* Get the buffer size */
279 i_param_size = sizeof( p_sys->i_buffer_size );
280 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
281 kAudioDevicePropertyBufferSize,
282 &i_param_size, &p_sys->i_buffer_size );
285 msg_Err( p_aout, "failed to get buffer size: [%4.4s]",
287 FreeDevice( p_aout );
288 FreeHardwareInfo( p_aout );
289 vlc_mutex_destroy( &p_sys->lock );
290 free( (void *)p_sys );
291 return( VLC_EGENERIC );
294 msg_Dbg( p_aout, "device buffer size: [%ld]", p_sys->i_buffer_size );
296 /* If we do AC3 over SPDIF, set buffer size to one AC3 frame */
297 if( ( p_sys->stream_format.mFormatID == kAudioFormat60958AC3 ||
298 p_sys->stream_format.mFormatID == 'IAC3' ) &&
299 p_sys->i_buffer_size != AOUT_SPDIF_SIZE )
301 p_sys->i_buffer_size = AOUT_SPDIF_SIZE;
302 i_param_size = sizeof( p_sys->i_buffer_size );
303 err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
304 kAudioDevicePropertyBufferSize,
305 i_param_size, &p_sys->i_buffer_size );
308 msg_Err( p_aout, "failed to set buffer size: [%4.4s]",
310 FreeDevice( p_aout );
311 FreeHardwareInfo( p_aout );
312 vlc_mutex_destroy( &p_sys->lock );
313 free( (void *)p_sys );
314 return( VLC_EGENERIC );
317 msg_Dbg( p_aout, "device buffer size set to: [%ld]",
318 p_sys->i_buffer_size );
321 switch( p_sys->stream_format.mFormatID )
323 case kAudioFormatLinearPCM:
324 p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
326 switch( p_sys->stream_format.mChannelsPerFrame )
329 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
333 p_aout->output.output.i_physical_channels =
334 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
338 p_aout->output.output.i_physical_channels =
339 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
340 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
344 p_aout->output.output.i_physical_channels =
345 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
346 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
347 AOUT_CHAN_CENTER | AOUT_CHAN_LFE;
351 p_aout->output.output.i_physical_channels =
352 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
353 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
354 AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
355 AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT;
359 msg_Err( p_aout, "unknown channel count: [%ld]",
360 p_sys->stream_format.mChannelsPerFrame );
361 FreeDevice( p_aout );
362 FreeHardwareInfo( p_aout );
363 vlc_mutex_destroy( &p_sys->lock );
364 free( (void *)p_sys );
365 return( VLC_EGENERIC );
368 p_aout->output.i_nb_samples = (int)( p_sys->i_buffer_size /
369 p_sys->stream_format.mBytesPerFrame );
371 aout_VolumeSoftInit( p_aout );
375 case kAudioFormat60958AC3:
376 p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
377 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
378 p_aout->output.output.i_frame_length = A52_FRAME_NB;
379 p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
381 aout_VolumeNoneInit( p_aout );
385 msg_Err( p_aout, "unknown hardware format: [%4.4s]",
386 (char *)&p_sys->stream_format.mFormatID );
387 FreeDevice( p_aout );
388 FreeHardwareInfo( p_aout );
389 vlc_mutex_destroy( &p_sys->lock );
390 free( (void *)p_sys );
391 return( VLC_EGENERIC );
394 /* Set buffer frame size */
395 i_param_size = sizeof( p_aout->output.i_nb_samples );
396 err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
397 kAudioDevicePropertyBufferFrameSize,
399 &p_aout->output.i_nb_samples );
402 msg_Err( p_aout, "failed to set buffer frame size: [%4.4s]",
404 FreeDevice( p_aout );
405 FreeHardwareInfo( p_aout );
406 vlc_mutex_destroy( &p_sys->lock );
407 free( (void *)p_sys );
408 return( VLC_EGENERIC );
411 msg_Dbg( p_aout, "device buffer frame size set to: [%d]",
412 p_aout->output.i_nb_samples );
415 err = AudioDeviceAddIOProc( p_sys->devid,
416 (AudioDeviceIOProc)IOCallback,
420 msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]",
422 FreeDevice( p_aout );
423 FreeHardwareInfo( p_aout );
424 vlc_mutex_destroy( &p_sys->lock );
425 free( (void *)p_sys );
426 return( VLC_EGENERIC );
430 err = AudioDeviceStart( p_sys->devid, (AudioDeviceIOProc)IOCallback );
433 msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]",
436 err = AudioDeviceRemoveIOProc( p_sys->devid,
437 (AudioDeviceIOProc)IOCallback );
440 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
444 FreeDevice( p_aout );
445 FreeHardwareInfo( p_aout );
446 vlc_mutex_destroy( &p_sys->lock );
447 free( (void *)p_sys );
449 return( VLC_EGENERIC );
452 err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
457 msg_Err( p_aout, "AudioHardwareAddPropertyListener failed: %4.4s",
461 err = AudioDeviceStop( p_sys->devid,
462 (AudioDeviceIOProc)IOCallback );
465 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
469 /* Remove callback */
470 err = AudioDeviceRemoveIOProc( p_sys->devid,
471 (AudioDeviceIOProc)IOCallback );
474 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
478 FreeDevice( p_aout );
479 FreeHardwareInfo( p_aout );
480 vlc_mutex_destroy( &p_sys->lock );
481 free( (void *)p_sys );
483 return( VLC_EGENERIC );
486 /* Let's pray for the following operation to be atomic... */
487 p_sys->clock_diff = - (mtime_t)
488 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
489 p_sys->clock_diff += mdate();
491 return( VLC_SUCCESS );
494 /*****************************************************************************
495 * Close: close the CoreAudio HAL device
496 *****************************************************************************/
497 void E_(CloseAudio)( aout_instance_t * p_aout )
500 struct aout_sys_t * p_sys = p_aout->output.p_sys;
502 if( p_sys->b_dev_alive )
505 err = AudioDeviceStop( p_sys->devid,
506 (AudioDeviceIOProc)IOCallback );
509 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
513 /* Remove callback */
514 err = AudioDeviceRemoveIOProc( p_sys->devid,
515 (AudioDeviceIOProc)IOCallback );
518 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
522 FreeDevice( p_aout );
525 err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
529 msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]",
533 FreeHardwareInfo( p_aout );
535 vlc_mutex_destroy( &p_sys->lock );
540 /*****************************************************************************
541 * Play: nothing to do
542 *****************************************************************************/
543 static void Play( aout_instance_t * p_aout )
547 /*****************************************************************************
548 * IOCallback: callback for audio output
549 *****************************************************************************/
550 static OSStatus IOCallback( AudioDeviceID inDevice,
551 const AudioTimeStamp * inNow,
552 const void * inInputData,
553 const AudioTimeStamp * inInputTime,
554 AudioBufferList * outOutputData,
555 const AudioTimeStamp * inOutputTime,
556 void * threadGlobals )
558 aout_buffer_t * p_buffer;
559 AudioTimeStamp host_time;
560 mtime_t current_date;
562 aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
563 struct aout_sys_t * p_sys = p_aout->output.p_sys;
565 host_time.mFlags = kAudioTimeStampHostTimeValid;
566 AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
567 current_date = p_sys->clock_diff +
568 AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
570 #define B_SPDI (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i'))
571 p_buffer = aout_OutputNextBuffer( p_aout, current_date, B_SPDI );
574 if( p_buffer != NULL )
576 /* move data into output data buffer */
577 BlockMoveData( p_buffer->p_buffer,
578 outOutputData->mBuffers[ 0 ].mData,
579 p_sys->i_buffer_size );
581 aout_BufferFree( p_buffer );
585 if( p_aout->output.output.i_format == VLC_FOURCC('f','l','3','2') )
587 UInt32 i, i_size = p_sys->i_buffer_size / sizeof(float);
588 float * p = (float *)outOutputData->mBuffers[ 0 ].mData;
590 for( i = 0; i < i_size; i++ )
597 memset( outOutputData->mBuffers[ 0 ].mData,
598 0, p_sys->i_buffer_size );
605 /*****************************************************************************
607 *****************************************************************************/
608 static int InitHardwareInfo( aout_instance_t * p_aout )
611 UInt32 i, i_param_size;
612 AudioDeviceID devid_def;
613 AudioDeviceID * p_devices;
615 struct aout_sys_t * p_sys = p_aout->output.p_sys;
617 vlc_mutex_lock( &p_sys->lock );
619 /* Get number of devices */
620 err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
621 &i_param_size, NULL );
624 msg_Err( p_aout, "AudioHardwareGetPropertyInfo failed: [%4.4s]",
626 vlc_mutex_unlock( &p_sys->lock );
627 return( VLC_EGENERIC );
630 p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
632 if( p_sys->i_devices < 1 )
634 msg_Err( p_aout, "no devices found" );
635 vlc_mutex_unlock( &p_sys->lock );
636 return( VLC_EGENERIC );
639 msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
641 /* Allocate DeviceID array */
642 p_devices = (AudioDeviceID *)malloc( i_param_size );
643 if( p_devices == NULL )
645 msg_Err( p_aout, "out of memory" );
646 vlc_mutex_unlock( &p_sys->lock );
647 return( VLC_ENOMEM );
650 /* Populate DeviceID array */
651 err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
652 &i_param_size, (void *)p_devices );
655 msg_Err( p_aout, "AudioHardwareGetProperty failed: [%4.4s]",
657 free( (void *)p_devices );
658 vlc_mutex_unlock( &p_sys->lock );
659 return( VLC_EGENERIC );
662 i_param_size = sizeof( AudioDeviceID );
663 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
664 &i_param_size, (void *)&devid_def );
667 msg_Err( p_aout, "AudioHardwareGetProperty failed: [%4.4s]",
669 free( (void *)p_devices );
670 vlc_mutex_unlock( &p_sys->lock );
671 return( VLC_EGENERIC );
674 p_sys->p_devices = (struct aout_dev_t *)
675 malloc( sizeof( struct aout_dev_t ) * p_sys->i_devices );
676 if( p_sys->p_devices == NULL )
678 msg_Err( p_aout, "out of memory" );
679 free( (void *)p_devices );
680 vlc_mutex_unlock( &p_sys->lock );
681 return( VLC_ENOMEM );
684 p_sys->i_options = 0;
685 p_sys->p_options = NULL;
687 for( i = 0; i < p_sys->i_devices; i++ )
689 p_sys->p_devices[i].devid = p_devices[i];
691 if( p_devices[i] == devid_def )
693 p_sys->i_def_dev = i;
696 if( InitDeviceInfo( i, p_aout ) )
700 msg_Err( p_aout, "InitDeviceInfo(%ld) failed", i );
702 for( j = 0; j < i; j++ )
704 FreeDeviceInfo( j, p_aout );
707 free( (void *)p_sys->p_devices );
708 free( (void *)p_devices );
710 vlc_mutex_unlock( &p_sys->lock );
712 return( VLC_EGENERIC );
716 free( (void *)p_devices );
718 p_sys->b_hwinfo = VLC_TRUE;
720 vlc_mutex_unlock( &p_sys->lock );
722 return( VLC_SUCCESS );
725 /*****************************************************************************
727 *****************************************************************************/
728 static int InitDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
731 UInt32 i, i_param_size;
732 AudioBufferList * p_buffer_list;
734 struct aout_sys_t * p_sys = p_aout->output.p_sys;
735 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
737 /* Get length of device name */
738 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
739 kAudioDevicePropertyDeviceName,
740 &i_param_size, NULL );
743 msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
745 return( VLC_EGENERIC );
748 /* Allocate memory for device name */
749 p_dev->psz_device_name = (char *)malloc( i_param_size );
750 if( p_dev->psz_device_name == NULL )
752 msg_Err( p_aout, "out of memory" );
753 return( VLC_ENOMEM );
756 /* Get device name */
757 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
758 kAudioDevicePropertyDeviceName,
759 &i_param_size, p_dev->psz_device_name );
762 msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
764 free( (void *)p_dev->psz_device_name );
765 return( VLC_EGENERIC );
768 msg_Dbg( p_aout, "device [%ld] has name [%s]",
769 i_dev, p_dev->psz_device_name );
771 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
772 kAudioDevicePropertyStreamConfiguration,
773 &i_param_size, NULL );
776 msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
778 free( (void *)p_dev->psz_device_name );
779 return( VLC_EGENERIC );
782 p_buffer_list = (AudioBufferList *)malloc( i_param_size );
783 if( p_buffer_list == NULL )
785 msg_Err( p_aout, "out of memory" );
786 free( (void *)p_dev->psz_device_name );
787 return( VLC_ENOMEM );
790 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
791 kAudioDevicePropertyStreamConfiguration,
792 &i_param_size, p_buffer_list );
795 msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
797 free( (void *)p_dev->psz_device_name );
798 free( (void *)p_buffer_list );
799 return( VLC_EGENERIC );
802 p_dev->i_streams = p_buffer_list->mNumberBuffers;
803 free( (void *)p_buffer_list );
805 msg_Dbg( p_aout, "device [%ld] has [%ld] streams",
806 i_dev, p_dev->i_streams );
808 p_dev->pi_streams = (UInt32 *)malloc( p_dev->i_streams *
809 sizeof( *p_dev->pi_streams ) );
810 if( p_dev->pi_streams == NULL )
812 msg_Err( p_aout, "out of memory" );
813 free( (void *)p_dev->psz_device_name );
814 return( VLC_ENOMEM );
817 p_dev->pp_streams = (AudioStreamBasicDescription **)
818 malloc( p_dev->i_streams *
819 sizeof( *p_dev->pp_streams ) );
820 if( p_dev->pp_streams == NULL )
822 msg_Err( p_aout, "out of memory" );
823 free( (void *)p_dev->psz_device_name );
824 free( (void *)p_dev->pi_streams );
825 return( VLC_ENOMEM );
828 for( i = 0; i < p_dev->i_streams; i++ )
830 if( InitStreamInfo( i_dev, p_aout, i ) )
834 msg_Err( p_aout, "InitStreamInfo(%ld, %ld) failed", i_dev, i );
836 for( j = 0; j < i; j++ )
838 FreeStreamInfo( i_dev, p_aout, j );
841 free( (void *)p_dev->psz_device_name );
842 free( (void *)p_dev->pi_streams );
843 free( (void *)p_dev->pp_streams );
845 return( VLC_EGENERIC );
849 return( VLC_SUCCESS );
852 /*****************************************************************************
854 *****************************************************************************/
855 static void FreeDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
859 struct aout_sys_t * p_sys = p_aout->output.p_sys;
860 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
862 for( i = 0; i < p_dev->i_streams; i++ )
864 FreeStreamInfo( i_dev, p_aout, i );
867 free( (void *)p_dev->pp_streams );
868 free( (void *)p_dev->pi_streams );
869 free( (void *)p_dev->psz_device_name );
872 /*****************************************************************************
874 *****************************************************************************/
875 static void FreeHardwareInfo( aout_instance_t * p_aout )
879 struct aout_sys_t * p_sys = p_aout->output.p_sys;
881 vlc_mutex_lock( &p_sys->lock );
883 if( !p_sys->b_hwinfo )
885 vlc_mutex_unlock( &p_sys->lock );
889 for( i = 0; i < p_sys->i_devices; i++ )
891 FreeDeviceInfo( i, p_aout );
894 free( (void *)p_sys->p_options );
895 free( (void *)p_sys->p_devices );
897 p_sys->b_hwinfo = VLC_FALSE;
899 vlc_mutex_unlock( &p_sys->lock );
902 /*****************************************************************************
904 *****************************************************************************/
905 static int GetStreamID( AudioDeviceID devid, UInt32 i_idx,
906 AudioStreamID * p_sid )
910 AudioStreamID * p_stream_list;
912 err = AudioDeviceGetPropertyInfo( devid, 0, FALSE,
913 kAudioDevicePropertyStreams,
914 &i_param_size, NULL );
917 return( VLC_EGENERIC );
920 p_stream_list = (AudioStreamID *)malloc( i_param_size );
921 if( p_stream_list == NULL )
923 return( VLC_ENOMEM );
926 err = AudioDeviceGetProperty( devid, 0, FALSE,
927 kAudioDevicePropertyStreams,
928 &i_param_size, p_stream_list );
931 free( (void *)p_stream_list );
932 return( VLC_EGENERIC );
935 *p_sid = p_stream_list[i_idx - 1];
937 free( (void *)p_stream_list );
939 return( VLC_SUCCESS );
942 /*****************************************************************************
944 *****************************************************************************/
945 static int InitStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
950 UInt32 i, j, i_param_size;
952 struct aout_sys_t * p_sys = p_aout->output.p_sys;
953 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
955 if( GetStreamID( p_dev->devid, i_idx + 1, &i_sid ) )
957 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed", i_dev, i_idx );
958 return( VLC_EGENERIC );
961 err = AudioStreamGetPropertyInfo( i_sid, 0,
962 kAudioStreamPropertyPhysicalFormats,
963 &i_param_size, NULL );
966 msg_Err( p_aout, "AudioStreamGetPropertyInfo failed: [%4.4s]",
968 return( VLC_EGENERIC );
971 #define P_STREAMS p_dev->pp_streams[i_idx]
972 #define I_STREAMS p_dev->pi_streams[i_idx]
974 I_STREAMS = i_param_size / sizeof( AudioStreamBasicDescription );
976 P_STREAMS = (AudioStreamBasicDescription *)malloc( i_param_size );
977 if( P_STREAMS == NULL )
979 msg_Err( p_aout, "out of memory" );
980 return( VLC_ENOMEM );
983 memset( P_STREAMS, 0, i_param_size );
985 err = AudioStreamGetProperty( i_sid, 0,
986 kAudioStreamPropertyPhysicalFormats,
987 &i_param_size, P_STREAMS );
990 msg_Err( p_aout, "AudioStreamGetProperty failed: [%4.4s]",
992 free( (void *)P_STREAMS );
993 return( VLC_EGENERIC );
996 for( j = 0; j < N_AOUT_CLASSES; j++ )
998 vlc_bool_t b_found = 0;
1000 for( i = 0; i < I_STREAMS; i++ )
1004 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format",
1008 if( ( P_STREAMS[i].mFormatID == 'IAC3' ||
1009 P_STREAMS[i].mFormatID == kAudioFormat60958AC3 ) &&
1010 !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
1015 if( ( P_STREAMS[i].mFormatID != aout_classes[j].mFormatID ) ||
1016 ( P_STREAMS[i].mChannelsPerFrame !=
1017 aout_classes[j].mChannelsPerFrame ) )
1028 p_sys->p_options = (struct aout_option_t *)
1029 realloc( p_sys->p_options,
1030 ( p_sys->i_options + 1 ) *
1031 sizeof( struct aout_option_t ) );
1032 if( p_sys->p_options == NULL )
1034 msg_Err( p_aout, "out of memory" );
1035 free( (void *)P_STREAMS );
1036 return( VLC_ENOMEM );
1039 #define AOUT_OPTION p_sys->p_options[p_sys->i_options]
1041 snprintf( AOUT_OPTION.sz_option,
1042 sizeof( AOUT_OPTION.sz_option ) /
1043 sizeof( AOUT_OPTION.sz_option[0] ) - 1,
1046 p_dev->psz_device_name,
1047 aout_classes[j].psz_class );
1049 AOUT_OPTION.i_sid = i_sid;
1050 AOUT_OPTION.i_dev = i_dev;
1051 AOUT_OPTION.i_idx = i_idx;
1052 AOUT_OPTION.i_sdx = i;
1053 AOUT_OPTION.i_cdx = j;
1064 return( VLC_SUCCESS );
1067 /*****************************************************************************
1069 *****************************************************************************/
1070 static void FreeStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
1073 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1074 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
1076 free( (void *)p_dev->pp_streams[i_idx] );
1079 /*****************************************************************************
1081 *****************************************************************************/
1082 static int InitDevice( aout_instance_t * p_aout )
1086 unsigned int i_option;
1087 vlc_bool_t b_found = VLC_FALSE;
1088 UInt32 i, i_stream, i_param_size;
1089 AudioStreamBasicDescription desc;
1091 struct aout_dev_t * p_dev;
1092 struct aout_option_t * p_option;
1093 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1095 if( var_Get( p_aout, "audio-device", &val ) < 0 )
1097 msg_Err( p_aout, "audio-device var does not exist" );
1098 return( VLC_ENOVAR );
1101 if( !sscanf( val.psz_string, "%d:", &i_option ) ||
1102 p_sys->i_options <= i_option )
1107 free( (void *)val.psz_string );
1109 p_option = &p_sys->p_options[i_option];
1110 p_dev = &p_sys->p_devices[p_option->i_dev];
1112 msg_Dbg( p_aout, "getting device [%ld]", p_option->i_dev );
1114 i_param_size = sizeof( p_sys->b_dev_alive );
1115 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
1116 kAudioDevicePropertyDeviceIsAlive,
1117 &i_param_size, &p_sys->b_dev_alive );
1120 msg_Err( p_aout, "AudioDeviceGetProperty failed: %4.4s",
1122 return( VLC_EGENERIC );
1125 #define P_STREAMS p_dev->pp_streams[p_option->i_idx]
1126 #define I_STREAMS p_dev->pi_streams[p_option->i_idx]
1128 for( i = 0; i < I_STREAMS; i++ )
1130 if( P_STREAMS[i].mFormatID ==
1131 aout_classes[p_option->i_cdx].mFormatID &&
1132 P_STREAMS[i].mSampleRate == p_aout->output.output.i_rate )
1139 i_stream = b_found ? i : p_option->i_sdx;
1141 i_param_size = sizeof( desc );
1142 memset( &desc, 0, i_param_size );
1143 err = AudioStreamGetProperty( p_option->i_sid, 0,
1144 kAudioStreamPropertyPhysicalFormat,
1145 &i_param_size, (void *)&desc );
1148 msg_Err( p_aout, "AudioStreamGetPropertyInfo failed: [%4.4s]",
1150 return( VLC_EGENERIC );
1153 if( memcmp( &P_STREAMS[i_stream], &desc, sizeof( desc ) ) != 0 )
1155 struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
1157 vlc_cond_init( p_aout, &w.cond );
1158 vlc_mutex_init( p_aout, &w.lock );
1160 err = AudioStreamAddPropertyListener( p_option->i_sid, 0,
1161 kAudioStreamPropertyPhysicalFormat,
1162 StreamListener, (void *)&w );
1165 msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]",
1167 vlc_mutex_destroy( &w.lock );
1168 vlc_cond_destroy( &w.cond );
1169 return( VLC_EGENERIC );
1172 vlc_mutex_lock( &w.lock );
1174 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting format",
1175 P_STREAMS[i_stream] ) );
1177 err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
1178 kAudioStreamPropertyPhysicalFormat,
1179 sizeof( P_STREAMS[i_stream] ),
1180 &P_STREAMS[i_stream] );
1183 msg_Err( p_aout, "AudioStreamSetProperty failed: [%4.4s]",
1185 vlc_mutex_unlock( &w.lock );
1186 vlc_mutex_destroy( &w.lock );
1187 vlc_cond_destroy( &w.cond );
1188 return( VLC_EGENERIC );
1191 vlc_cond_wait( &w.cond, &w.lock );
1192 vlc_mutex_unlock( &w.lock );
1194 vlc_mutex_destroy( &w.lock );
1195 vlc_cond_destroy( &w.cond );
1197 if( GetStreamID( p_dev->devid, p_option->i_idx + 1,
1198 &p_option->i_sid ) )
1200 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed",
1201 p_option->i_dev, p_option->i_idx );
1202 return( VLC_EGENERIC );
1205 err = AudioStreamRemovePropertyListener( p_option->i_sid, 0,
1206 kAudioStreamPropertyPhysicalFormat, StreamListener );
1210 "AudioStreamRemovePropertyListener failed: [%4.4s]",
1213 return( VLC_EGENERIC );
1220 err = AudioDeviceAddPropertyListener( p_dev->devid, 0, FALSE,
1221 kAudioDevicePropertyDeviceIsAlive,
1222 DeviceListener, (void *)p_aout );
1225 msg_Err( p_aout, "AudioDeviceAddPropertyListener failed: [%4.4s]",
1227 return( VLC_EGENERIC );
1230 config_PutInt( p_aout, "macosx-adev", i_option );
1232 p_sys->i_sel_opt = i_option;
1233 p_sys->devid = p_dev->devid;
1235 return( VLC_SUCCESS );
1238 /*****************************************************************************
1240 *****************************************************************************/
1241 static void FreeDevice( aout_instance_t * p_aout )
1245 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1247 err = AudioDeviceRemovePropertyListener( p_sys->devid, 0, FALSE,
1248 kAudioDevicePropertyDeviceIsAlive,
1252 msg_Err( p_aout, "AudioDeviceRemovePropertyListener failed: [%4.4s]",
1257 /*****************************************************************************
1259 *****************************************************************************/
1260 static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
1261 void * inClientData )
1263 OSStatus err = noErr;
1265 aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1266 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1268 switch( inPropertyID )
1270 case kAudioHardwarePropertyDevices:
1276 if( p_sys->b_dev_alive )
1278 i_idx = p_sys->p_options[p_sys->i_sel_opt].i_idx;
1279 i_sdx = p_sys->p_options[p_sys->i_sel_opt].i_sdx;
1282 FreeHardwareInfo( p_aout );
1284 if( InitHardwareInfo( p_aout ) )
1286 msg_Err( p_aout, "InitHardwareInfo failed" );
1290 if( p_sys->b_dev_alive )
1294 for( i = 0; i < p_sys->i_options; i++ )
1296 if( p_sys->p_devices[p_sys->p_options[i].i_dev].devid ==
1297 p_sys->devid && p_sys->p_options[i].i_idx == i_idx &&
1298 p_sys->p_options[i].i_sdx == i_sdx )
1306 var_Destroy( p_aout, "audio-device" );
1307 InitDeviceVar( p_aout, i_option, !p_sys->b_dev_alive );
1315 /*****************************************************************************
1317 *****************************************************************************/
1318 static OSStatus DeviceListener( AudioDeviceID inDevice,
1321 AudioDevicePropertyID inPropertyID,
1322 void *inClientData )
1324 UInt32 i_param_size;
1325 OSStatus err = noErr;
1327 aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1328 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1330 switch( inPropertyID )
1332 case kAudioDevicePropertyDeviceIsAlive:
1334 i_param_size = sizeof( p_sys->b_dev_alive );
1335 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
1336 kAudioDevicePropertyDeviceIsAlive,
1337 &i_param_size, &p_sys->b_dev_alive );
1340 msg_Err( p_aout, "AudioDeviceGetProperty failed: %4.4s",
1350 /*****************************************************************************
1352 *****************************************************************************/
1353 static OSStatus StreamListener( AudioStreamID inStream,
1355 AudioDevicePropertyID inPropertyID,
1356 void * inClientData )
1358 OSStatus err = noErr;
1360 struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1362 switch( inPropertyID )
1364 case kAudioStreamPropertyPhysicalFormat:
1365 vlc_mutex_lock( &w->lock );
1366 vlc_cond_signal( &w->cond );
1367 vlc_mutex_unlock( &w->lock );
1377 /*****************************************************************************
1379 *****************************************************************************/
1380 static void InitDeviceVar( aout_instance_t * p_aout, int i_option,
1381 vlc_bool_t b_change )
1386 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1388 if( i_option == -1 || i_option >= (int)p_sys->i_options )
1390 for( i = 0; i < p_sys->i_options; i++ )
1392 if( p_sys->p_options[i].i_dev == p_sys->i_def_dev )
1400 var_Create( p_aout, "audio-device", VLC_VAR_STRING |
1401 VLC_VAR_HASCHOICE );
1403 for( i = 0; i < p_sys->i_options; i++ )
1405 val.psz_string = p_sys->p_options[i].sz_option;
1406 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
1408 if( !b_change && i == (UInt32)i_option )
1410 p_sys->i_sel_opt = i;
1411 var_Set( p_aout, "audio-device", val );
1412 config_PutInt( p_aout, "macosx-adev", i_option );
1416 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
1421 val.psz_string = p_sys->p_options[i_option].sz_option;
1422 var_Set( p_aout, "audio-device", val );
1425 val.b_bool = VLC_TRUE;
1426 var_Set( p_aout, "intf-change", val );