1 /*****************************************************************************
2 * coreaudio.c: CoreAudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2002-2004 VideoLAN
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 STREAM_FORMAT_MSG( pre, sfm ) \
41 pre ": [%ld][%4.4s][%ld][%ld][%ld][%ld][%ld][%ld]", \
42 (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
43 sfm.mFormatFlags, sfm.mBytesPerPacket, \
44 sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
45 sfm.mChannelsPerFrame, sfm.mBitsPerChannel
47 /*****************************************************************************
49 ****************************************************************************/
52 AudioDeviceClassA52 = 1 << 0,
53 AudioDeviceClassPCM = 1 << 1
56 static struct aout_class_t
59 UInt32 mChannelsPerFrame;
60 enum AudioDeviceClass class;
61 const char * psz_class;
65 { /* old A/52 format type */
72 { /* new A/52 format type */
80 kAudioFormatLinearPCM,
87 kAudioFormatLinearPCM,
94 kAudioFormatLinearPCM,
101 kAudioFormatLinearPCM,
108 kAudioFormatLinearPCM,
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 UInt32 i_stream_index;
159 AudioStreamBasicDescription stream_format;
162 vlc_bool_t b_revert_sfmt;
163 AudioStreamBasicDescription sfmt_revert;
165 UInt32 i_bufframe_size;
169 /*****************************************************************************
171 *****************************************************************************/
172 static int InitHardwareInfo ( aout_instance_t * p_aout );
173 static int InitDeviceInfo ( UInt32 i_dev, aout_instance_t * p_aout );
174 static void FreeDeviceInfo ( UInt32 i_dev, aout_instance_t * p_aout );
175 static void FreeHardwareInfo ( aout_instance_t * p_aout );
176 static int InitDevice ( aout_instance_t * p_aout );
177 static void FreeDevice ( aout_instance_t * p_aout );
178 static int GetStreamID ( AudioDeviceID devid, UInt32 i_idx,
179 AudioStreamID * p_sid );
180 static int InitStreamInfo ( UInt32 i_dev, aout_instance_t * p_aout,
182 static void FreeStreamInfo ( UInt32 i_dev, aout_instance_t * p_aout,
184 static void InitDeviceVar ( aout_instance_t * p_aout, int i_option,
185 vlc_bool_t b_change );
187 static int Open ( vlc_object_t * );
188 static void Close ( vlc_object_t * );
190 static void Play ( aout_instance_t * p_aout );
192 static OSStatus IOCallback ( AudioDeviceID inDevice,
193 const AudioTimeStamp * inNow,
194 const void * inInputData,
195 const AudioTimeStamp * inInputTime,
196 AudioBufferList * outOutputData,
197 const AudioTimeStamp * inOutputTime,
198 void * threadGlobals );
200 static OSStatus HardwareListener ( AudioHardwarePropertyID inPropertyID,
201 void * inClientData );
203 static OSStatus DeviceListener ( AudioDeviceID inDevice,
206 AudioDevicePropertyID inPropertyID,
207 void * inClientData );
209 static OSStatus StreamListener ( AudioStreamID inStream,
211 AudioDevicePropertyID inPropertyID,
212 void * inClientData );
214 /*****************************************************************************
216 *****************************************************************************/
217 #define ADEV_TEXT N_("Audio Device")
218 #define ADEV_LONGTEXT N_("Choose a number corresponding to the number of an " \
219 "audio device, as listed in your 'Audio Device' menu. This device will " \
220 "then be used by default for audio playback.")
223 set_description( _("CoreAudio output") );
224 set_capability( "audio output", 100 );
225 set_category( CAT_AUDIO );
226 set_subcategory( SUBCAT_AUDIO_AOUT );
227 set_callbacks( Open, Close );
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;
240 struct aout_option_t * p_option;
241 UInt32 i_startingChannel;
243 /* Allocate structure */
244 p_sys = (struct aout_sys_t *)malloc( sizeof( struct aout_sys_t ) );
247 msg_Err( p_aout, "out of memory" );
248 return( VLC_ENOMEM );
251 memset( p_sys, 0, sizeof( struct aout_sys_t ) );
253 p_aout->output.p_sys = p_sys;
254 p_aout->output.pf_play = Play;
256 vlc_mutex_init( p_aout, &p_sys->lock );
258 if( InitHardwareInfo( p_aout ) )
260 msg_Err( p_aout, "InitHardwareInfo failed" );
261 vlc_mutex_destroy( &p_sys->lock );
262 free( (void *)p_sys );
263 return( VLC_EGENERIC );
266 if( var_Type( p_aout, "audio-device" ) == 0 )
268 InitDeviceVar( p_aout, config_GetInt( p_aout, "coreaudio-dev" ),
272 if( InitDevice( p_aout ) )
274 msg_Err( p_aout, "InitDevice failed" );
275 FreeHardwareInfo( p_aout );
276 vlc_mutex_destroy( &p_sys->lock );
277 free( (void *)p_sys );
278 return( VLC_EGENERIC );
281 /* get starting channel for the selected stream */
282 p_option = &p_sys->p_options[p_sys->i_sel_opt];
284 i_param_size = sizeof( UInt32 );
285 err = AudioStreamGetProperty( p_option->i_sid, 0,
286 kAudioStreamPropertyStartingChannel,
287 &i_param_size, &i_startingChannel );
290 msg_Err( p_aout, "failed to get channel number: [%4.4s]",
292 FreeDevice( p_aout );
293 FreeHardwareInfo( p_aout );
294 vlc_mutex_destroy( &p_sys->lock );
295 free( (void *)p_sys );
296 return( VLC_EGENERIC );
299 msg_Dbg( p_aout, "starting channel: [%ld]", i_startingChannel );
301 /* Get a description of the stream format */
302 i_param_size = sizeof( AudioStreamBasicDescription );
303 err = AudioDeviceGetProperty( p_sys->devid, i_startingChannel, FALSE,
304 kAudioDevicePropertyStreamFormat,
305 &i_param_size, &p_sys->stream_format );
308 msg_Err( p_aout, "failed to get stream format: [%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 /* Set the output sample rate */
318 p_aout->output.output.i_rate =
319 (unsigned int)p_sys->stream_format.mSampleRate;
321 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "using format",
322 p_sys->stream_format ) );
324 /* Get the bufframe size */
325 i_param_size = sizeof( p_sys->i_bufframe_size );
326 err = AudioDeviceGetProperty( p_sys->devid, i_startingChannel, FALSE,
327 kAudioDevicePropertyBufferFrameSize,
328 &i_param_size, &p_sys->i_bufframe_size );
331 msg_Err( p_aout, "failed to get bufframe size: [%4.4s]",
333 FreeDevice( p_aout );
334 FreeHardwareInfo( p_aout );
335 vlc_mutex_destroy( &p_sys->lock );
336 free( (void *)p_sys );
337 return( VLC_EGENERIC );
340 msg_Dbg( p_aout, "device bufframe size: [%ld]", p_sys->i_bufframe_size );
341 msg_Dbg( p_aout, "device buffer index: [%ld]", p_sys->i_stream_index );
343 /* If we do AC3 over SPDIF, set buffer size to one AC3 frame */
344 if( ( p_sys->stream_format.mFormatID == kAudioFormat60958AC3 ||
345 p_sys->stream_format.mFormatID == 'IAC3' ) &&
346 p_sys->i_bufframe_size != A52_FRAME_NB )
348 p_sys->i_bufframe_size = A52_FRAME_NB;
349 i_param_size = sizeof( p_sys->i_bufframe_size );
350 err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
351 kAudioDevicePropertyBufferFrameSize,
352 i_param_size, &p_sys->i_bufframe_size );
355 msg_Err( p_aout, "failed to set bufframe size (%ld): [%4.4s]",
356 p_sys->i_bufframe_size, (char *)&err );
357 FreeDevice( p_aout );
358 FreeHardwareInfo( p_aout );
359 vlc_mutex_destroy( &p_sys->lock );
360 free( (void *)p_sys );
361 return( VLC_EGENERIC );
364 msg_Dbg( p_aout, "device bufframe size set to: [%ld]",
365 p_sys->i_bufframe_size );
368 switch( p_sys->stream_format.mFormatID )
370 case kAudioFormatLinearPCM:
371 p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
373 switch( p_sys->stream_format.mChannelsPerFrame )
376 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
380 p_aout->output.output.i_physical_channels =
381 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
385 p_aout->output.output.i_physical_channels =
386 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
387 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
391 p_aout->output.output.i_physical_channels =
392 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
393 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
394 AOUT_CHAN_CENTER | AOUT_CHAN_LFE;
398 p_aout->output.output.i_physical_channels =
399 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
400 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
401 AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
402 AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT;
406 msg_Err( p_aout, "unknown channel count: [%ld]",
407 p_sys->stream_format.mChannelsPerFrame );
408 FreeDevice( p_aout );
409 FreeHardwareInfo( p_aout );
410 vlc_mutex_destroy( &p_sys->lock );
411 free( (void *)p_sys );
412 return( VLC_EGENERIC );
415 p_aout->output.i_nb_samples = p_sys->i_bufframe_size;
417 aout_VolumeSoftInit( p_aout );
421 case kAudioFormat60958AC3:
422 p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
423 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
424 p_aout->output.output.i_frame_length = A52_FRAME_NB;
425 p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
427 aout_VolumeNoneInit( p_aout );
431 msg_Err( p_aout, "unknown hardware format: [%4.4s]",
432 (char *)&p_sys->stream_format.mFormatID );
433 FreeDevice( p_aout );
434 FreeHardwareInfo( p_aout );
435 vlc_mutex_destroy( &p_sys->lock );
436 free( (void *)p_sys );
437 return( VLC_EGENERIC );
441 err = AudioDeviceAddIOProc( p_sys->devid,
442 (AudioDeviceIOProc)IOCallback,
446 msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]",
448 FreeDevice( p_aout );
449 FreeHardwareInfo( p_aout );
450 vlc_mutex_destroy( &p_sys->lock );
451 free( (void *)p_sys );
452 return( VLC_EGENERIC );
456 err = AudioDeviceStart( p_sys->devid, (AudioDeviceIOProc)IOCallback );
459 msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]",
462 err = AudioDeviceRemoveIOProc( p_sys->devid,
463 (AudioDeviceIOProc)IOCallback );
466 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
470 FreeDevice( p_aout );
471 FreeHardwareInfo( p_aout );
472 vlc_mutex_destroy( &p_sys->lock );
473 free( (void *)p_sys );
475 return( VLC_EGENERIC );
478 err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
483 msg_Err( p_aout, "AudioHardwareAddPropertyListener failed: %4.4s",
487 err = AudioDeviceStop( p_sys->devid,
488 (AudioDeviceIOProc)IOCallback );
491 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
495 /* Remove callback */
496 err = AudioDeviceRemoveIOProc( p_sys->devid,
497 (AudioDeviceIOProc)IOCallback );
500 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
504 FreeDevice( p_aout );
505 FreeHardwareInfo( p_aout );
506 vlc_mutex_destroy( &p_sys->lock );
507 free( (void *)p_sys );
509 return( VLC_EGENERIC );
512 /* Let's pray for the following operation to be atomic... */
513 p_sys->clock_diff = - (mtime_t)
514 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
515 p_sys->clock_diff += mdate();
517 return( VLC_SUCCESS );
520 /*****************************************************************************
521 * Close: close the CoreAudio HAL device
522 *****************************************************************************/
523 static void Close( vlc_object_t * p_this )
526 aout_instance_t * p_aout = (aout_instance_t *)p_this;
527 struct aout_sys_t * p_sys = p_aout->output.p_sys;
529 if( p_sys->b_dev_alive )
532 err = AudioDeviceStop( p_sys->devid,
533 (AudioDeviceIOProc)IOCallback );
536 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
540 /* Remove callback */
541 err = AudioDeviceRemoveIOProc( p_sys->devid,
542 (AudioDeviceIOProc)IOCallback );
545 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
549 FreeDevice( p_aout );
552 err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
556 msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]",
560 FreeHardwareInfo( p_aout );
562 vlc_mutex_destroy( &p_sys->lock );
567 /*****************************************************************************
568 * Play: nothing to do
569 *****************************************************************************/
570 static void Play( aout_instance_t * p_aout )
574 /*****************************************************************************
575 * IOCallback: callback for audio output
576 *****************************************************************************/
577 static OSStatus IOCallback( AudioDeviceID inDevice,
578 const AudioTimeStamp * inNow,
579 const void * inInputData,
580 const AudioTimeStamp * inInputTime,
581 AudioBufferList * outOutputData,
582 const AudioTimeStamp * inOutputTime,
583 void * threadGlobals )
585 aout_buffer_t * p_buffer;
586 AudioTimeStamp host_time;
587 mtime_t current_date;
589 aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
590 struct aout_sys_t * p_sys = p_aout->output.p_sys;
592 host_time.mFlags = kAudioTimeStampHostTimeValid;
593 AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
596 p_sys->clock_diff = - (mtime_t)
597 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
598 p_sys->clock_diff += mdate();
601 current_date = p_sys->clock_diff +
602 AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
604 #define B_SPDI (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i'))
605 p_buffer = aout_OutputNextBuffer( p_aout, current_date, B_SPDI );
608 #define BUFFER outOutputData->mBuffers[ p_sys->i_stream_index ]
609 if( p_buffer != NULL )
611 /* move data into output data buffer */
612 p_aout->p_vlc->pf_memcpy( BUFFER.mData,
613 p_buffer->p_buffer, p_buffer->i_nb_bytes );
615 aout_BufferFree( p_buffer );
619 if( p_aout->output.output.i_format == VLC_FOURCC('f','l','3','2') )
621 UInt32 i, i_size = p_sys->i_bufframe_size *
622 p_sys->stream_format.mChannelsPerFrame;
623 float * p = (float *)BUFFER.mData;
625 for( i = 0; i < i_size; i++ )
632 p_aout->p_vlc->pf_memset( BUFFER.mData, 0, BUFFER.mDataByteSize );
640 /*****************************************************************************
642 *****************************************************************************/
643 static int InitHardwareInfo( aout_instance_t * p_aout )
646 UInt32 i, i_param_size;
647 AudioDeviceID devid_def;
648 AudioDeviceID * p_devices;
650 struct aout_sys_t * p_sys = p_aout->output.p_sys;
652 vlc_mutex_lock( &p_sys->lock );
654 /* Get number of devices */
655 err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
656 &i_param_size, NULL );
659 msg_Err( p_aout, "could not get number of devices: [%4.4s]",
661 vlc_mutex_unlock( &p_sys->lock );
662 return( VLC_EGENERIC );
665 p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
667 if( p_sys->i_devices < 1 )
669 msg_Err( p_aout, "no devices found" );
670 vlc_mutex_unlock( &p_sys->lock );
671 return( VLC_EGENERIC );
674 msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
676 /* Allocate DeviceID array */
677 p_devices = (AudioDeviceID *)malloc( i_param_size );
678 if( p_devices == NULL )
680 msg_Err( p_aout, "out of memory" );
681 vlc_mutex_unlock( &p_sys->lock );
682 return( VLC_ENOMEM );
685 /* Populate DeviceID array */
686 err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
687 &i_param_size, (void *)p_devices );
690 msg_Err( p_aout, "could not get the device ID's: [%4.4s]",
692 free( (void *)p_devices );
693 vlc_mutex_unlock( &p_sys->lock );
694 return( VLC_EGENERIC );
697 i_param_size = sizeof( AudioDeviceID );
698 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
699 &i_param_size, (void *)&devid_def );
702 msg_Err( p_aout, "could not get default audio device: [%4.4s]",
704 free( (void *)p_devices );
705 vlc_mutex_unlock( &p_sys->lock );
706 return( VLC_EGENERIC );
709 p_sys->p_devices = (struct aout_dev_t *)
710 malloc( sizeof( struct aout_dev_t ) * p_sys->i_devices );
711 if( p_sys->p_devices == NULL )
713 msg_Err( p_aout, "out of memory" );
714 free( (void *)p_devices );
715 vlc_mutex_unlock( &p_sys->lock );
716 return( VLC_ENOMEM );
719 p_sys->i_options = 0;
720 p_sys->p_options = NULL;
722 for( i = 0; i < p_sys->i_devices; i++ )
724 p_sys->p_devices[i].devid = p_devices[i];
726 if( p_devices[i] == devid_def )
728 p_sys->i_def_dev = i;
731 if( InitDeviceInfo( i, p_aout ) )
735 msg_Err( p_aout, "InitDeviceInfo(%ld) failed", i );
737 for( j = 0; j < i; j++ )
739 FreeDeviceInfo( j, p_aout );
742 free( (void *)p_sys->p_devices );
743 free( (void *)p_devices );
745 vlc_mutex_unlock( &p_sys->lock );
747 return( VLC_EGENERIC );
751 free( (void *)p_devices );
753 p_sys->b_hwinfo = VLC_TRUE;
755 vlc_mutex_unlock( &p_sys->lock );
757 return( VLC_SUCCESS );
760 /*****************************************************************************
762 *****************************************************************************/
763 static int InitDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
766 UInt32 i, i_param_size;
767 AudioBufferList * p_buffer_list;
769 struct aout_sys_t * p_sys = p_aout->output.p_sys;
770 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
772 /* Get length of device name */
773 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
774 kAudioDevicePropertyDeviceName,
775 &i_param_size, NULL );
778 msg_Err( p_aout, "could not get size of devicename: [%4.4s]",
780 return( VLC_EGENERIC );
783 /* Allocate memory for device name */
784 p_dev->psz_device_name = (char *)malloc( i_param_size );
785 if( p_dev->psz_device_name == NULL )
787 msg_Err( p_aout, "out of memory" );
788 return( VLC_ENOMEM );
791 /* Get device name */
792 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
793 kAudioDevicePropertyDeviceName,
794 &i_param_size, p_dev->psz_device_name );
797 msg_Err( p_aout, "could not get devicename: [%4.4s]",
799 free( (void *)p_dev->psz_device_name );
800 return( VLC_EGENERIC );
803 msg_Dbg( p_aout, "device [%ld] has name [%s]",
804 i_dev, p_dev->psz_device_name );
806 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
807 kAudioDevicePropertyStreamConfiguration,
808 &i_param_size, NULL );
811 msg_Err( p_aout, "could not get size of stream configuration: [%4.4s]",
813 free( (void *)p_dev->psz_device_name );
814 return( VLC_EGENERIC );
817 p_buffer_list = (AudioBufferList *)malloc( i_param_size );
818 if( p_buffer_list == NULL )
820 msg_Err( p_aout, "out of memory" );
821 free( (void *)p_dev->psz_device_name );
822 return( VLC_ENOMEM );
825 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
826 kAudioDevicePropertyStreamConfiguration,
827 &i_param_size, p_buffer_list );
830 msg_Err( p_aout, "could not get stream configuration: [%4.4s]",
832 free( (void *)p_dev->psz_device_name );
833 free( (void *)p_buffer_list );
834 return( VLC_EGENERIC );
837 p_dev->i_streams = p_buffer_list->mNumberBuffers;
838 free( (void *)p_buffer_list );
840 msg_Dbg( p_aout, "device [%ld] has [%ld] streams",
841 i_dev, p_dev->i_streams );
843 p_dev->pi_streams = (UInt32 *)malloc( p_dev->i_streams *
844 sizeof( *p_dev->pi_streams ) );
845 if( p_dev->pi_streams == NULL )
847 msg_Err( p_aout, "out of memory" );
848 free( (void *)p_dev->psz_device_name );
849 return( VLC_ENOMEM );
852 p_dev->pp_streams = (AudioStreamBasicDescription **)
853 malloc( p_dev->i_streams *
854 sizeof( *p_dev->pp_streams ) );
855 if( p_dev->pp_streams == NULL )
857 msg_Err( p_aout, "out of memory" );
858 free( (void *)p_dev->psz_device_name );
859 free( (void *)p_dev->pi_streams );
860 return( VLC_ENOMEM );
863 for( i = 0; i < p_dev->i_streams; i++ )
865 if( InitStreamInfo( i_dev, p_aout, i ) )
869 msg_Err( p_aout, "InitStreamInfo(%ld, %ld) failed", i_dev, i );
871 for( j = 0; j < i; j++ )
873 FreeStreamInfo( i_dev, p_aout, j );
876 free( (void *)p_dev->psz_device_name );
877 free( (void *)p_dev->pi_streams );
878 free( (void *)p_dev->pp_streams );
880 return( VLC_EGENERIC );
884 return( VLC_SUCCESS );
887 /*****************************************************************************
889 *****************************************************************************/
890 static void FreeDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
894 struct aout_sys_t * p_sys = p_aout->output.p_sys;
895 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
897 for( i = 0; i < p_dev->i_streams; i++ )
899 FreeStreamInfo( i_dev, p_aout, i );
902 free( (void *)p_dev->pp_streams );
903 free( (void *)p_dev->pi_streams );
904 free( (void *)p_dev->psz_device_name );
907 /*****************************************************************************
909 *****************************************************************************/
910 static void FreeHardwareInfo( aout_instance_t * p_aout )
914 struct aout_sys_t * p_sys = p_aout->output.p_sys;
916 vlc_mutex_lock( &p_sys->lock );
918 if( !p_sys->b_hwinfo )
920 vlc_mutex_unlock( &p_sys->lock );
924 for( i = 0; i < p_sys->i_devices; i++ )
926 FreeDeviceInfo( i, p_aout );
929 free( (void *)p_sys->p_options );
930 free( (void *)p_sys->p_devices );
932 p_sys->b_hwinfo = VLC_FALSE;
934 vlc_mutex_unlock( &p_sys->lock );
937 /*****************************************************************************
939 *****************************************************************************/
940 static int GetStreamID( AudioDeviceID devid, UInt32 i_idx,
941 AudioStreamID * p_sid )
945 AudioStreamID * p_stream_list;
947 err = AudioDeviceGetPropertyInfo( devid, 0, FALSE,
948 kAudioDevicePropertyStreams,
949 &i_param_size, NULL );
952 return( VLC_EGENERIC );
955 p_stream_list = (AudioStreamID *)malloc( i_param_size );
956 if( p_stream_list == NULL )
958 return( VLC_ENOMEM );
961 err = AudioDeviceGetProperty( devid, 0, FALSE,
962 kAudioDevicePropertyStreams,
963 &i_param_size, p_stream_list );
966 free( (void *)p_stream_list );
967 return( VLC_EGENERIC );
970 *p_sid = p_stream_list[i_idx - 1];
972 free( (void *)p_stream_list );
974 return( VLC_SUCCESS );
977 /*****************************************************************************
979 *****************************************************************************/
980 static int InitStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
985 UInt32 i, j, i_param_size;
987 struct aout_sys_t * p_sys = p_aout->output.p_sys;
988 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
990 if( GetStreamID( p_dev->devid, i_idx + 1, &i_sid ) )
992 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed", i_dev, i_idx );
993 return( VLC_EGENERIC );
996 err = AudioStreamGetPropertyInfo( i_sid, 0,
997 kAudioStreamPropertyPhysicalFormats,
998 &i_param_size, NULL );
1001 msg_Err( p_aout, "could not retrieve the number of streams: [%4.4s]",
1003 return( VLC_EGENERIC );
1006 #define P_STREAMS p_dev->pp_streams[i_idx]
1007 #define I_STREAMS p_dev->pi_streams[i_idx]
1009 I_STREAMS = i_param_size / sizeof( AudioStreamBasicDescription );
1011 P_STREAMS = (AudioStreamBasicDescription *)malloc( i_param_size );
1012 if( P_STREAMS == NULL )
1014 msg_Err( p_aout, "out of memory" );
1015 return( VLC_ENOMEM );
1018 memset( P_STREAMS, 0, i_param_size );
1020 err = AudioStreamGetProperty( i_sid, 0,
1021 kAudioStreamPropertyPhysicalFormats,
1022 &i_param_size, P_STREAMS );
1025 msg_Err( p_aout, "could no get the streams: [%4.4s]",
1027 free( (void *)P_STREAMS );
1028 return( VLC_EGENERIC );
1031 for( j = 0; j < N_AOUT_CLASSES; j++ )
1033 vlc_bool_t b_found = 0;
1035 for( i = 0; i < I_STREAMS; i++ )
1039 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format",
1043 if( ( P_STREAMS[i].mFormatID == 'IAC3' ||
1044 P_STREAMS[i].mFormatID == kAudioFormat60958AC3 ) &&
1045 !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
1050 if( ( P_STREAMS[i].mFormatID != aout_classes[j].mFormatID ) ||
1051 ( P_STREAMS[i].mChannelsPerFrame !=
1052 aout_classes[j].mChannelsPerFrame ) )
1063 p_sys->p_options = (struct aout_option_t *)
1064 realloc( p_sys->p_options,
1065 ( p_sys->i_options + 1 ) *
1066 sizeof( struct aout_option_t ) );
1067 if( p_sys->p_options == NULL )
1069 msg_Err( p_aout, "out of memory" );
1070 free( (void *)P_STREAMS );
1071 return( VLC_ENOMEM );
1074 #define AOUT_OPTION p_sys->p_options[p_sys->i_options]
1076 snprintf( AOUT_OPTION.sz_option,
1077 sizeof( AOUT_OPTION.sz_option ) /
1078 sizeof( AOUT_OPTION.sz_option[0] ) - 1,
1081 p_dev->psz_device_name,
1082 aout_classes[j].psz_class );
1084 AOUT_OPTION.i_sid = i_sid;
1085 AOUT_OPTION.i_dev = i_dev;
1086 AOUT_OPTION.i_idx = i_idx;
1087 AOUT_OPTION.i_sdx = i;
1088 AOUT_OPTION.i_cdx = j;
1099 return( VLC_SUCCESS );
1102 /*****************************************************************************
1104 *****************************************************************************/
1105 static void FreeStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
1108 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1109 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
1111 free( (void *)p_dev->pp_streams[i_idx] );
1114 /*****************************************************************************
1116 *****************************************************************************/
1117 static int InitDevice( aout_instance_t * p_aout )
1121 unsigned int i_option;
1122 vlc_bool_t b_found = VLC_FALSE;
1123 UInt32 i, i_stream, i_param_size, i_firstChannelNum;
1125 struct aout_dev_t * p_dev;
1126 struct aout_option_t * p_option;
1127 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1129 if( var_Get( p_aout, "audio-device", &val ) < 0 )
1131 msg_Err( p_aout, "audio-device var does not exist" );
1132 return( VLC_ENOVAR );
1135 i_option = val.i_int;
1136 p_option = &p_sys->p_options[i_option];
1137 p_dev = &p_sys->p_devices[p_option->i_dev];
1139 msg_Dbg( p_aout, "getting device [%ld]", p_option->i_dev );
1141 i_param_size = sizeof( p_sys->b_dev_alive );
1142 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
1143 kAudioDevicePropertyDeviceIsAlive,
1144 &i_param_size, &p_sys->b_dev_alive );
1147 msg_Err( p_aout, "could not check whether device is alive: %4.4s",
1149 return( VLC_EGENERIC );
1152 #define P_STREAMS p_dev->pp_streams[p_option->i_idx]
1153 #define I_STREAMS p_dev->pi_streams[p_option->i_idx]
1155 for( i = 0; i < I_STREAMS; i++ )
1157 if( P_STREAMS[i].mFormatID ==
1158 aout_classes[p_option->i_cdx].mFormatID &&
1159 P_STREAMS[i].mChannelsPerFrame ==
1160 aout_classes[p_option->i_cdx].mChannelsPerFrame &&
1161 P_STREAMS[i].mSampleRate == p_aout->output.output.i_rate )
1168 i_stream = b_found ? i : p_option->i_sdx;
1170 i_firstChannelNum = 0;
1171 for( i = 0; i < i_stream; i++ )
1173 i_firstChannelNum += P_STREAMS[i].mChannelsPerFrame;
1177 i_param_size = sizeof( p_sys->sfmt_revert );
1178 err = AudioStreamGetProperty( p_option->i_sid, 0,
1179 kAudioStreamPropertyPhysicalFormat,
1181 (void *)&p_sys->sfmt_revert );
1184 msg_Err( p_aout, "could not retrieve the original streamformat: [%4.4s]",
1186 return( VLC_EGENERIC );
1189 if( memcmp( &P_STREAMS[i_stream], &p_sys->sfmt_revert,
1190 sizeof( p_sys->sfmt_revert ) ) != 0 )
1193 struct timespec timeout;
1194 struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
1196 vlc_cond_init( p_aout, &w.cond );
1197 vlc_mutex_init( p_aout, &w.lock );
1199 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "stream format",
1200 p_sys->sfmt_revert ) );
1202 err = AudioStreamAddPropertyListener( p_option->i_sid, 0,
1203 kAudioStreamPropertyPhysicalFormat,
1204 StreamListener, (void *)&w );
1207 msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]",
1209 vlc_mutex_destroy( &w.lock );
1210 vlc_cond_destroy( &w.cond );
1211 return( VLC_EGENERIC );
1214 vlc_mutex_lock( &w.lock );
1216 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting format",
1217 P_STREAMS[i_stream] ) );
1219 err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
1220 kAudioStreamPropertyPhysicalFormat,
1221 sizeof( P_STREAMS[i_stream] ),
1222 &P_STREAMS[i_stream] );
1225 msg_Err( p_aout, "could not set the stream format: [%4.4s]",
1227 vlc_mutex_unlock( &w.lock );
1228 vlc_mutex_destroy( &w.lock );
1229 vlc_cond_destroy( &w.cond );
1230 return( VLC_EGENERIC );
1233 gettimeofday( &now, NULL );
1234 timeout.tv_sec = now.tv_sec;
1235 timeout.tv_nsec = (now.tv_usec + 100000) * 1000;
1237 pthread_cond_timedwait( &w.cond.cond, &w.lock.mutex, &timeout );
1238 vlc_mutex_unlock( &w.lock );
1240 if( GetStreamID( p_dev->devid, p_option->i_idx + 1,
1241 &p_option->i_sid ) )
1243 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed",
1244 p_option->i_dev, p_option->i_idx );
1245 vlc_mutex_destroy( &w.lock );
1246 vlc_cond_destroy( &w.cond );
1247 return( VLC_EGENERIC );
1250 err = AudioStreamRemovePropertyListener( p_option->i_sid, 0,
1251 kAudioStreamPropertyPhysicalFormat, StreamListener );
1255 "AudioStreamRemovePropertyListener failed: [%4.4s]",
1257 vlc_mutex_destroy( &w.lock );
1258 vlc_cond_destroy( &w.cond );
1259 return( VLC_EGENERIC );
1262 vlc_mutex_destroy( &w.lock );
1263 vlc_cond_destroy( &w.cond );
1265 p_sys->b_revert_sfmt = VLC_TRUE;
1269 err = AudioDeviceAddPropertyListener( p_dev->devid, 0, FALSE,
1270 kAudioDevicePropertyDeviceIsAlive,
1271 DeviceListener, (void *)p_aout );
1274 msg_Err( p_aout, "AudioDeviceAddPropertyListener failed: [%4.4s]",
1276 return( VLC_EGENERIC );
1279 config_PutInt( p_aout, "coreaudio-dev", i_option );
1281 p_sys->i_sel_opt = i_option;
1282 p_sys->devid = p_dev->devid;
1283 p_sys->i_stream_index = p_option->i_idx;
1287 return( VLC_SUCCESS );
1290 /*****************************************************************************
1292 *****************************************************************************/
1293 static void FreeDevice( aout_instance_t * p_aout )
1297 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1299 if( p_sys->b_revert_sfmt )
1301 struct aout_dev_t * p_dev;
1302 struct aout_option_t * p_option;
1304 p_option = &p_sys->p_options[p_sys->i_sel_opt];
1305 p_dev = &p_sys->p_devices[p_option->i_dev];
1307 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "reverting to format",
1308 p_sys->sfmt_revert ) );
1310 if( GetStreamID( p_dev->devid, p_option->i_idx + 1,
1311 &p_option->i_sid ) )
1313 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed",
1314 p_option->i_dev, p_option->i_idx );
1318 err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
1319 kAudioStreamPropertyPhysicalFormat,
1320 sizeof( p_sys->sfmt_revert ),
1321 &p_sys->sfmt_revert );
1324 msg_Err( p_aout, "AudioStreamSetProperty revert format failed: [%4.4s]",
1330 err = AudioDeviceRemovePropertyListener( p_sys->devid, 0, FALSE,
1331 kAudioDevicePropertyDeviceIsAlive,
1335 msg_Err( p_aout, "AudioDeviceRemovePropertyListener failed: [%4.4s]",
1340 /*****************************************************************************
1342 *****************************************************************************/
1343 static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
1344 void * inClientData )
1346 OSStatus err = noErr;
1348 aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1349 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1351 switch( inPropertyID )
1353 case kAudioHardwarePropertyDevices:
1359 if( p_sys->b_dev_alive )
1361 i_idx = p_sys->p_options[p_sys->i_sel_opt].i_idx;
1362 i_sdx = p_sys->p_options[p_sys->i_sel_opt].i_sdx;
1365 FreeHardwareInfo( p_aout );
1367 if( InitHardwareInfo( p_aout ) )
1369 msg_Err( p_aout, "InitHardwareInfo failed" );
1373 if( p_sys->b_dev_alive )
1377 for( i = 0; i < p_sys->i_options; i++ )
1379 if( p_sys->p_devices[p_sys->p_options[i].i_dev].devid ==
1380 p_sys->devid && p_sys->p_options[i].i_idx == i_idx &&
1381 p_sys->p_options[i].i_sdx == i_sdx )
1389 var_Destroy( p_aout, "audio-device" );
1390 InitDeviceVar( p_aout, i_option, !p_sys->b_dev_alive );
1398 /*****************************************************************************
1400 *****************************************************************************/
1401 static OSStatus DeviceListener( AudioDeviceID inDevice,
1404 AudioDevicePropertyID inPropertyID,
1405 void *inClientData )
1407 UInt32 i_param_size;
1408 OSStatus err = noErr;
1410 aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1411 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1413 switch( inPropertyID )
1415 case kAudioDevicePropertyDeviceIsAlive:
1417 i_param_size = sizeof( p_sys->b_dev_alive );
1418 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
1419 kAudioDevicePropertyDeviceIsAlive,
1420 &i_param_size, &p_sys->b_dev_alive );
1423 msg_Err( p_aout, "could not determine wether device is alive: %4.4s",
1433 /*****************************************************************************
1435 *****************************************************************************/
1436 static OSStatus StreamListener( AudioStreamID inStream,
1438 AudioDevicePropertyID inPropertyID,
1439 void * inClientData )
1441 OSStatus err = noErr;
1443 struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1445 switch( inPropertyID )
1447 case kAudioStreamPropertyPhysicalFormat:
1448 vlc_mutex_lock( &w->lock );
1449 vlc_cond_signal( &w->cond );
1450 vlc_mutex_unlock( &w->lock );
1460 /*****************************************************************************
1462 *****************************************************************************/
1463 static void InitDeviceVar( aout_instance_t * p_aout, int i_option,
1464 vlc_bool_t b_change )
1467 vlc_value_t val, text;
1469 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1471 if( i_option == -1 || i_option >= (int)p_sys->i_options )
1473 for( i = 0; i < p_sys->i_options; i++ )
1475 if( p_sys->p_options[i].i_dev == p_sys->i_def_dev )
1483 var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
1484 text.psz_string = ADEV_TEXT;
1485 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
1487 for( i = 0; i < p_sys->i_options; i++ )
1489 text.psz_string = p_sys->p_options[i].sz_option;
1491 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
1493 if( !b_change && i == (UInt32)i_option )
1495 p_sys->i_sel_opt = i;
1496 var_Set( p_aout, "audio-device", val );
1497 config_PutInt( p_aout, "coreaudio-dev", i_option );
1501 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
1506 val.i_int = i_option;
1507 var_Set( p_aout, "audio-device", val );
1510 val.b_bool = VLC_TRUE;
1511 var_Set( p_aout, "intf-change", val );