1 /*****************************************************************************
2 * coreaudio.c: CoreAudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2002-2003 VideoLAN
5 * $Id: coreaudio.c,v 1.2 2003/05/04 15:02:42 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 );
589 current_date = p_sys->clock_diff +
590 AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
592 #define B_SPDI (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i'))
593 p_buffer = aout_OutputNextBuffer( p_aout, current_date, B_SPDI );
596 if( p_buffer != NULL )
598 /* move data into output data buffer */
599 p_aout->p_vlc->pf_memcpy( outOutputData->mBuffers[ 0 ].mData,
601 p_sys->i_buffer_size );
603 aout_BufferFree( p_buffer );
607 if( p_aout->output.output.i_format == VLC_FOURCC('f','l','3','2') )
609 UInt32 i, i_size = p_sys->i_buffer_size / sizeof(float);
610 float * p = (float *)outOutputData->mBuffers[ 0 ].mData;
612 for( i = 0; i < i_size; i++ )
619 memset( outOutputData->mBuffers[ 0 ].mData,
620 0, p_sys->i_buffer_size );
627 /*****************************************************************************
629 *****************************************************************************/
630 static int InitHardwareInfo( aout_instance_t * p_aout )
633 UInt32 i, i_param_size;
634 AudioDeviceID devid_def;
635 AudioDeviceID * p_devices;
637 struct aout_sys_t * p_sys = p_aout->output.p_sys;
639 vlc_mutex_lock( &p_sys->lock );
641 /* Get number of devices */
642 err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
643 &i_param_size, NULL );
646 msg_Err( p_aout, "AudioHardwareGetPropertyInfo failed: [%4.4s]",
648 vlc_mutex_unlock( &p_sys->lock );
649 return( VLC_EGENERIC );
652 p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
654 if( p_sys->i_devices < 1 )
656 msg_Err( p_aout, "no devices found" );
657 vlc_mutex_unlock( &p_sys->lock );
658 return( VLC_EGENERIC );
661 msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
663 /* Allocate DeviceID array */
664 p_devices = (AudioDeviceID *)malloc( i_param_size );
665 if( p_devices == NULL )
667 msg_Err( p_aout, "out of memory" );
668 vlc_mutex_unlock( &p_sys->lock );
669 return( VLC_ENOMEM );
672 /* Populate DeviceID array */
673 err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
674 &i_param_size, (void *)p_devices );
677 msg_Err( p_aout, "AudioHardwareGetProperty failed: [%4.4s]",
679 free( (void *)p_devices );
680 vlc_mutex_unlock( &p_sys->lock );
681 return( VLC_EGENERIC );
684 i_param_size = sizeof( AudioDeviceID );
685 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
686 &i_param_size, (void *)&devid_def );
689 msg_Err( p_aout, "AudioHardwareGetProperty failed: [%4.4s]",
691 free( (void *)p_devices );
692 vlc_mutex_unlock( &p_sys->lock );
693 return( VLC_EGENERIC );
696 p_sys->p_devices = (struct aout_dev_t *)
697 malloc( sizeof( struct aout_dev_t ) * p_sys->i_devices );
698 if( p_sys->p_devices == NULL )
700 msg_Err( p_aout, "out of memory" );
701 free( (void *)p_devices );
702 vlc_mutex_unlock( &p_sys->lock );
703 return( VLC_ENOMEM );
706 p_sys->i_options = 0;
707 p_sys->p_options = NULL;
709 for( i = 0; i < p_sys->i_devices; i++ )
711 p_sys->p_devices[i].devid = p_devices[i];
713 if( p_devices[i] == devid_def )
715 p_sys->i_def_dev = i;
718 if( InitDeviceInfo( i, p_aout ) )
722 msg_Err( p_aout, "InitDeviceInfo(%ld) failed", i );
724 for( j = 0; j < i; j++ )
726 FreeDeviceInfo( j, p_aout );
729 free( (void *)p_sys->p_devices );
730 free( (void *)p_devices );
732 vlc_mutex_unlock( &p_sys->lock );
734 return( VLC_EGENERIC );
738 free( (void *)p_devices );
740 p_sys->b_hwinfo = VLC_TRUE;
742 vlc_mutex_unlock( &p_sys->lock );
744 return( VLC_SUCCESS );
747 /*****************************************************************************
749 *****************************************************************************/
750 static int InitDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
753 UInt32 i, i_param_size;
754 AudioBufferList * p_buffer_list;
756 struct aout_sys_t * p_sys = p_aout->output.p_sys;
757 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
759 /* Get length of device name */
760 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
761 kAudioDevicePropertyDeviceName,
762 &i_param_size, NULL );
765 msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
767 return( VLC_EGENERIC );
770 /* Allocate memory for device name */
771 p_dev->psz_device_name = (char *)malloc( i_param_size );
772 if( p_dev->psz_device_name == NULL )
774 msg_Err( p_aout, "out of memory" );
775 return( VLC_ENOMEM );
778 /* Get device name */
779 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
780 kAudioDevicePropertyDeviceName,
781 &i_param_size, p_dev->psz_device_name );
784 msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
786 free( (void *)p_dev->psz_device_name );
787 return( VLC_EGENERIC );
790 msg_Dbg( p_aout, "device [%ld] has name [%s]",
791 i_dev, p_dev->psz_device_name );
793 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
794 kAudioDevicePropertyStreamConfiguration,
795 &i_param_size, NULL );
798 msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
800 free( (void *)p_dev->psz_device_name );
801 return( VLC_EGENERIC );
804 p_buffer_list = (AudioBufferList *)malloc( i_param_size );
805 if( p_buffer_list == NULL )
807 msg_Err( p_aout, "out of memory" );
808 free( (void *)p_dev->psz_device_name );
809 return( VLC_ENOMEM );
812 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
813 kAudioDevicePropertyStreamConfiguration,
814 &i_param_size, p_buffer_list );
817 msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
819 free( (void *)p_dev->psz_device_name );
820 free( (void *)p_buffer_list );
821 return( VLC_EGENERIC );
824 p_dev->i_streams = p_buffer_list->mNumberBuffers;
825 free( (void *)p_buffer_list );
827 msg_Dbg( p_aout, "device [%ld] has [%ld] streams",
828 i_dev, p_dev->i_streams );
830 p_dev->pi_streams = (UInt32 *)malloc( p_dev->i_streams *
831 sizeof( *p_dev->pi_streams ) );
832 if( p_dev->pi_streams == NULL )
834 msg_Err( p_aout, "out of memory" );
835 free( (void *)p_dev->psz_device_name );
836 return( VLC_ENOMEM );
839 p_dev->pp_streams = (AudioStreamBasicDescription **)
840 malloc( p_dev->i_streams *
841 sizeof( *p_dev->pp_streams ) );
842 if( p_dev->pp_streams == NULL )
844 msg_Err( p_aout, "out of memory" );
845 free( (void *)p_dev->psz_device_name );
846 free( (void *)p_dev->pi_streams );
847 return( VLC_ENOMEM );
850 for( i = 0; i < p_dev->i_streams; i++ )
852 if( InitStreamInfo( i_dev, p_aout, i ) )
856 msg_Err( p_aout, "InitStreamInfo(%ld, %ld) failed", i_dev, i );
858 for( j = 0; j < i; j++ )
860 FreeStreamInfo( i_dev, p_aout, j );
863 free( (void *)p_dev->psz_device_name );
864 free( (void *)p_dev->pi_streams );
865 free( (void *)p_dev->pp_streams );
867 return( VLC_EGENERIC );
871 return( VLC_SUCCESS );
874 /*****************************************************************************
876 *****************************************************************************/
877 static void FreeDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
881 struct aout_sys_t * p_sys = p_aout->output.p_sys;
882 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
884 for( i = 0; i < p_dev->i_streams; i++ )
886 FreeStreamInfo( i_dev, p_aout, i );
889 free( (void *)p_dev->pp_streams );
890 free( (void *)p_dev->pi_streams );
891 free( (void *)p_dev->psz_device_name );
894 /*****************************************************************************
896 *****************************************************************************/
897 static void FreeHardwareInfo( aout_instance_t * p_aout )
901 struct aout_sys_t * p_sys = p_aout->output.p_sys;
903 vlc_mutex_lock( &p_sys->lock );
905 if( !p_sys->b_hwinfo )
907 vlc_mutex_unlock( &p_sys->lock );
911 for( i = 0; i < p_sys->i_devices; i++ )
913 FreeDeviceInfo( i, p_aout );
916 free( (void *)p_sys->p_options );
917 free( (void *)p_sys->p_devices );
919 p_sys->b_hwinfo = VLC_FALSE;
921 vlc_mutex_unlock( &p_sys->lock );
924 /*****************************************************************************
926 *****************************************************************************/
927 static int GetStreamID( AudioDeviceID devid, UInt32 i_idx,
928 AudioStreamID * p_sid )
932 AudioStreamID * p_stream_list;
934 err = AudioDeviceGetPropertyInfo( devid, 0, FALSE,
935 kAudioDevicePropertyStreams,
936 &i_param_size, NULL );
939 return( VLC_EGENERIC );
942 p_stream_list = (AudioStreamID *)malloc( i_param_size );
943 if( p_stream_list == NULL )
945 return( VLC_ENOMEM );
948 err = AudioDeviceGetProperty( devid, 0, FALSE,
949 kAudioDevicePropertyStreams,
950 &i_param_size, p_stream_list );
953 free( (void *)p_stream_list );
954 return( VLC_EGENERIC );
957 *p_sid = p_stream_list[i_idx - 1];
959 free( (void *)p_stream_list );
961 return( VLC_SUCCESS );
964 /*****************************************************************************
966 *****************************************************************************/
967 static int InitStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
972 UInt32 i, j, i_param_size;
974 struct aout_sys_t * p_sys = p_aout->output.p_sys;
975 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
977 if( GetStreamID( p_dev->devid, i_idx + 1, &i_sid ) )
979 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed", i_dev, i_idx );
980 return( VLC_EGENERIC );
983 err = AudioStreamGetPropertyInfo( i_sid, 0,
984 kAudioStreamPropertyPhysicalFormats,
985 &i_param_size, NULL );
988 msg_Err( p_aout, "AudioStreamGetPropertyInfo failed: [%4.4s]",
990 return( VLC_EGENERIC );
993 #define P_STREAMS p_dev->pp_streams[i_idx]
994 #define I_STREAMS p_dev->pi_streams[i_idx]
996 I_STREAMS = i_param_size / sizeof( AudioStreamBasicDescription );
998 P_STREAMS = (AudioStreamBasicDescription *)malloc( i_param_size );
999 if( P_STREAMS == NULL )
1001 msg_Err( p_aout, "out of memory" );
1002 return( VLC_ENOMEM );
1005 memset( P_STREAMS, 0, i_param_size );
1007 err = AudioStreamGetProperty( i_sid, 0,
1008 kAudioStreamPropertyPhysicalFormats,
1009 &i_param_size, P_STREAMS );
1012 msg_Err( p_aout, "AudioStreamGetProperty failed: [%4.4s]",
1014 free( (void *)P_STREAMS );
1015 return( VLC_EGENERIC );
1018 for( j = 0; j < N_AOUT_CLASSES; j++ )
1020 vlc_bool_t b_found = 0;
1022 for( i = 0; i < I_STREAMS; i++ )
1026 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format",
1030 if( ( P_STREAMS[i].mFormatID == 'IAC3' ||
1031 P_STREAMS[i].mFormatID == kAudioFormat60958AC3 ) &&
1032 !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
1037 if( ( P_STREAMS[i].mFormatID != aout_classes[j].mFormatID ) ||
1038 ( P_STREAMS[i].mChannelsPerFrame !=
1039 aout_classes[j].mChannelsPerFrame ) )
1050 p_sys->p_options = (struct aout_option_t *)
1051 realloc( p_sys->p_options,
1052 ( p_sys->i_options + 1 ) *
1053 sizeof( struct aout_option_t ) );
1054 if( p_sys->p_options == NULL )
1056 msg_Err( p_aout, "out of memory" );
1057 free( (void *)P_STREAMS );
1058 return( VLC_ENOMEM );
1061 #define AOUT_OPTION p_sys->p_options[p_sys->i_options]
1063 snprintf( AOUT_OPTION.sz_option,
1064 sizeof( AOUT_OPTION.sz_option ) /
1065 sizeof( AOUT_OPTION.sz_option[0] ) - 1,
1068 p_dev->psz_device_name,
1069 aout_classes[j].psz_class );
1071 AOUT_OPTION.i_sid = i_sid;
1072 AOUT_OPTION.i_dev = i_dev;
1073 AOUT_OPTION.i_idx = i_idx;
1074 AOUT_OPTION.i_sdx = i;
1075 AOUT_OPTION.i_cdx = j;
1086 return( VLC_SUCCESS );
1089 /*****************************************************************************
1091 *****************************************************************************/
1092 static void FreeStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
1095 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1096 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
1098 free( (void *)p_dev->pp_streams[i_idx] );
1101 /*****************************************************************************
1103 *****************************************************************************/
1104 static int InitDevice( aout_instance_t * p_aout )
1108 unsigned int i_option;
1109 vlc_bool_t b_found = VLC_FALSE;
1110 UInt32 i, i_stream, i_param_size;
1112 struct aout_dev_t * p_dev;
1113 struct aout_option_t * p_option;
1114 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1116 if( var_Get( p_aout, "audio-device", &val ) < 0 )
1118 msg_Err( p_aout, "audio-device var does not exist" );
1119 return( VLC_ENOVAR );
1122 if( !sscanf( val.psz_string, "%d:", &i_option ) ||
1123 p_sys->i_options <= i_option )
1128 free( (void *)val.psz_string );
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 )
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_STRING |
1470 VLC_VAR_HASCHOICE );
1472 for( i = 0; i < p_sys->i_options; i++ )
1474 val.psz_string = p_sys->p_options[i].sz_option;
1475 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
1477 if( !b_change && i == (UInt32)i_option )
1479 p_sys->i_sel_opt = i;
1480 var_Set( p_aout, "audio-device", val );
1481 config_PutInt( p_aout, "coreaudio-dev", i_option );
1485 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
1490 val.psz_string = p_sys->p_options[i_option].sz_option;
1491 var_Set( p_aout, "audio-device", val );
1494 val.b_bool = VLC_TRUE;
1495 var_Set( p_aout, "intf-change", val );