1 /*****************************************************************************
2 * coreaudio.c: CoreAudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2002-2003 VideoLAN
5 * $Id: coreaudio.c,v 1.9 2004/01/25 17:58:29 murray 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 <CoreAudio/CoreAudio.h>
40 #define A52_FRAME_NB 1536
42 #define STREAM_FORMAT_MSG( pre, sfm ) \
43 pre ": [%ld][%4.4s][%ld][%ld][%ld][%ld][%ld][%ld]", \
44 (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
45 sfm.mFormatFlags, sfm.mBytesPerPacket, \
46 sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
47 sfm.mChannelsPerFrame, sfm.mBitsPerChannel
49 /*****************************************************************************
51 ****************************************************************************/
54 AudioDeviceClassA52 = 1 << 0,
55 AudioDeviceClassPCM = 1 << 1
58 static struct aout_class_t
61 UInt32 mChannelsPerFrame;
62 enum AudioDeviceClass class;
63 const char * psz_class;
67 { /* old A/52 format type */
74 { /* new A/52 format type */
82 kAudioFormatLinearPCM,
89 kAudioFormatLinearPCM,
96 kAudioFormatLinearPCM,
103 kAudioFormatLinearPCM,
110 kAudioFormatLinearPCM,
117 #define N_AOUT_CLASSES (sizeof(aout_classes)/sizeof(aout_classes[0]))
119 /*****************************************************************************
121 ****************************************************************************/
130 /*****************************************************************************
132 ****************************************************************************/
136 char * psz_device_name;
139 AudioStreamBasicDescription ** pp_streams;
142 /*****************************************************************************
143 * aout_sys_t: private audio output method descriptor
144 *****************************************************************************
145 * This structure is part of the audio output thread descriptor.
146 * It describes the CoreAudio specific properties of an output thread.
147 *****************************************************************************/
154 struct aout_dev_t * p_devices;
157 struct aout_option_t * p_options;
160 AudioStreamBasicDescription stream_format;
163 vlc_bool_t b_revert_sfmt;
164 AudioStreamBasicDescription sfmt_revert;
166 UInt32 i_buffer_size;
170 /*****************************************************************************
172 *****************************************************************************/
173 static int InitHardwareInfo ( aout_instance_t * p_aout );
174 static int InitDeviceInfo ( UInt32 i_dev, aout_instance_t * p_aout );
175 static void FreeDeviceInfo ( UInt32 i_dev, aout_instance_t * p_aout );
176 static void FreeHardwareInfo ( aout_instance_t * p_aout );
177 static int InitDevice ( aout_instance_t * p_aout );
178 static void FreeDevice ( aout_instance_t * p_aout );
179 static int GetStreamID ( AudioDeviceID devid, UInt32 i_idx,
180 AudioStreamID * p_sid );
181 static int InitStreamInfo ( UInt32 i_dev, aout_instance_t * p_aout,
183 static void FreeStreamInfo ( UInt32 i_dev, aout_instance_t * p_aout,
185 static void InitDeviceVar ( aout_instance_t * p_aout, int i_option,
186 vlc_bool_t b_change );
188 static int Open ( vlc_object_t * );
189 static void Close ( vlc_object_t * );
191 static void Play ( aout_instance_t * p_aout );
193 static OSStatus IOCallback ( AudioDeviceID inDevice,
194 const AudioTimeStamp * inNow,
195 const void * inInputData,
196 const AudioTimeStamp * inInputTime,
197 AudioBufferList * outOutputData,
198 const AudioTimeStamp * inOutputTime,
199 void * threadGlobals );
201 static OSStatus HardwareListener ( AudioHardwarePropertyID inPropertyID,
202 void * inClientData );
204 static OSStatus DeviceListener ( AudioDeviceID inDevice,
207 AudioDevicePropertyID inPropertyID,
208 void * inClientData );
210 static OSStatus StreamListener ( AudioStreamID inStream,
212 AudioDevicePropertyID inPropertyID,
213 void * inClientData );
215 /*****************************************************************************
217 *****************************************************************************/
218 #define ADEV_TEXT N_("Audio Device")
219 #define ADEV_LONGTEXT N_("Choose a number corresponding to the number of an " \
220 "audio device, as listed in your 'Audio Device' menu. This device will " \
221 "then be used by default for audio playback.")
224 set_description( _("CoreAudio output") );
225 set_capability( "audio output", 100 );
226 set_callbacks( Open, Close );
227 add_category_hint( N_("Audio"), NULL, VLC_FALSE );
228 add_integer( "coreaudio-dev", -1, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE );
231 /*****************************************************************************
232 * Open: open a CoreAudio HAL device
233 *****************************************************************************/
234 static int Open( vlc_object_t * p_this )
238 struct aout_sys_t * p_sys;
239 aout_instance_t * p_aout = (aout_instance_t *)p_this;
241 /* Allocate structure */
242 p_sys = (struct aout_sys_t *)malloc( sizeof( struct aout_sys_t ) );
245 msg_Err( p_aout, "out of memory" );
246 return( VLC_ENOMEM );
249 memset( p_sys, 0, sizeof( struct aout_sys_t ) );
251 p_aout->output.p_sys = p_sys;
252 p_aout->output.pf_play = Play;
254 vlc_mutex_init( p_aout, &p_sys->lock );
256 if( InitHardwareInfo( p_aout ) )
258 msg_Err( p_aout, "InitHardwareInfo failed" );
259 vlc_mutex_destroy( &p_sys->lock );
260 free( (void *)p_sys );
261 return( VLC_EGENERIC );
264 if( var_Type( p_aout, "audio-device" ) == 0 )
266 InitDeviceVar( p_aout, config_GetInt( p_aout, "coreaudio-dev" ),
270 if( InitDevice( p_aout ) )
272 msg_Err( p_aout, "InitDevice failed" );
273 FreeHardwareInfo( p_aout );
274 vlc_mutex_destroy( &p_sys->lock );
275 free( (void *)p_sys );
276 return( VLC_EGENERIC );
279 /* Get a description of the stream format */
280 i_param_size = sizeof( AudioStreamBasicDescription );
281 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
282 kAudioDevicePropertyStreamFormat,
283 &i_param_size, &p_sys->stream_format );
286 msg_Err( p_aout, "failed to get stream format: [%4.4s]",
288 FreeDevice( p_aout );
289 FreeHardwareInfo( p_aout );
290 vlc_mutex_destroy( &p_sys->lock );
291 free( (void *)p_sys );
292 return( VLC_EGENERIC );
295 /* Set the output sample rate */
296 p_aout->output.output.i_rate =
297 (unsigned int)p_sys->stream_format.mSampleRate;
299 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "using format",
300 p_sys->stream_format ) );
302 /* Get the buffer size */
303 i_param_size = sizeof( p_sys->i_buffer_size );
304 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
305 kAudioDevicePropertyBufferSize,
306 &i_param_size, &p_sys->i_buffer_size );
309 msg_Err( p_aout, "failed to get buffer size: [%4.4s]",
311 FreeDevice( p_aout );
312 FreeHardwareInfo( p_aout );
313 vlc_mutex_destroy( &p_sys->lock );
314 free( (void *)p_sys );
315 return( VLC_EGENERIC );
318 msg_Dbg( p_aout, "device buffer size: [%ld]", p_sys->i_buffer_size );
320 /* If we do AC3 over SPDIF, set buffer size to one AC3 frame */
321 if( ( p_sys->stream_format.mFormatID == kAudioFormat60958AC3 ||
322 p_sys->stream_format.mFormatID == 'IAC3' ) &&
323 p_sys->i_buffer_size != AOUT_SPDIF_SIZE )
325 p_sys->i_buffer_size = AOUT_SPDIF_SIZE;
326 i_param_size = sizeof( p_sys->i_buffer_size );
327 err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
328 kAudioDevicePropertyBufferSize,
329 i_param_size, &p_sys->i_buffer_size );
332 msg_Err( p_aout, "failed to set buffer size: [%4.4s]",
334 FreeDevice( p_aout );
335 FreeHardwareInfo( p_aout );
336 vlc_mutex_destroy( &p_sys->lock );
337 free( (void *)p_sys );
338 return( VLC_EGENERIC );
341 msg_Dbg( p_aout, "device buffer size set to: [%ld]",
342 p_sys->i_buffer_size );
344 /* Set buffer frame size */
345 i_param_size = sizeof( p_aout->output.i_nb_samples );
346 err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
347 kAudioDevicePropertyBufferFrameSize,
349 &p_aout->output.i_nb_samples );
352 msg_Err( p_aout, "failed to set buffer frame size: [%4.4s]",
354 FreeDevice( p_aout );
355 FreeHardwareInfo( p_aout );
356 vlc_mutex_destroy( &p_sys->lock );
357 free( (void *)p_sys );
358 return( VLC_EGENERIC );
361 msg_Dbg( p_aout, "device buffer frame size set to: [%d]",
362 p_aout->output.i_nb_samples );
365 switch( p_sys->stream_format.mFormatID )
367 case kAudioFormatLinearPCM:
368 p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
370 switch( p_sys->stream_format.mChannelsPerFrame )
373 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
377 p_aout->output.output.i_physical_channels =
378 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
382 p_aout->output.output.i_physical_channels =
383 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
384 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
388 p_aout->output.output.i_physical_channels =
389 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
390 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
391 AOUT_CHAN_CENTER | AOUT_CHAN_LFE;
395 p_aout->output.output.i_physical_channels =
396 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
397 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
398 AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
399 AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT;
403 msg_Err( p_aout, "unknown channel count: [%ld]",
404 p_sys->stream_format.mChannelsPerFrame );
405 FreeDevice( p_aout );
406 FreeHardwareInfo( p_aout );
407 vlc_mutex_destroy( &p_sys->lock );
408 free( (void *)p_sys );
409 return( VLC_EGENERIC );
412 p_aout->output.i_nb_samples = (int)( p_sys->i_buffer_size /
413 p_sys->stream_format.mBytesPerFrame );
415 aout_VolumeSoftInit( p_aout );
419 case kAudioFormat60958AC3:
420 p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
421 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
422 p_aout->output.output.i_frame_length = A52_FRAME_NB;
423 p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
425 aout_VolumeNoneInit( p_aout );
429 msg_Err( p_aout, "unknown hardware format: [%4.4s]",
430 (char *)&p_sys->stream_format.mFormatID );
431 FreeDevice( p_aout );
432 FreeHardwareInfo( p_aout );
433 vlc_mutex_destroy( &p_sys->lock );
434 free( (void *)p_sys );
435 return( VLC_EGENERIC );
439 err = AudioDeviceAddIOProc( p_sys->devid,
440 (AudioDeviceIOProc)IOCallback,
444 msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]",
446 FreeDevice( p_aout );
447 FreeHardwareInfo( p_aout );
448 vlc_mutex_destroy( &p_sys->lock );
449 free( (void *)p_sys );
450 return( VLC_EGENERIC );
454 err = AudioDeviceStart( p_sys->devid, (AudioDeviceIOProc)IOCallback );
457 msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]",
460 err = AudioDeviceRemoveIOProc( p_sys->devid,
461 (AudioDeviceIOProc)IOCallback );
464 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
468 FreeDevice( p_aout );
469 FreeHardwareInfo( p_aout );
470 vlc_mutex_destroy( &p_sys->lock );
471 free( (void *)p_sys );
473 return( VLC_EGENERIC );
476 err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
481 msg_Err( p_aout, "AudioHardwareAddPropertyListener failed: %4.4s",
485 err = AudioDeviceStop( p_sys->devid,
486 (AudioDeviceIOProc)IOCallback );
489 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
493 /* Remove callback */
494 err = AudioDeviceRemoveIOProc( p_sys->devid,
495 (AudioDeviceIOProc)IOCallback );
498 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
502 FreeDevice( p_aout );
503 FreeHardwareInfo( p_aout );
504 vlc_mutex_destroy( &p_sys->lock );
505 free( (void *)p_sys );
507 return( VLC_EGENERIC );
510 /* Let's pray for the following operation to be atomic... */
511 p_sys->clock_diff = - (mtime_t)
512 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
513 p_sys->clock_diff += mdate();
515 return( VLC_SUCCESS );
518 /*****************************************************************************
519 * Close: close the CoreAudio HAL device
520 *****************************************************************************/
521 static void Close( vlc_object_t * p_this )
524 aout_instance_t * p_aout = (aout_instance_t *)p_this;
525 struct aout_sys_t * p_sys = p_aout->output.p_sys;
527 if( p_sys->b_dev_alive )
530 err = AudioDeviceStop( p_sys->devid,
531 (AudioDeviceIOProc)IOCallback );
534 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
538 /* Remove callback */
539 err = AudioDeviceRemoveIOProc( p_sys->devid,
540 (AudioDeviceIOProc)IOCallback );
543 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
547 FreeDevice( p_aout );
550 err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
554 msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]",
558 FreeHardwareInfo( p_aout );
560 vlc_mutex_destroy( &p_sys->lock );
565 /*****************************************************************************
566 * Play: nothing to do
567 *****************************************************************************/
568 static void Play( aout_instance_t * p_aout )
572 /*****************************************************************************
573 * IOCallback: callback for audio output
574 *****************************************************************************/
575 static OSStatus IOCallback( AudioDeviceID inDevice,
576 const AudioTimeStamp * inNow,
577 const void * inInputData,
578 const AudioTimeStamp * inInputTime,
579 AudioBufferList * outOutputData,
580 const AudioTimeStamp * inOutputTime,
581 void * threadGlobals )
583 aout_buffer_t * p_buffer;
584 AudioTimeStamp host_time;
585 mtime_t current_date;
587 aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
588 struct aout_sys_t * p_sys = p_aout->output.p_sys;
590 host_time.mFlags = kAudioTimeStampHostTimeValid;
591 AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
594 p_sys->clock_diff = - (mtime_t)
595 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
596 p_sys->clock_diff += mdate();
599 current_date = p_sys->clock_diff +
600 AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
602 #define B_SPDI (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i'))
603 p_buffer = aout_OutputNextBuffer( p_aout, current_date, B_SPDI );
606 if( p_buffer != NULL )
608 /* move data into output data buffer */
609 p_aout->p_vlc->pf_memcpy( outOutputData->mBuffers[ 0 ].mData,
611 p_sys->i_buffer_size );
613 aout_BufferFree( p_buffer );
617 if( p_aout->output.output.i_format == VLC_FOURCC('f','l','3','2') )
619 UInt32 i, i_size = p_sys->i_buffer_size / sizeof(float);
620 float * p = (float *)outOutputData->mBuffers[ 0 ].mData;
622 for( i = 0; i < i_size; i++ )
629 memset( outOutputData->mBuffers[ 0 ].mData,
630 0, p_sys->i_buffer_size );
637 /*****************************************************************************
639 *****************************************************************************/
640 static int InitHardwareInfo( aout_instance_t * p_aout )
643 UInt32 i, i_param_size;
644 AudioDeviceID devid_def;
645 AudioDeviceID * p_devices;
647 struct aout_sys_t * p_sys = p_aout->output.p_sys;
649 vlc_mutex_lock( &p_sys->lock );
651 /* Get number of devices */
652 err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
653 &i_param_size, NULL );
656 msg_Err( p_aout, "could not get number of devices: [%4.4s]",
658 vlc_mutex_unlock( &p_sys->lock );
659 return( VLC_EGENERIC );
662 p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
664 if( p_sys->i_devices < 1 )
666 msg_Err( p_aout, "no devices found" );
667 vlc_mutex_unlock( &p_sys->lock );
668 return( VLC_EGENERIC );
671 msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
673 /* Allocate DeviceID array */
674 p_devices = (AudioDeviceID *)malloc( i_param_size );
675 if( p_devices == NULL )
677 msg_Err( p_aout, "out of memory" );
678 vlc_mutex_unlock( &p_sys->lock );
679 return( VLC_ENOMEM );
682 /* Populate DeviceID array */
683 err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
684 &i_param_size, (void *)p_devices );
687 msg_Err( p_aout, "could not get the device ID's: [%4.4s]",
689 free( (void *)p_devices );
690 vlc_mutex_unlock( &p_sys->lock );
691 return( VLC_EGENERIC );
694 i_param_size = sizeof( AudioDeviceID );
695 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
696 &i_param_size, (void *)&devid_def );
699 msg_Err( p_aout, "could not get default audio device: [%4.4s]",
701 free( (void *)p_devices );
702 vlc_mutex_unlock( &p_sys->lock );
703 return( VLC_EGENERIC );
706 p_sys->p_devices = (struct aout_dev_t *)
707 malloc( sizeof( struct aout_dev_t ) * p_sys->i_devices );
708 if( p_sys->p_devices == NULL )
710 msg_Err( p_aout, "out of memory" );
711 free( (void *)p_devices );
712 vlc_mutex_unlock( &p_sys->lock );
713 return( VLC_ENOMEM );
716 p_sys->i_options = 0;
717 p_sys->p_options = NULL;
719 for( i = 0; i < p_sys->i_devices; i++ )
721 p_sys->p_devices[i].devid = p_devices[i];
723 if( p_devices[i] == devid_def )
725 p_sys->i_def_dev = i;
728 if( InitDeviceInfo( i, p_aout ) )
732 msg_Err( p_aout, "InitDeviceInfo(%ld) failed", i );
734 for( j = 0; j < i; j++ )
736 FreeDeviceInfo( j, p_aout );
739 free( (void *)p_sys->p_devices );
740 free( (void *)p_devices );
742 vlc_mutex_unlock( &p_sys->lock );
744 return( VLC_EGENERIC );
748 free( (void *)p_devices );
750 p_sys->b_hwinfo = VLC_TRUE;
752 vlc_mutex_unlock( &p_sys->lock );
754 return( VLC_SUCCESS );
757 /*****************************************************************************
759 *****************************************************************************/
760 static int InitDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
763 UInt32 i, i_param_size;
764 AudioBufferList * p_buffer_list;
766 struct aout_sys_t * p_sys = p_aout->output.p_sys;
767 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
769 /* Get length of device name */
770 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
771 kAudioDevicePropertyDeviceName,
772 &i_param_size, NULL );
775 msg_Err( p_aout, "could not get size of devicename: [%4.4s]",
777 return( VLC_EGENERIC );
780 /* Allocate memory for device name */
781 p_dev->psz_device_name = (char *)malloc( i_param_size );
782 if( p_dev->psz_device_name == NULL )
784 msg_Err( p_aout, "out of memory" );
785 return( VLC_ENOMEM );
788 /* Get device name */
789 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
790 kAudioDevicePropertyDeviceName,
791 &i_param_size, p_dev->psz_device_name );
794 msg_Err( p_aout, "could not get devicename: [%4.4s]",
796 free( (void *)p_dev->psz_device_name );
797 return( VLC_EGENERIC );
800 msg_Dbg( p_aout, "device [%ld] has name [%s]",
801 i_dev, p_dev->psz_device_name );
803 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
804 kAudioDevicePropertyStreamConfiguration,
805 &i_param_size, NULL );
808 msg_Err( p_aout, "could not get size of stream configuration: [%4.4s]",
810 free( (void *)p_dev->psz_device_name );
811 return( VLC_EGENERIC );
814 p_buffer_list = (AudioBufferList *)malloc( i_param_size );
815 if( p_buffer_list == NULL )
817 msg_Err( p_aout, "out of memory" );
818 free( (void *)p_dev->psz_device_name );
819 return( VLC_ENOMEM );
822 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
823 kAudioDevicePropertyStreamConfiguration,
824 &i_param_size, p_buffer_list );
827 msg_Err( p_aout, "could not get stream configuration: [%4.4s]",
829 free( (void *)p_dev->psz_device_name );
830 free( (void *)p_buffer_list );
831 return( VLC_EGENERIC );
834 p_dev->i_streams = p_buffer_list->mNumberBuffers;
835 free( (void *)p_buffer_list );
837 msg_Dbg( p_aout, "device [%ld] has [%ld] streams",
838 i_dev, p_dev->i_streams );
840 p_dev->pi_streams = (UInt32 *)malloc( p_dev->i_streams *
841 sizeof( *p_dev->pi_streams ) );
842 if( p_dev->pi_streams == NULL )
844 msg_Err( p_aout, "out of memory" );
845 free( (void *)p_dev->psz_device_name );
846 return( VLC_ENOMEM );
849 p_dev->pp_streams = (AudioStreamBasicDescription **)
850 malloc( p_dev->i_streams *
851 sizeof( *p_dev->pp_streams ) );
852 if( p_dev->pp_streams == NULL )
854 msg_Err( p_aout, "out of memory" );
855 free( (void *)p_dev->psz_device_name );
856 free( (void *)p_dev->pi_streams );
857 return( VLC_ENOMEM );
860 for( i = 0; i < p_dev->i_streams; i++ )
862 if( InitStreamInfo( i_dev, p_aout, i ) )
866 msg_Err( p_aout, "InitStreamInfo(%ld, %ld) failed", i_dev, i );
868 for( j = 0; j < i; j++ )
870 FreeStreamInfo( i_dev, p_aout, j );
873 free( (void *)p_dev->psz_device_name );
874 free( (void *)p_dev->pi_streams );
875 free( (void *)p_dev->pp_streams );
877 return( VLC_EGENERIC );
881 return( VLC_SUCCESS );
884 /*****************************************************************************
886 *****************************************************************************/
887 static void FreeDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
891 struct aout_sys_t * p_sys = p_aout->output.p_sys;
892 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
894 for( i = 0; i < p_dev->i_streams; i++ )
896 FreeStreamInfo( i_dev, p_aout, i );
899 free( (void *)p_dev->pp_streams );
900 free( (void *)p_dev->pi_streams );
901 free( (void *)p_dev->psz_device_name );
904 /*****************************************************************************
906 *****************************************************************************/
907 static void FreeHardwareInfo( aout_instance_t * p_aout )
911 struct aout_sys_t * p_sys = p_aout->output.p_sys;
913 vlc_mutex_lock( &p_sys->lock );
915 if( !p_sys->b_hwinfo )
917 vlc_mutex_unlock( &p_sys->lock );
921 for( i = 0; i < p_sys->i_devices; i++ )
923 FreeDeviceInfo( i, p_aout );
926 free( (void *)p_sys->p_options );
927 free( (void *)p_sys->p_devices );
929 p_sys->b_hwinfo = VLC_FALSE;
931 vlc_mutex_unlock( &p_sys->lock );
934 /*****************************************************************************
936 *****************************************************************************/
937 static int GetStreamID( AudioDeviceID devid, UInt32 i_idx,
938 AudioStreamID * p_sid )
942 AudioStreamID * p_stream_list;
944 err = AudioDeviceGetPropertyInfo( devid, 0, FALSE,
945 kAudioDevicePropertyStreams,
946 &i_param_size, NULL );
949 return( VLC_EGENERIC );
952 p_stream_list = (AudioStreamID *)malloc( i_param_size );
953 if( p_stream_list == NULL )
955 return( VLC_ENOMEM );
958 err = AudioDeviceGetProperty( devid, 0, FALSE,
959 kAudioDevicePropertyStreams,
960 &i_param_size, p_stream_list );
963 free( (void *)p_stream_list );
964 return( VLC_EGENERIC );
967 *p_sid = p_stream_list[i_idx - 1];
969 free( (void *)p_stream_list );
971 return( VLC_SUCCESS );
974 /*****************************************************************************
976 *****************************************************************************/
977 static int InitStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
982 UInt32 i, j, i_param_size;
984 struct aout_sys_t * p_sys = p_aout->output.p_sys;
985 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
987 if( GetStreamID( p_dev->devid, i_idx + 1, &i_sid ) )
989 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed", i_dev, i_idx );
990 return( VLC_EGENERIC );
993 err = AudioStreamGetPropertyInfo( i_sid, 0,
994 kAudioStreamPropertyPhysicalFormats,
995 &i_param_size, NULL );
998 msg_Err( p_aout, "could not retrieve the number of streams: [%4.4s]",
1000 return( VLC_EGENERIC );
1003 #define P_STREAMS p_dev->pp_streams[i_idx]
1004 #define I_STREAMS p_dev->pi_streams[i_idx]
1006 I_STREAMS = i_param_size / sizeof( AudioStreamBasicDescription );
1008 P_STREAMS = (AudioStreamBasicDescription *)malloc( i_param_size );
1009 if( P_STREAMS == NULL )
1011 msg_Err( p_aout, "out of memory" );
1012 return( VLC_ENOMEM );
1015 memset( P_STREAMS, 0, i_param_size );
1017 err = AudioStreamGetProperty( i_sid, 0,
1018 kAudioStreamPropertyPhysicalFormats,
1019 &i_param_size, P_STREAMS );
1022 msg_Err( p_aout, "could no get the streams: [%4.4s]",
1024 free( (void *)P_STREAMS );
1025 return( VLC_EGENERIC );
1028 for( j = 0; j < N_AOUT_CLASSES; j++ )
1030 vlc_bool_t b_found = 0;
1032 for( i = 0; i < I_STREAMS; i++ )
1036 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format",
1040 if( ( P_STREAMS[i].mFormatID == 'IAC3' ||
1041 P_STREAMS[i].mFormatID == kAudioFormat60958AC3 ) &&
1042 !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
1047 if( ( P_STREAMS[i].mFormatID != aout_classes[j].mFormatID ) ||
1048 ( P_STREAMS[i].mChannelsPerFrame !=
1049 aout_classes[j].mChannelsPerFrame ) )
1060 p_sys->p_options = (struct aout_option_t *)
1061 realloc( p_sys->p_options,
1062 ( p_sys->i_options + 1 ) *
1063 sizeof( struct aout_option_t ) );
1064 if( p_sys->p_options == NULL )
1066 msg_Err( p_aout, "out of memory" );
1067 free( (void *)P_STREAMS );
1068 return( VLC_ENOMEM );
1071 #define AOUT_OPTION p_sys->p_options[p_sys->i_options]
1073 snprintf( AOUT_OPTION.sz_option,
1074 sizeof( AOUT_OPTION.sz_option ) /
1075 sizeof( AOUT_OPTION.sz_option[0] ) - 1,
1078 p_dev->psz_device_name,
1079 aout_classes[j].psz_class );
1081 AOUT_OPTION.i_sid = i_sid;
1082 AOUT_OPTION.i_dev = i_dev;
1083 AOUT_OPTION.i_idx = i_idx;
1084 AOUT_OPTION.i_sdx = i;
1085 AOUT_OPTION.i_cdx = j;
1096 return( VLC_SUCCESS );
1099 /*****************************************************************************
1101 *****************************************************************************/
1102 static void FreeStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
1105 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1106 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
1108 free( (void *)p_dev->pp_streams[i_idx] );
1111 /*****************************************************************************
1113 *****************************************************************************/
1114 static int InitDevice( aout_instance_t * p_aout )
1118 unsigned int i_option;
1119 vlc_bool_t b_found = VLC_FALSE;
1120 UInt32 i, i_stream, i_param_size;
1122 struct aout_dev_t * p_dev;
1123 struct aout_option_t * p_option;
1124 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1126 if( var_Get( p_aout, "audio-device", &val ) < 0 )
1128 msg_Err( p_aout, "audio-device var does not exist" );
1129 return( VLC_ENOVAR );
1132 i_option = val.i_int;
1133 p_option = &p_sys->p_options[i_option];
1134 p_dev = &p_sys->p_devices[p_option->i_dev];
1136 msg_Dbg( p_aout, "getting device [%ld]", p_option->i_dev );
1138 i_param_size = sizeof( p_sys->b_dev_alive );
1139 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
1140 kAudioDevicePropertyDeviceIsAlive,
1141 &i_param_size, &p_sys->b_dev_alive );
1144 msg_Err( p_aout, "could not check whether device is alive: %4.4s",
1146 return( VLC_EGENERIC );
1149 #define P_STREAMS p_dev->pp_streams[p_option->i_idx]
1150 #define I_STREAMS p_dev->pi_streams[p_option->i_idx]
1152 for( i = 0; i < I_STREAMS; i++ )
1154 if( P_STREAMS[i].mFormatID ==
1155 aout_classes[p_option->i_cdx].mFormatID &&
1156 P_STREAMS[i].mChannelsPerFrame ==
1157 aout_classes[p_option->i_cdx].mChannelsPerFrame &&
1158 P_STREAMS[i].mSampleRate == p_aout->output.output.i_rate )
1165 i_stream = b_found ? i : p_option->i_sdx;
1167 i_param_size = sizeof( p_sys->sfmt_revert );
1168 err = AudioStreamGetProperty( p_option->i_sid, 0,
1169 kAudioStreamPropertyPhysicalFormat,
1171 (void *)&p_sys->sfmt_revert );
1174 msg_Err( p_aout, "could not retrieve the original streamformat: [%4.4s]",
1176 return( VLC_EGENERIC );
1179 if( memcmp( &P_STREAMS[i_stream], &p_sys->sfmt_revert,
1180 sizeof( p_sys->sfmt_revert ) ) != 0 )
1183 struct timespec timeout;
1184 struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
1186 vlc_cond_init( p_aout, &w.cond );
1187 vlc_mutex_init( p_aout, &w.lock );
1189 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "stream format",
1190 p_sys->sfmt_revert ) );
1192 err = AudioStreamAddPropertyListener( p_option->i_sid, 0,
1193 kAudioStreamPropertyPhysicalFormat,
1194 StreamListener, (void *)&w );
1197 msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]",
1199 vlc_mutex_destroy( &w.lock );
1200 vlc_cond_destroy( &w.cond );
1201 return( VLC_EGENERIC );
1204 vlc_mutex_lock( &w.lock );
1206 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting format",
1207 P_STREAMS[i_stream] ) );
1209 err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
1210 kAudioStreamPropertyPhysicalFormat,
1211 sizeof( P_STREAMS[i_stream] ),
1212 &P_STREAMS[i_stream] );
1215 msg_Err( p_aout, "could not set the stream format: [%4.4s]",
1217 vlc_mutex_unlock( &w.lock );
1218 vlc_mutex_destroy( &w.lock );
1219 vlc_cond_destroy( &w.cond );
1220 return( VLC_EGENERIC );
1223 gettimeofday( &now, NULL );
1224 timeout.tv_sec = now.tv_sec;
1225 timeout.tv_nsec = (now.tv_usec + 100000) * 1000;
1227 pthread_cond_timedwait( &w.cond.cond, &w.lock.mutex, &timeout );
1228 vlc_mutex_unlock( &w.lock );
1230 if( GetStreamID( p_dev->devid, p_option->i_idx + 1,
1231 &p_option->i_sid ) )
1233 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed",
1234 p_option->i_dev, p_option->i_idx );
1235 vlc_mutex_destroy( &w.lock );
1236 vlc_cond_destroy( &w.cond );
1237 return( VLC_EGENERIC );
1240 err = AudioStreamRemovePropertyListener( p_option->i_sid, 0,
1241 kAudioStreamPropertyPhysicalFormat, StreamListener );
1245 "AudioStreamRemovePropertyListener failed: [%4.4s]",
1247 vlc_mutex_destroy( &w.lock );
1248 vlc_cond_destroy( &w.cond );
1249 return( VLC_EGENERIC );
1252 vlc_mutex_destroy( &w.lock );
1253 vlc_cond_destroy( &w.cond );
1255 p_sys->b_revert_sfmt = VLC_TRUE;
1261 err = AudioDeviceAddPropertyListener( p_dev->devid, 0, FALSE,
1262 kAudioDevicePropertyDeviceIsAlive,
1263 DeviceListener, (void *)p_aout );
1266 msg_Err( p_aout, "AudioDeviceAddPropertyListener failed: [%4.4s]",
1268 return( VLC_EGENERIC );
1271 config_PutInt( p_aout, "coreaudio-dev", i_option );
1273 p_sys->i_sel_opt = i_option;
1274 p_sys->devid = p_dev->devid;
1276 return( VLC_SUCCESS );
1279 /*****************************************************************************
1281 *****************************************************************************/
1282 static void FreeDevice( aout_instance_t * p_aout )
1286 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1288 if( p_sys->b_revert_sfmt )
1290 struct aout_dev_t * p_dev;
1291 struct aout_option_t * p_option;
1293 p_option = &p_sys->p_options[p_sys->i_sel_opt];
1294 p_dev = &p_sys->p_devices[p_option->i_dev];
1296 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "reverting to format",
1297 p_sys->sfmt_revert ) );
1299 if( GetStreamID( p_dev->devid, p_option->i_idx + 1,
1300 &p_option->i_sid ) )
1302 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed",
1303 p_option->i_dev, p_option->i_idx );
1307 err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
1308 kAudioStreamPropertyPhysicalFormat,
1309 sizeof( p_sys->sfmt_revert ),
1310 &p_sys->sfmt_revert );
1313 msg_Err( p_aout, "AudioStreamSetProperty revert format failed: [%4.4s]",
1319 err = AudioDeviceRemovePropertyListener( p_sys->devid, 0, FALSE,
1320 kAudioDevicePropertyDeviceIsAlive,
1324 msg_Err( p_aout, "AudioDeviceRemovePropertyListener failed: [%4.4s]",
1329 /*****************************************************************************
1331 *****************************************************************************/
1332 static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
1333 void * inClientData )
1335 OSStatus err = noErr;
1337 aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1338 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1340 switch( inPropertyID )
1342 case kAudioHardwarePropertyDevices:
1348 if( p_sys->b_dev_alive )
1350 i_idx = p_sys->p_options[p_sys->i_sel_opt].i_idx;
1351 i_sdx = p_sys->p_options[p_sys->i_sel_opt].i_sdx;
1354 FreeHardwareInfo( p_aout );
1356 if( InitHardwareInfo( p_aout ) )
1358 msg_Err( p_aout, "InitHardwareInfo failed" );
1362 if( p_sys->b_dev_alive )
1366 for( i = 0; i < p_sys->i_options; i++ )
1368 if( p_sys->p_devices[p_sys->p_options[i].i_dev].devid ==
1369 p_sys->devid && p_sys->p_options[i].i_idx == i_idx &&
1370 p_sys->p_options[i].i_sdx == i_sdx )
1378 var_Destroy( p_aout, "audio-device" );
1379 InitDeviceVar( p_aout, i_option, !p_sys->b_dev_alive );
1387 /*****************************************************************************
1389 *****************************************************************************/
1390 static OSStatus DeviceListener( AudioDeviceID inDevice,
1393 AudioDevicePropertyID inPropertyID,
1394 void *inClientData )
1396 UInt32 i_param_size;
1397 OSStatus err = noErr;
1399 aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1400 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1402 switch( inPropertyID )
1404 case kAudioDevicePropertyDeviceIsAlive:
1406 i_param_size = sizeof( p_sys->b_dev_alive );
1407 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
1408 kAudioDevicePropertyDeviceIsAlive,
1409 &i_param_size, &p_sys->b_dev_alive );
1412 msg_Err( p_aout, "could not determine wether device is alive: %4.4s",
1422 /*****************************************************************************
1424 *****************************************************************************/
1425 static OSStatus StreamListener( AudioStreamID inStream,
1427 AudioDevicePropertyID inPropertyID,
1428 void * inClientData )
1430 OSStatus err = noErr;
1432 struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1434 switch( inPropertyID )
1436 case kAudioStreamPropertyPhysicalFormat:
1437 vlc_mutex_lock( &w->lock );
1438 vlc_cond_signal( &w->cond );
1439 vlc_mutex_unlock( &w->lock );
1449 /*****************************************************************************
1451 *****************************************************************************/
1452 static void InitDeviceVar( aout_instance_t * p_aout, int i_option,
1453 vlc_bool_t b_change )
1456 vlc_value_t val, text;
1458 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1460 if( i_option == -1 || i_option >= (int)p_sys->i_options )
1462 for( i = 0; i < p_sys->i_options; i++ )
1464 if( p_sys->p_options[i].i_dev == p_sys->i_def_dev )
1472 var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
1473 text.psz_string = ADEV_TEXT;
1474 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
1476 for( i = 0; i < p_sys->i_options; i++ )
1478 text.psz_string = p_sys->p_options[i].sz_option;
1480 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
1482 if( !b_change && i == (UInt32)i_option )
1484 p_sys->i_sel_opt = i;
1485 var_Set( p_aout, "audio-device", val );
1486 config_PutInt( p_aout, "coreaudio-dev", i_option );
1490 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
1495 val.i_int = i_option;
1496 var_Set( p_aout, "audio-device", val );
1499 val.b_bool = VLC_TRUE;
1500 var_Set( p_aout, "intf-change", val );