1 /*****************************************************************************
2 * coreaudio.c: CoreAudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2002-2003 VideoLAN
5 * $Id: coreaudio.c,v 1.4 2003/05/11 01:00:26 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 <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")
221 set_description( _("CoreAudio output") );
222 set_capability( "audio output", 100 );
223 set_callbacks( Open, Close );
224 add_category_hint( N_("Audio"), NULL, VLC_FALSE );
225 add_integer( "coreaudio-dev", -1, NULL, ADEV_TEXT, ADEV_TEXT, VLC_FALSE );
228 /*****************************************************************************
229 * Open: open a CoreAudio HAL device
230 *****************************************************************************/
231 static int Open( vlc_object_t * p_this )
235 struct aout_sys_t * p_sys;
236 aout_instance_t * p_aout = (aout_instance_t *)p_this;
238 /* Allocate structure */
239 p_sys = (struct aout_sys_t *)malloc( sizeof( struct aout_sys_t ) );
242 msg_Err( p_aout, "out of memory" );
243 return( VLC_ENOMEM );
246 memset( p_sys, 0, sizeof( struct aout_sys_t ) );
248 p_aout->output.p_sys = p_sys;
249 p_aout->output.pf_play = Play;
251 vlc_mutex_init( p_aout, &p_sys->lock );
253 if( InitHardwareInfo( p_aout ) )
255 msg_Err( p_aout, "InitHardwareInfo failed" );
256 vlc_mutex_destroy( &p_sys->lock );
257 free( (void *)p_sys );
258 return( VLC_EGENERIC );
261 if( var_Type( p_aout, "audio-device" ) == 0 )
263 InitDeviceVar( p_aout, config_GetInt( p_aout, "coreaudio-dev" ),
267 if( InitDevice( p_aout ) )
269 msg_Err( p_aout, "InitDevice failed" );
270 FreeHardwareInfo( p_aout );
271 vlc_mutex_destroy( &p_sys->lock );
272 free( (void *)p_sys );
273 return( VLC_EGENERIC );
276 /* Get a description of the stream format */
277 i_param_size = sizeof( AudioStreamBasicDescription );
278 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
279 kAudioDevicePropertyStreamFormat,
280 &i_param_size, &p_sys->stream_format );
283 msg_Err( p_aout, "failed to get stream format: [%4.4s]",
285 FreeDevice( p_aout );
286 FreeHardwareInfo( p_aout );
287 vlc_mutex_destroy( &p_sys->lock );
288 free( (void *)p_sys );
289 return( VLC_EGENERIC );
292 /* Set the output sample rate */
293 p_aout->output.output.i_rate =
294 (unsigned int)p_sys->stream_format.mSampleRate;
296 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "using format",
297 p_sys->stream_format ) );
299 /* Get the buffer size */
300 i_param_size = sizeof( p_sys->i_buffer_size );
301 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
302 kAudioDevicePropertyBufferSize,
303 &i_param_size, &p_sys->i_buffer_size );
306 msg_Err( p_aout, "failed to get buffer size: [%4.4s]",
308 FreeDevice( p_aout );
309 FreeHardwareInfo( p_aout );
310 vlc_mutex_destroy( &p_sys->lock );
311 free( (void *)p_sys );
312 return( VLC_EGENERIC );
315 msg_Dbg( p_aout, "device buffer size: [%ld]", p_sys->i_buffer_size );
317 /* If we do AC3 over SPDIF, set buffer size to one AC3 frame */
318 if( ( p_sys->stream_format.mFormatID == kAudioFormat60958AC3 ||
319 p_sys->stream_format.mFormatID == 'IAC3' ) &&
320 p_sys->i_buffer_size != AOUT_SPDIF_SIZE )
322 p_sys->i_buffer_size = AOUT_SPDIF_SIZE;
323 i_param_size = sizeof( p_sys->i_buffer_size );
324 err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
325 kAudioDevicePropertyBufferSize,
326 i_param_size, &p_sys->i_buffer_size );
329 msg_Err( p_aout, "failed to set buffer size: [%4.4s]",
331 FreeDevice( p_aout );
332 FreeHardwareInfo( p_aout );
333 vlc_mutex_destroy( &p_sys->lock );
334 free( (void *)p_sys );
335 return( VLC_EGENERIC );
338 msg_Dbg( p_aout, "device buffer size set to: [%ld]",
339 p_sys->i_buffer_size );
341 /* Set buffer frame size */
342 i_param_size = sizeof( p_aout->output.i_nb_samples );
343 err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
344 kAudioDevicePropertyBufferFrameSize,
346 &p_aout->output.i_nb_samples );
349 msg_Err( p_aout, "failed to set buffer frame size: [%4.4s]",
351 FreeDevice( p_aout );
352 FreeHardwareInfo( p_aout );
353 vlc_mutex_destroy( &p_sys->lock );
354 free( (void *)p_sys );
355 return( VLC_EGENERIC );
358 msg_Dbg( p_aout, "device buffer frame size set to: [%d]",
359 p_aout->output.i_nb_samples );
362 switch( p_sys->stream_format.mFormatID )
364 case kAudioFormatLinearPCM:
365 p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
367 switch( p_sys->stream_format.mChannelsPerFrame )
370 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
374 p_aout->output.output.i_physical_channels =
375 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
379 p_aout->output.output.i_physical_channels =
380 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
381 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
385 p_aout->output.output.i_physical_channels =
386 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
387 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
388 AOUT_CHAN_CENTER | AOUT_CHAN_LFE;
392 p_aout->output.output.i_physical_channels =
393 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
394 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
395 AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
396 AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT;
400 msg_Err( p_aout, "unknown channel count: [%ld]",
401 p_sys->stream_format.mChannelsPerFrame );
402 FreeDevice( p_aout );
403 FreeHardwareInfo( p_aout );
404 vlc_mutex_destroy( &p_sys->lock );
405 free( (void *)p_sys );
406 return( VLC_EGENERIC );
409 p_aout->output.i_nb_samples = (int)( p_sys->i_buffer_size /
410 p_sys->stream_format.mBytesPerFrame );
412 aout_VolumeSoftInit( p_aout );
416 case kAudioFormat60958AC3:
417 p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
418 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
419 p_aout->output.output.i_frame_length = A52_FRAME_NB;
420 p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
422 aout_VolumeNoneInit( p_aout );
426 msg_Err( p_aout, "unknown hardware format: [%4.4s]",
427 (char *)&p_sys->stream_format.mFormatID );
428 FreeDevice( p_aout );
429 FreeHardwareInfo( p_aout );
430 vlc_mutex_destroy( &p_sys->lock );
431 free( (void *)p_sys );
432 return( VLC_EGENERIC );
436 err = AudioDeviceAddIOProc( p_sys->devid,
437 (AudioDeviceIOProc)IOCallback,
441 msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]",
443 FreeDevice( p_aout );
444 FreeHardwareInfo( p_aout );
445 vlc_mutex_destroy( &p_sys->lock );
446 free( (void *)p_sys );
447 return( VLC_EGENERIC );
451 err = AudioDeviceStart( p_sys->devid, (AudioDeviceIOProc)IOCallback );
454 msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]",
457 err = AudioDeviceRemoveIOProc( p_sys->devid,
458 (AudioDeviceIOProc)IOCallback );
461 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
465 FreeDevice( p_aout );
466 FreeHardwareInfo( p_aout );
467 vlc_mutex_destroy( &p_sys->lock );
468 free( (void *)p_sys );
470 return( VLC_EGENERIC );
473 err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
478 msg_Err( p_aout, "AudioHardwareAddPropertyListener failed: %4.4s",
482 err = AudioDeviceStop( p_sys->devid,
483 (AudioDeviceIOProc)IOCallback );
486 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
490 /* Remove callback */
491 err = AudioDeviceRemoveIOProc( p_sys->devid,
492 (AudioDeviceIOProc)IOCallback );
495 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
499 FreeDevice( p_aout );
500 FreeHardwareInfo( p_aout );
501 vlc_mutex_destroy( &p_sys->lock );
502 free( (void *)p_sys );
504 return( VLC_EGENERIC );
507 /* Let's pray for the following operation to be atomic... */
508 p_sys->clock_diff = - (mtime_t)
509 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
510 p_sys->clock_diff += mdate();
512 return( VLC_SUCCESS );
515 /*****************************************************************************
516 * Close: close the CoreAudio HAL device
517 *****************************************************************************/
518 static void Close( vlc_object_t * p_this )
521 aout_instance_t * p_aout = (aout_instance_t *)p_this;
522 struct aout_sys_t * p_sys = p_aout->output.p_sys;
524 if( p_sys->b_dev_alive )
527 err = AudioDeviceStop( p_sys->devid,
528 (AudioDeviceIOProc)IOCallback );
531 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
535 /* Remove callback */
536 err = AudioDeviceRemoveIOProc( p_sys->devid,
537 (AudioDeviceIOProc)IOCallback );
540 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
544 FreeDevice( p_aout );
547 err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
551 msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]",
555 FreeHardwareInfo( p_aout );
557 vlc_mutex_destroy( &p_sys->lock );
562 /*****************************************************************************
563 * Play: nothing to do
564 *****************************************************************************/
565 static void Play( aout_instance_t * p_aout )
569 /*****************************************************************************
570 * IOCallback: callback for audio output
571 *****************************************************************************/
572 static OSStatus IOCallback( AudioDeviceID inDevice,
573 const AudioTimeStamp * inNow,
574 const void * inInputData,
575 const AudioTimeStamp * inInputTime,
576 AudioBufferList * outOutputData,
577 const AudioTimeStamp * inOutputTime,
578 void * threadGlobals )
580 aout_buffer_t * p_buffer;
581 AudioTimeStamp host_time;
582 mtime_t current_date;
584 aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
585 struct aout_sys_t * p_sys = p_aout->output.p_sys;
587 host_time.mFlags = kAudioTimeStampHostTimeValid;
588 AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
591 p_sys->clock_diff = - (mtime_t)
592 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
593 p_sys->clock_diff += mdate();
596 current_date = p_sys->clock_diff +
597 AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
599 #define B_SPDI (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i'))
600 p_buffer = aout_OutputNextBuffer( p_aout, current_date, B_SPDI );
603 if( p_buffer != NULL )
605 /* move data into output data buffer */
606 p_aout->p_vlc->pf_memcpy( outOutputData->mBuffers[ 0 ].mData,
608 p_sys->i_buffer_size );
610 aout_BufferFree( p_buffer );
614 if( p_aout->output.output.i_format == VLC_FOURCC('f','l','3','2') )
616 UInt32 i, i_size = p_sys->i_buffer_size / sizeof(float);
617 float * p = (float *)outOutputData->mBuffers[ 0 ].mData;
619 for( i = 0; i < i_size; i++ )
626 memset( outOutputData->mBuffers[ 0 ].mData,
627 0, p_sys->i_buffer_size );
634 /*****************************************************************************
636 *****************************************************************************/
637 static int InitHardwareInfo( aout_instance_t * p_aout )
640 UInt32 i, i_param_size;
641 AudioDeviceID devid_def;
642 AudioDeviceID * p_devices;
644 struct aout_sys_t * p_sys = p_aout->output.p_sys;
646 vlc_mutex_lock( &p_sys->lock );
648 /* Get number of devices */
649 err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
650 &i_param_size, NULL );
653 msg_Err( p_aout, "AudioHardwareGetPropertyInfo failed: [%4.4s]",
655 vlc_mutex_unlock( &p_sys->lock );
656 return( VLC_EGENERIC );
659 p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
661 if( p_sys->i_devices < 1 )
663 msg_Err( p_aout, "no devices found" );
664 vlc_mutex_unlock( &p_sys->lock );
665 return( VLC_EGENERIC );
668 msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
670 /* Allocate DeviceID array */
671 p_devices = (AudioDeviceID *)malloc( i_param_size );
672 if( p_devices == NULL )
674 msg_Err( p_aout, "out of memory" );
675 vlc_mutex_unlock( &p_sys->lock );
676 return( VLC_ENOMEM );
679 /* Populate DeviceID array */
680 err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
681 &i_param_size, (void *)p_devices );
684 msg_Err( p_aout, "AudioHardwareGetProperty failed: [%4.4s]",
686 free( (void *)p_devices );
687 vlc_mutex_unlock( &p_sys->lock );
688 return( VLC_EGENERIC );
691 i_param_size = sizeof( AudioDeviceID );
692 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
693 &i_param_size, (void *)&devid_def );
696 msg_Err( p_aout, "AudioHardwareGetProperty failed: [%4.4s]",
698 free( (void *)p_devices );
699 vlc_mutex_unlock( &p_sys->lock );
700 return( VLC_EGENERIC );
703 p_sys->p_devices = (struct aout_dev_t *)
704 malloc( sizeof( struct aout_dev_t ) * p_sys->i_devices );
705 if( p_sys->p_devices == NULL )
707 msg_Err( p_aout, "out of memory" );
708 free( (void *)p_devices );
709 vlc_mutex_unlock( &p_sys->lock );
710 return( VLC_ENOMEM );
713 p_sys->i_options = 0;
714 p_sys->p_options = NULL;
716 for( i = 0; i < p_sys->i_devices; i++ )
718 p_sys->p_devices[i].devid = p_devices[i];
720 if( p_devices[i] == devid_def )
722 p_sys->i_def_dev = i;
725 if( InitDeviceInfo( i, p_aout ) )
729 msg_Err( p_aout, "InitDeviceInfo(%ld) failed", i );
731 for( j = 0; j < i; j++ )
733 FreeDeviceInfo( j, p_aout );
736 free( (void *)p_sys->p_devices );
737 free( (void *)p_devices );
739 vlc_mutex_unlock( &p_sys->lock );
741 return( VLC_EGENERIC );
745 free( (void *)p_devices );
747 p_sys->b_hwinfo = VLC_TRUE;
749 vlc_mutex_unlock( &p_sys->lock );
751 return( VLC_SUCCESS );
754 /*****************************************************************************
756 *****************************************************************************/
757 static int InitDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
760 UInt32 i, i_param_size;
761 AudioBufferList * p_buffer_list;
763 struct aout_sys_t * p_sys = p_aout->output.p_sys;
764 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
766 /* Get length of device name */
767 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
768 kAudioDevicePropertyDeviceName,
769 &i_param_size, NULL );
772 msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
774 return( VLC_EGENERIC );
777 /* Allocate memory for device name */
778 p_dev->psz_device_name = (char *)malloc( i_param_size );
779 if( p_dev->psz_device_name == NULL )
781 msg_Err( p_aout, "out of memory" );
782 return( VLC_ENOMEM );
785 /* Get device name */
786 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
787 kAudioDevicePropertyDeviceName,
788 &i_param_size, p_dev->psz_device_name );
791 msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
793 free( (void *)p_dev->psz_device_name );
794 return( VLC_EGENERIC );
797 msg_Dbg( p_aout, "device [%ld] has name [%s]",
798 i_dev, p_dev->psz_device_name );
800 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
801 kAudioDevicePropertyStreamConfiguration,
802 &i_param_size, NULL );
805 msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
807 free( (void *)p_dev->psz_device_name );
808 return( VLC_EGENERIC );
811 p_buffer_list = (AudioBufferList *)malloc( i_param_size );
812 if( p_buffer_list == NULL )
814 msg_Err( p_aout, "out of memory" );
815 free( (void *)p_dev->psz_device_name );
816 return( VLC_ENOMEM );
819 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
820 kAudioDevicePropertyStreamConfiguration,
821 &i_param_size, p_buffer_list );
824 msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
826 free( (void *)p_dev->psz_device_name );
827 free( (void *)p_buffer_list );
828 return( VLC_EGENERIC );
831 p_dev->i_streams = p_buffer_list->mNumberBuffers;
832 free( (void *)p_buffer_list );
834 msg_Dbg( p_aout, "device [%ld] has [%ld] streams",
835 i_dev, p_dev->i_streams );
837 p_dev->pi_streams = (UInt32 *)malloc( p_dev->i_streams *
838 sizeof( *p_dev->pi_streams ) );
839 if( p_dev->pi_streams == NULL )
841 msg_Err( p_aout, "out of memory" );
842 free( (void *)p_dev->psz_device_name );
843 return( VLC_ENOMEM );
846 p_dev->pp_streams = (AudioStreamBasicDescription **)
847 malloc( p_dev->i_streams *
848 sizeof( *p_dev->pp_streams ) );
849 if( p_dev->pp_streams == NULL )
851 msg_Err( p_aout, "out of memory" );
852 free( (void *)p_dev->psz_device_name );
853 free( (void *)p_dev->pi_streams );
854 return( VLC_ENOMEM );
857 for( i = 0; i < p_dev->i_streams; i++ )
859 if( InitStreamInfo( i_dev, p_aout, i ) )
863 msg_Err( p_aout, "InitStreamInfo(%ld, %ld) failed", i_dev, i );
865 for( j = 0; j < i; j++ )
867 FreeStreamInfo( i_dev, p_aout, j );
870 free( (void *)p_dev->psz_device_name );
871 free( (void *)p_dev->pi_streams );
872 free( (void *)p_dev->pp_streams );
874 return( VLC_EGENERIC );
878 return( VLC_SUCCESS );
881 /*****************************************************************************
883 *****************************************************************************/
884 static void FreeDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
888 struct aout_sys_t * p_sys = p_aout->output.p_sys;
889 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
891 for( i = 0; i < p_dev->i_streams; i++ )
893 FreeStreamInfo( i_dev, p_aout, i );
896 free( (void *)p_dev->pp_streams );
897 free( (void *)p_dev->pi_streams );
898 free( (void *)p_dev->psz_device_name );
901 /*****************************************************************************
903 *****************************************************************************/
904 static void FreeHardwareInfo( aout_instance_t * p_aout )
908 struct aout_sys_t * p_sys = p_aout->output.p_sys;
910 vlc_mutex_lock( &p_sys->lock );
912 if( !p_sys->b_hwinfo )
914 vlc_mutex_unlock( &p_sys->lock );
918 for( i = 0; i < p_sys->i_devices; i++ )
920 FreeDeviceInfo( i, p_aout );
923 free( (void *)p_sys->p_options );
924 free( (void *)p_sys->p_devices );
926 p_sys->b_hwinfo = VLC_FALSE;
928 vlc_mutex_unlock( &p_sys->lock );
931 /*****************************************************************************
933 *****************************************************************************/
934 static int GetStreamID( AudioDeviceID devid, UInt32 i_idx,
935 AudioStreamID * p_sid )
939 AudioStreamID * p_stream_list;
941 err = AudioDeviceGetPropertyInfo( devid, 0, FALSE,
942 kAudioDevicePropertyStreams,
943 &i_param_size, NULL );
946 return( VLC_EGENERIC );
949 p_stream_list = (AudioStreamID *)malloc( i_param_size );
950 if( p_stream_list == NULL )
952 return( VLC_ENOMEM );
955 err = AudioDeviceGetProperty( devid, 0, FALSE,
956 kAudioDevicePropertyStreams,
957 &i_param_size, p_stream_list );
960 free( (void *)p_stream_list );
961 return( VLC_EGENERIC );
964 *p_sid = p_stream_list[i_idx - 1];
966 free( (void *)p_stream_list );
968 return( VLC_SUCCESS );
971 /*****************************************************************************
973 *****************************************************************************/
974 static int InitStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
979 UInt32 i, j, i_param_size;
981 struct aout_sys_t * p_sys = p_aout->output.p_sys;
982 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
984 if( GetStreamID( p_dev->devid, i_idx + 1, &i_sid ) )
986 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed", i_dev, i_idx );
987 return( VLC_EGENERIC );
990 err = AudioStreamGetPropertyInfo( i_sid, 0,
991 kAudioStreamPropertyPhysicalFormats,
992 &i_param_size, NULL );
995 msg_Err( p_aout, "AudioStreamGetPropertyInfo failed: [%4.4s]",
997 return( VLC_EGENERIC );
1000 #define P_STREAMS p_dev->pp_streams[i_idx]
1001 #define I_STREAMS p_dev->pi_streams[i_idx]
1003 I_STREAMS = i_param_size / sizeof( AudioStreamBasicDescription );
1005 P_STREAMS = (AudioStreamBasicDescription *)malloc( i_param_size );
1006 if( P_STREAMS == NULL )
1008 msg_Err( p_aout, "out of memory" );
1009 return( VLC_ENOMEM );
1012 memset( P_STREAMS, 0, i_param_size );
1014 err = AudioStreamGetProperty( i_sid, 0,
1015 kAudioStreamPropertyPhysicalFormats,
1016 &i_param_size, P_STREAMS );
1019 msg_Err( p_aout, "AudioStreamGetProperty failed: [%4.4s]",
1021 free( (void *)P_STREAMS );
1022 return( VLC_EGENERIC );
1025 for( j = 0; j < N_AOUT_CLASSES; j++ )
1027 vlc_bool_t b_found = 0;
1029 for( i = 0; i < I_STREAMS; i++ )
1033 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format",
1037 if( ( P_STREAMS[i].mFormatID == 'IAC3' ||
1038 P_STREAMS[i].mFormatID == kAudioFormat60958AC3 ) &&
1039 !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
1044 if( ( P_STREAMS[i].mFormatID != aout_classes[j].mFormatID ) ||
1045 ( P_STREAMS[i].mChannelsPerFrame !=
1046 aout_classes[j].mChannelsPerFrame ) )
1057 p_sys->p_options = (struct aout_option_t *)
1058 realloc( p_sys->p_options,
1059 ( p_sys->i_options + 1 ) *
1060 sizeof( struct aout_option_t ) );
1061 if( p_sys->p_options == NULL )
1063 msg_Err( p_aout, "out of memory" );
1064 free( (void *)P_STREAMS );
1065 return( VLC_ENOMEM );
1068 #define AOUT_OPTION p_sys->p_options[p_sys->i_options]
1070 snprintf( AOUT_OPTION.sz_option,
1071 sizeof( AOUT_OPTION.sz_option ) /
1072 sizeof( AOUT_OPTION.sz_option[0] ) - 1,
1075 p_dev->psz_device_name,
1076 aout_classes[j].psz_class );
1078 AOUT_OPTION.i_sid = i_sid;
1079 AOUT_OPTION.i_dev = i_dev;
1080 AOUT_OPTION.i_idx = i_idx;
1081 AOUT_OPTION.i_sdx = i;
1082 AOUT_OPTION.i_cdx = j;
1093 return( VLC_SUCCESS );
1096 /*****************************************************************************
1098 *****************************************************************************/
1099 static void FreeStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
1102 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1103 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
1105 free( (void *)p_dev->pp_streams[i_idx] );
1108 /*****************************************************************************
1110 *****************************************************************************/
1111 static int InitDevice( aout_instance_t * p_aout )
1115 unsigned int i_option;
1116 vlc_bool_t b_found = VLC_FALSE;
1117 UInt32 i, i_stream, i_param_size;
1119 struct aout_dev_t * p_dev;
1120 struct aout_option_t * p_option;
1121 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1123 if( var_Get( p_aout, "audio-device", &val ) < 0 )
1125 msg_Err( p_aout, "audio-device var does not exist" );
1126 return( VLC_ENOVAR );
1129 i_option = val.i_int;
1130 p_option = &p_sys->p_options[i_option];
1131 p_dev = &p_sys->p_devices[p_option->i_dev];
1133 msg_Dbg( p_aout, "getting device [%ld]", p_option->i_dev );
1135 i_param_size = sizeof( p_sys->b_dev_alive );
1136 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
1137 kAudioDevicePropertyDeviceIsAlive,
1138 &i_param_size, &p_sys->b_dev_alive );
1141 msg_Err( p_aout, "AudioDeviceGetProperty failed: %4.4s",
1143 return( VLC_EGENERIC );
1146 #define P_STREAMS p_dev->pp_streams[p_option->i_idx]
1147 #define I_STREAMS p_dev->pi_streams[p_option->i_idx]
1149 for( i = 0; i < I_STREAMS; i++ )
1151 if( P_STREAMS[i].mFormatID ==
1152 aout_classes[p_option->i_cdx].mFormatID &&
1153 P_STREAMS[i].mChannelsPerFrame ==
1154 aout_classes[p_option->i_cdx].mChannelsPerFrame &&
1155 P_STREAMS[i].mSampleRate == p_aout->output.output.i_rate )
1162 i_stream = b_found ? i : p_option->i_sdx;
1164 i_param_size = sizeof( p_sys->sfmt_revert );
1165 err = AudioStreamGetProperty( p_option->i_sid, 0,
1166 kAudioStreamPropertyPhysicalFormat,
1168 (void *)&p_sys->sfmt_revert );
1171 msg_Err( p_aout, "AudioStreamGetPropertyInfo failed: [%4.4s]",
1173 return( VLC_EGENERIC );
1176 if( memcmp( &P_STREAMS[i_stream], &p_sys->sfmt_revert,
1177 sizeof( p_sys->sfmt_revert ) ) != 0 )
1180 struct timespec timeout;
1181 struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
1183 vlc_cond_init( p_aout, &w.cond );
1184 vlc_mutex_init( p_aout, &w.lock );
1186 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "stream format",
1187 p_sys->sfmt_revert ) );
1189 err = AudioStreamAddPropertyListener( p_option->i_sid, 0,
1190 kAudioStreamPropertyPhysicalFormat,
1191 StreamListener, (void *)&w );
1194 msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]",
1196 vlc_mutex_destroy( &w.lock );
1197 vlc_cond_destroy( &w.cond );
1198 return( VLC_EGENERIC );
1201 vlc_mutex_lock( &w.lock );
1203 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting format",
1204 P_STREAMS[i_stream] ) );
1206 err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
1207 kAudioStreamPropertyPhysicalFormat,
1208 sizeof( P_STREAMS[i_stream] ),
1209 &P_STREAMS[i_stream] );
1212 msg_Err( p_aout, "AudioStreamSetProperty failed: [%4.4s]",
1214 vlc_mutex_unlock( &w.lock );
1215 vlc_mutex_destroy( &w.lock );
1216 vlc_cond_destroy( &w.cond );
1217 return( VLC_EGENERIC );
1220 gettimeofday( &now, NULL );
1221 timeout.tv_sec = now.tv_sec;
1222 timeout.tv_nsec = (now.tv_usec + 100000) * 1000;
1224 pthread_cond_timedwait( &w.cond.cond, &w.lock.mutex, &timeout );
1225 vlc_mutex_unlock( &w.lock );
1227 if( GetStreamID( p_dev->devid, p_option->i_idx + 1,
1228 &p_option->i_sid ) )
1230 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed",
1231 p_option->i_dev, p_option->i_idx );
1232 vlc_mutex_destroy( &w.lock );
1233 vlc_cond_destroy( &w.cond );
1234 return( VLC_EGENERIC );
1237 err = AudioStreamRemovePropertyListener( p_option->i_sid, 0,
1238 kAudioStreamPropertyPhysicalFormat, StreamListener );
1242 "AudioStreamRemovePropertyListener failed: [%4.4s]",
1244 vlc_mutex_destroy( &w.lock );
1245 vlc_cond_destroy( &w.cond );
1246 return( VLC_EGENERIC );
1249 vlc_mutex_destroy( &w.lock );
1250 vlc_cond_destroy( &w.cond );
1252 p_sys->b_revert_sfmt = VLC_TRUE;
1258 err = AudioDeviceAddPropertyListener( p_dev->devid, 0, FALSE,
1259 kAudioDevicePropertyDeviceIsAlive,
1260 DeviceListener, (void *)p_aout );
1263 msg_Err( p_aout, "AudioDeviceAddPropertyListener failed: [%4.4s]",
1265 return( VLC_EGENERIC );
1268 config_PutInt( p_aout, "coreaudio-dev", i_option );
1270 p_sys->i_sel_opt = i_option;
1271 p_sys->devid = p_dev->devid;
1273 return( VLC_SUCCESS );
1276 /*****************************************************************************
1278 *****************************************************************************/
1279 static void FreeDevice( aout_instance_t * p_aout )
1283 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1285 if( p_sys->b_revert_sfmt )
1287 struct aout_dev_t * p_dev;
1288 struct aout_option_t * p_option;
1290 p_option = &p_sys->p_options[p_sys->i_sel_opt];
1291 p_dev = &p_sys->p_devices[p_option->i_dev];
1293 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "reverting to format",
1294 p_sys->sfmt_revert ) );
1296 if( GetStreamID( p_dev->devid, p_option->i_idx + 1,
1297 &p_option->i_sid ) )
1299 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed",
1300 p_option->i_dev, p_option->i_idx );
1304 err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
1305 kAudioStreamPropertyPhysicalFormat,
1306 sizeof( p_sys->sfmt_revert ),
1307 &p_sys->sfmt_revert );
1310 msg_Err( p_aout, "AudioStreamSetProperty failed: [%4.4s]",
1316 err = AudioDeviceRemovePropertyListener( p_sys->devid, 0, FALSE,
1317 kAudioDevicePropertyDeviceIsAlive,
1321 msg_Err( p_aout, "AudioDeviceRemovePropertyListener failed: [%4.4s]",
1326 /*****************************************************************************
1328 *****************************************************************************/
1329 static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
1330 void * inClientData )
1332 OSStatus err = noErr;
1334 aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1335 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1337 switch( inPropertyID )
1339 case kAudioHardwarePropertyDevices:
1345 if( p_sys->b_dev_alive )
1347 i_idx = p_sys->p_options[p_sys->i_sel_opt].i_idx;
1348 i_sdx = p_sys->p_options[p_sys->i_sel_opt].i_sdx;
1351 FreeHardwareInfo( p_aout );
1353 if( InitHardwareInfo( p_aout ) )
1355 msg_Err( p_aout, "InitHardwareInfo failed" );
1359 if( p_sys->b_dev_alive )
1363 for( i = 0; i < p_sys->i_options; i++ )
1365 if( p_sys->p_devices[p_sys->p_options[i].i_dev].devid ==
1366 p_sys->devid && p_sys->p_options[i].i_idx == i_idx &&
1367 p_sys->p_options[i].i_sdx == i_sdx )
1375 var_Destroy( p_aout, "audio-device" );
1376 InitDeviceVar( p_aout, i_option, !p_sys->b_dev_alive );
1384 /*****************************************************************************
1386 *****************************************************************************/
1387 static OSStatus DeviceListener( AudioDeviceID inDevice,
1390 AudioDevicePropertyID inPropertyID,
1391 void *inClientData )
1393 UInt32 i_param_size;
1394 OSStatus err = noErr;
1396 aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1397 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1399 switch( inPropertyID )
1401 case kAudioDevicePropertyDeviceIsAlive:
1403 i_param_size = sizeof( p_sys->b_dev_alive );
1404 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
1405 kAudioDevicePropertyDeviceIsAlive,
1406 &i_param_size, &p_sys->b_dev_alive );
1409 msg_Err( p_aout, "AudioDeviceGetProperty failed: %4.4s",
1419 /*****************************************************************************
1421 *****************************************************************************/
1422 static OSStatus StreamListener( AudioStreamID inStream,
1424 AudioDevicePropertyID inPropertyID,
1425 void * inClientData )
1427 OSStatus err = noErr;
1429 struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1431 switch( inPropertyID )
1433 case kAudioStreamPropertyPhysicalFormat:
1434 vlc_mutex_lock( &w->lock );
1435 vlc_cond_signal( &w->cond );
1436 vlc_mutex_unlock( &w->lock );
1446 /*****************************************************************************
1448 *****************************************************************************/
1449 static void InitDeviceVar( aout_instance_t * p_aout, int i_option,
1450 vlc_bool_t b_change )
1453 vlc_value_t val, text;
1455 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1457 if( i_option == -1 || i_option >= (int)p_sys->i_options )
1459 for( i = 0; i < p_sys->i_options; i++ )
1461 if( p_sys->p_options[i].i_dev == p_sys->i_def_dev )
1469 var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
1470 text.psz_string = _("Audio device");
1471 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
1473 for( i = 0; i < p_sys->i_options; i++ )
1475 text.psz_string = p_sys->p_options[i].sz_option;
1477 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
1479 if( !b_change && i == (UInt32)i_option )
1481 p_sys->i_sel_opt = i;
1482 var_Set( p_aout, "audio-device", val );
1483 config_PutInt( p_aout, "coreaudio-dev", i_option );
1487 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
1492 val.i_int = i_option;
1493 var_Set( p_aout, "audio-device", val );
1496 val.b_bool = VLC_TRUE;
1497 var_Set( p_aout, "intf-change", val );