1 /*****************************************************************************
2 * aout.m: CoreAudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2002-2003 VideoLAN
5 * $Id: aout.m,v 1.28 2003/03/15 22:10:58 jlj Exp $
7 * Authors: Colin Delacroix <colin@zoy.org>
8 * Jon Lech Johansen <jon-vl@nanocrew.net>
9 * Christophe Massiot <massiot@via.ecp.fr>
10 * Heiko Panther <heiko.panther@web.de>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
36 #include "aout_internal.h"
38 #include <Carbon/Carbon.h>
39 #include <CoreAudio/HostTime.h>
40 #include <CoreAudio/AudioHardware.h>
42 #define A52_FRAME_NB 1536
44 #define STREAM_FORMAT_MSG( pre, sfm ) \
45 pre ": [%ld][%4.4s][%ld][%ld][%ld][%ld][%ld][%ld]", \
46 (UInt32)sfm.mSampleRate, (char *)&sfm.mFormatID, \
47 sfm.mFormatFlags, sfm.mBytesPerPacket, \
48 sfm.mFramesPerPacket, sfm.mBytesPerFrame, \
49 sfm.mChannelsPerFrame, sfm.mBitsPerChannel
51 /*****************************************************************************
53 ****************************************************************************/
56 AudioDeviceClassA52 = 1 << 0,
57 AudioDeviceClassPCM = 1 << 1
60 static struct aout_class_t
63 UInt32 mChannelsPerFrame;
64 enum AudioDeviceClass class;
65 const char * psz_class;
69 { /* old A/52 format type */
76 { /* new A/52 format type */
84 kAudioFormatLinearPCM,
91 kAudioFormatLinearPCM,
98 kAudioFormatLinearPCM,
105 kAudioFormatLinearPCM,
112 kAudioFormatLinearPCM,
119 #define N_AOUT_CLASSES (sizeof(aout_classes)/sizeof(aout_classes[0]))
121 /*****************************************************************************
123 ****************************************************************************/
132 /*****************************************************************************
134 ****************************************************************************/
138 char * psz_device_name;
141 AudioStreamBasicDescription ** pp_streams;
144 /*****************************************************************************
145 * aout_sys_t: private audio output method descriptor
146 *****************************************************************************
147 * This structure is part of the audio output thread descriptor.
148 * It describes the CoreAudio specific properties of an output thread.
149 *****************************************************************************/
156 struct aout_dev_t * p_devices;
159 struct aout_option_t * p_options;
162 AudioStreamBasicDescription stream_format;
165 vlc_bool_t b_revert_sfmt;
166 AudioStreamBasicDescription sfmt_revert;
168 UInt32 i_buffer_size;
172 /*****************************************************************************
174 *****************************************************************************/
175 static int InitHardwareInfo ( aout_instance_t * p_aout );
176 static int InitDeviceInfo ( UInt32 i_dev, aout_instance_t * p_aout );
177 static void FreeDeviceInfo ( UInt32 i_dev, aout_instance_t * p_aout );
178 static void FreeHardwareInfo ( aout_instance_t * p_aout );
179 static int InitDevice ( aout_instance_t * p_aout );
180 static void FreeDevice ( aout_instance_t * p_aout );
181 static int GetStreamID ( AudioDeviceID devid, UInt32 i_idx,
182 AudioStreamID * p_sid );
183 static int InitStreamInfo ( UInt32 i_dev, aout_instance_t * p_aout,
185 static void FreeStreamInfo ( UInt32 i_dev, aout_instance_t * p_aout,
187 static void InitDeviceVar ( aout_instance_t * p_aout, int i_option,
188 vlc_bool_t b_change );
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 /*****************************************************************************
215 * Open: open a CoreAudio HAL device
216 *****************************************************************************/
217 int E_(OpenAudio)( vlc_object_t * p_this )
221 struct aout_sys_t * p_sys;
222 aout_instance_t * p_aout = (aout_instance_t *)p_this;
224 /* Allocate structure */
225 p_sys = (struct aout_sys_t *)malloc( sizeof( struct aout_sys_t ) );
228 msg_Err( p_aout, "out of memory" );
229 return( VLC_ENOMEM );
232 memset( p_sys, 0, sizeof( struct aout_sys_t ) );
234 p_aout->output.p_sys = p_sys;
235 p_aout->output.pf_play = Play;
237 vlc_mutex_init( p_aout, &p_sys->lock );
239 if( InitHardwareInfo( p_aout ) )
241 msg_Err( p_aout, "InitHardwareInfo failed" );
242 vlc_mutex_destroy( &p_sys->lock );
243 free( (void *)p_sys );
244 return( VLC_EGENERIC );
247 if( var_Type( p_aout, "audio-device" ) == 0 )
249 InitDeviceVar( p_aout, config_GetInt( p_aout, "macosx-adev" ),
253 if( InitDevice( p_aout ) )
255 msg_Err( p_aout, "InitDevice failed" );
256 FreeHardwareInfo( p_aout );
257 vlc_mutex_destroy( &p_sys->lock );
258 free( (void *)p_sys );
259 return( VLC_EGENERIC );
262 /* Get a description of the stream format */
263 i_param_size = sizeof( AudioStreamBasicDescription );
264 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
265 kAudioDevicePropertyStreamFormat,
266 &i_param_size, &p_sys->stream_format );
269 msg_Err( p_aout, "failed to get stream format: [%4.4s]",
271 FreeDevice( p_aout );
272 FreeHardwareInfo( p_aout );
273 vlc_mutex_destroy( &p_sys->lock );
274 free( (void *)p_sys );
275 return( VLC_EGENERIC );
278 /* Set the output sample rate */
279 p_aout->output.output.i_rate =
280 (unsigned int)p_sys->stream_format.mSampleRate;
282 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "using format",
283 p_sys->stream_format ) );
285 /* Get the buffer size */
286 i_param_size = sizeof( p_sys->i_buffer_size );
287 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
288 kAudioDevicePropertyBufferSize,
289 &i_param_size, &p_sys->i_buffer_size );
292 msg_Err( p_aout, "failed to get buffer size: [%4.4s]",
294 FreeDevice( p_aout );
295 FreeHardwareInfo( p_aout );
296 vlc_mutex_destroy( &p_sys->lock );
297 free( (void *)p_sys );
298 return( VLC_EGENERIC );
301 msg_Dbg( p_aout, "device buffer size: [%ld]", p_sys->i_buffer_size );
303 /* If we do AC3 over SPDIF, set buffer size to one AC3 frame */
304 if( ( p_sys->stream_format.mFormatID == kAudioFormat60958AC3 ||
305 p_sys->stream_format.mFormatID == 'IAC3' ) &&
306 p_sys->i_buffer_size != AOUT_SPDIF_SIZE )
308 p_sys->i_buffer_size = AOUT_SPDIF_SIZE;
309 i_param_size = sizeof( p_sys->i_buffer_size );
310 err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
311 kAudioDevicePropertyBufferSize,
312 i_param_size, &p_sys->i_buffer_size );
315 msg_Err( p_aout, "failed to set buffer size: [%4.4s]",
317 FreeDevice( p_aout );
318 FreeHardwareInfo( p_aout );
319 vlc_mutex_destroy( &p_sys->lock );
320 free( (void *)p_sys );
321 return( VLC_EGENERIC );
324 msg_Dbg( p_aout, "device buffer size set to: [%ld]",
325 p_sys->i_buffer_size );
328 switch( p_sys->stream_format.mFormatID )
330 case kAudioFormatLinearPCM:
331 p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
333 switch( p_sys->stream_format.mChannelsPerFrame )
336 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
340 p_aout->output.output.i_physical_channels =
341 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
345 p_aout->output.output.i_physical_channels =
346 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
347 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
351 p_aout->output.output.i_physical_channels =
352 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
353 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
354 AOUT_CHAN_CENTER | AOUT_CHAN_LFE;
358 p_aout->output.output.i_physical_channels =
359 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
360 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
361 AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
362 AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT;
366 msg_Err( p_aout, "unknown channel count: [%ld]",
367 p_sys->stream_format.mChannelsPerFrame );
368 FreeDevice( p_aout );
369 FreeHardwareInfo( p_aout );
370 vlc_mutex_destroy( &p_sys->lock );
371 free( (void *)p_sys );
372 return( VLC_EGENERIC );
375 p_aout->output.i_nb_samples = (int)( p_sys->i_buffer_size /
376 p_sys->stream_format.mBytesPerFrame );
378 aout_VolumeSoftInit( p_aout );
382 case kAudioFormat60958AC3:
383 p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
384 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
385 p_aout->output.output.i_frame_length = A52_FRAME_NB;
386 p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
388 aout_VolumeNoneInit( p_aout );
392 msg_Err( p_aout, "unknown hardware format: [%4.4s]",
393 (char *)&p_sys->stream_format.mFormatID );
394 FreeDevice( p_aout );
395 FreeHardwareInfo( p_aout );
396 vlc_mutex_destroy( &p_sys->lock );
397 free( (void *)p_sys );
398 return( VLC_EGENERIC );
401 /* Set buffer frame size */
402 i_param_size = sizeof( p_aout->output.i_nb_samples );
403 err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
404 kAudioDevicePropertyBufferFrameSize,
406 &p_aout->output.i_nb_samples );
409 msg_Err( p_aout, "failed to set buffer frame size: [%4.4s]",
411 FreeDevice( p_aout );
412 FreeHardwareInfo( p_aout );
413 vlc_mutex_destroy( &p_sys->lock );
414 free( (void *)p_sys );
415 return( VLC_EGENERIC );
418 msg_Dbg( p_aout, "device buffer frame size set to: [%d]",
419 p_aout->output.i_nb_samples );
422 err = AudioDeviceAddIOProc( p_sys->devid,
423 (AudioDeviceIOProc)IOCallback,
427 msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]",
429 FreeDevice( p_aout );
430 FreeHardwareInfo( p_aout );
431 vlc_mutex_destroy( &p_sys->lock );
432 free( (void *)p_sys );
433 return( VLC_EGENERIC );
437 err = AudioDeviceStart( p_sys->devid, (AudioDeviceIOProc)IOCallback );
440 msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]",
443 err = AudioDeviceRemoveIOProc( p_sys->devid,
444 (AudioDeviceIOProc)IOCallback );
447 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
451 FreeDevice( p_aout );
452 FreeHardwareInfo( p_aout );
453 vlc_mutex_destroy( &p_sys->lock );
454 free( (void *)p_sys );
456 return( VLC_EGENERIC );
459 err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
464 msg_Err( p_aout, "AudioHardwareAddPropertyListener failed: %4.4s",
468 err = AudioDeviceStop( p_sys->devid,
469 (AudioDeviceIOProc)IOCallback );
472 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
476 /* Remove callback */
477 err = AudioDeviceRemoveIOProc( p_sys->devid,
478 (AudioDeviceIOProc)IOCallback );
481 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
485 FreeDevice( p_aout );
486 FreeHardwareInfo( p_aout );
487 vlc_mutex_destroy( &p_sys->lock );
488 free( (void *)p_sys );
490 return( VLC_EGENERIC );
493 /* Let's pray for the following operation to be atomic... */
494 p_sys->clock_diff = - (mtime_t)
495 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
496 p_sys->clock_diff += mdate();
498 return( VLC_SUCCESS );
501 /*****************************************************************************
502 * Close: close the CoreAudio HAL device
503 *****************************************************************************/
504 void E_(CloseAudio)( aout_instance_t * p_aout )
507 struct aout_sys_t * p_sys = p_aout->output.p_sys;
509 if( p_sys->b_dev_alive )
512 err = AudioDeviceStop( p_sys->devid,
513 (AudioDeviceIOProc)IOCallback );
516 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
520 /* Remove callback */
521 err = AudioDeviceRemoveIOProc( p_sys->devid,
522 (AudioDeviceIOProc)IOCallback );
525 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
529 FreeDevice( p_aout );
532 err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
536 msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]",
540 FreeHardwareInfo( p_aout );
542 vlc_mutex_destroy( &p_sys->lock );
547 /*****************************************************************************
548 * Play: nothing to do
549 *****************************************************************************/
550 static void Play( aout_instance_t * p_aout )
554 /*****************************************************************************
555 * IOCallback: callback for audio output
556 *****************************************************************************/
557 static OSStatus IOCallback( AudioDeviceID inDevice,
558 const AudioTimeStamp * inNow,
559 const void * inInputData,
560 const AudioTimeStamp * inInputTime,
561 AudioBufferList * outOutputData,
562 const AudioTimeStamp * inOutputTime,
563 void * threadGlobals )
565 aout_buffer_t * p_buffer;
566 AudioTimeStamp host_time;
567 mtime_t current_date;
569 aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
570 struct aout_sys_t * p_sys = p_aout->output.p_sys;
572 host_time.mFlags = kAudioTimeStampHostTimeValid;
573 AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
574 current_date = p_sys->clock_diff +
575 AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
577 #define B_SPDI (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i'))
578 p_buffer = aout_OutputNextBuffer( p_aout, current_date, B_SPDI );
581 if( p_buffer != NULL )
583 /* move data into output data buffer */
584 BlockMoveData( p_buffer->p_buffer,
585 outOutputData->mBuffers[ 0 ].mData,
586 p_sys->i_buffer_size );
588 aout_BufferFree( p_buffer );
592 if( p_aout->output.output.i_format == VLC_FOURCC('f','l','3','2') )
594 UInt32 i, i_size = p_sys->i_buffer_size / sizeof(float);
595 float * p = (float *)outOutputData->mBuffers[ 0 ].mData;
597 for( i = 0; i < i_size; i++ )
604 memset( outOutputData->mBuffers[ 0 ].mData,
605 0, p_sys->i_buffer_size );
612 /*****************************************************************************
614 *****************************************************************************/
615 static int InitHardwareInfo( aout_instance_t * p_aout )
618 UInt32 i, i_param_size;
619 AudioDeviceID devid_def;
620 AudioDeviceID * p_devices;
622 struct aout_sys_t * p_sys = p_aout->output.p_sys;
624 vlc_mutex_lock( &p_sys->lock );
626 /* Get number of devices */
627 err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
628 &i_param_size, NULL );
631 msg_Err( p_aout, "AudioHardwareGetPropertyInfo failed: [%4.4s]",
633 vlc_mutex_unlock( &p_sys->lock );
634 return( VLC_EGENERIC );
637 p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
639 if( p_sys->i_devices < 1 )
641 msg_Err( p_aout, "no devices found" );
642 vlc_mutex_unlock( &p_sys->lock );
643 return( VLC_EGENERIC );
646 msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
648 /* Allocate DeviceID array */
649 p_devices = (AudioDeviceID *)malloc( i_param_size );
650 if( p_devices == NULL )
652 msg_Err( p_aout, "out of memory" );
653 vlc_mutex_unlock( &p_sys->lock );
654 return( VLC_ENOMEM );
657 /* Populate DeviceID array */
658 err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
659 &i_param_size, (void *)p_devices );
662 msg_Err( p_aout, "AudioHardwareGetProperty failed: [%4.4s]",
664 free( (void *)p_devices );
665 vlc_mutex_unlock( &p_sys->lock );
666 return( VLC_EGENERIC );
669 i_param_size = sizeof( AudioDeviceID );
670 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
671 &i_param_size, (void *)&devid_def );
674 msg_Err( p_aout, "AudioHardwareGetProperty failed: [%4.4s]",
676 free( (void *)p_devices );
677 vlc_mutex_unlock( &p_sys->lock );
678 return( VLC_EGENERIC );
681 p_sys->p_devices = (struct aout_dev_t *)
682 malloc( sizeof( struct aout_dev_t ) * p_sys->i_devices );
683 if( p_sys->p_devices == NULL )
685 msg_Err( p_aout, "out of memory" );
686 free( (void *)p_devices );
687 vlc_mutex_unlock( &p_sys->lock );
688 return( VLC_ENOMEM );
691 p_sys->i_options = 0;
692 p_sys->p_options = NULL;
694 for( i = 0; i < p_sys->i_devices; i++ )
696 p_sys->p_devices[i].devid = p_devices[i];
698 if( p_devices[i] == devid_def )
700 p_sys->i_def_dev = i;
703 if( InitDeviceInfo( i, p_aout ) )
707 msg_Err( p_aout, "InitDeviceInfo(%ld) failed", i );
709 for( j = 0; j < i; j++ )
711 FreeDeviceInfo( j, p_aout );
714 free( (void *)p_sys->p_devices );
715 free( (void *)p_devices );
717 vlc_mutex_unlock( &p_sys->lock );
719 return( VLC_EGENERIC );
723 free( (void *)p_devices );
725 p_sys->b_hwinfo = VLC_TRUE;
727 vlc_mutex_unlock( &p_sys->lock );
729 return( VLC_SUCCESS );
732 /*****************************************************************************
734 *****************************************************************************/
735 static int InitDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
738 UInt32 i, i_param_size;
739 AudioBufferList * p_buffer_list;
741 struct aout_sys_t * p_sys = p_aout->output.p_sys;
742 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
744 /* Get length of device name */
745 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
746 kAudioDevicePropertyDeviceName,
747 &i_param_size, NULL );
750 msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
752 return( VLC_EGENERIC );
755 /* Allocate memory for device name */
756 p_dev->psz_device_name = (char *)malloc( i_param_size );
757 if( p_dev->psz_device_name == NULL )
759 msg_Err( p_aout, "out of memory" );
760 return( VLC_ENOMEM );
763 /* Get device name */
764 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
765 kAudioDevicePropertyDeviceName,
766 &i_param_size, p_dev->psz_device_name );
769 msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
771 free( (void *)p_dev->psz_device_name );
772 return( VLC_EGENERIC );
775 msg_Dbg( p_aout, "device [%ld] has name [%s]",
776 i_dev, p_dev->psz_device_name );
778 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
779 kAudioDevicePropertyStreamConfiguration,
780 &i_param_size, NULL );
783 msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
785 free( (void *)p_dev->psz_device_name );
786 return( VLC_EGENERIC );
789 p_buffer_list = (AudioBufferList *)malloc( i_param_size );
790 if( p_buffer_list == NULL )
792 msg_Err( p_aout, "out of memory" );
793 free( (void *)p_dev->psz_device_name );
794 return( VLC_ENOMEM );
797 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
798 kAudioDevicePropertyStreamConfiguration,
799 &i_param_size, p_buffer_list );
802 msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
804 free( (void *)p_dev->psz_device_name );
805 free( (void *)p_buffer_list );
806 return( VLC_EGENERIC );
809 p_dev->i_streams = p_buffer_list->mNumberBuffers;
810 free( (void *)p_buffer_list );
812 msg_Dbg( p_aout, "device [%ld] has [%ld] streams",
813 i_dev, p_dev->i_streams );
815 p_dev->pi_streams = (UInt32 *)malloc( p_dev->i_streams *
816 sizeof( *p_dev->pi_streams ) );
817 if( p_dev->pi_streams == NULL )
819 msg_Err( p_aout, "out of memory" );
820 free( (void *)p_dev->psz_device_name );
821 return( VLC_ENOMEM );
824 p_dev->pp_streams = (AudioStreamBasicDescription **)
825 malloc( p_dev->i_streams *
826 sizeof( *p_dev->pp_streams ) );
827 if( p_dev->pp_streams == NULL )
829 msg_Err( p_aout, "out of memory" );
830 free( (void *)p_dev->psz_device_name );
831 free( (void *)p_dev->pi_streams );
832 return( VLC_ENOMEM );
835 for( i = 0; i < p_dev->i_streams; i++ )
837 if( InitStreamInfo( i_dev, p_aout, i ) )
841 msg_Err( p_aout, "InitStreamInfo(%ld, %ld) failed", i_dev, i );
843 for( j = 0; j < i; j++ )
845 FreeStreamInfo( i_dev, p_aout, j );
848 free( (void *)p_dev->psz_device_name );
849 free( (void *)p_dev->pi_streams );
850 free( (void *)p_dev->pp_streams );
852 return( VLC_EGENERIC );
856 return( VLC_SUCCESS );
859 /*****************************************************************************
861 *****************************************************************************/
862 static void FreeDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
866 struct aout_sys_t * p_sys = p_aout->output.p_sys;
867 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
869 for( i = 0; i < p_dev->i_streams; i++ )
871 FreeStreamInfo( i_dev, p_aout, i );
874 free( (void *)p_dev->pp_streams );
875 free( (void *)p_dev->pi_streams );
876 free( (void *)p_dev->psz_device_name );
879 /*****************************************************************************
881 *****************************************************************************/
882 static void FreeHardwareInfo( aout_instance_t * p_aout )
886 struct aout_sys_t * p_sys = p_aout->output.p_sys;
888 vlc_mutex_lock( &p_sys->lock );
890 if( !p_sys->b_hwinfo )
892 vlc_mutex_unlock( &p_sys->lock );
896 for( i = 0; i < p_sys->i_devices; i++ )
898 FreeDeviceInfo( i, p_aout );
901 free( (void *)p_sys->p_options );
902 free( (void *)p_sys->p_devices );
904 p_sys->b_hwinfo = VLC_FALSE;
906 vlc_mutex_unlock( &p_sys->lock );
909 /*****************************************************************************
911 *****************************************************************************/
912 static int GetStreamID( AudioDeviceID devid, UInt32 i_idx,
913 AudioStreamID * p_sid )
917 AudioStreamID * p_stream_list;
919 err = AudioDeviceGetPropertyInfo( devid, 0, FALSE,
920 kAudioDevicePropertyStreams,
921 &i_param_size, NULL );
924 return( VLC_EGENERIC );
927 p_stream_list = (AudioStreamID *)malloc( i_param_size );
928 if( p_stream_list == NULL )
930 return( VLC_ENOMEM );
933 err = AudioDeviceGetProperty( devid, 0, FALSE,
934 kAudioDevicePropertyStreams,
935 &i_param_size, p_stream_list );
938 free( (void *)p_stream_list );
939 return( VLC_EGENERIC );
942 *p_sid = p_stream_list[i_idx - 1];
944 free( (void *)p_stream_list );
946 return( VLC_SUCCESS );
949 /*****************************************************************************
951 *****************************************************************************/
952 static int InitStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
957 UInt32 i, j, i_param_size;
959 struct aout_sys_t * p_sys = p_aout->output.p_sys;
960 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
962 if( GetStreamID( p_dev->devid, i_idx + 1, &i_sid ) )
964 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed", i_dev, i_idx );
965 return( VLC_EGENERIC );
968 err = AudioStreamGetPropertyInfo( i_sid, 0,
969 kAudioStreamPropertyPhysicalFormats,
970 &i_param_size, NULL );
973 msg_Err( p_aout, "AudioStreamGetPropertyInfo failed: [%4.4s]",
975 return( VLC_EGENERIC );
978 #define P_STREAMS p_dev->pp_streams[i_idx]
979 #define I_STREAMS p_dev->pi_streams[i_idx]
981 I_STREAMS = i_param_size / sizeof( AudioStreamBasicDescription );
983 P_STREAMS = (AudioStreamBasicDescription *)malloc( i_param_size );
984 if( P_STREAMS == NULL )
986 msg_Err( p_aout, "out of memory" );
987 return( VLC_ENOMEM );
990 memset( P_STREAMS, 0, i_param_size );
992 err = AudioStreamGetProperty( i_sid, 0,
993 kAudioStreamPropertyPhysicalFormats,
994 &i_param_size, P_STREAMS );
997 msg_Err( p_aout, "AudioStreamGetProperty failed: [%4.4s]",
999 free( (void *)P_STREAMS );
1000 return( VLC_EGENERIC );
1003 for( j = 0; j < N_AOUT_CLASSES; j++ )
1005 vlc_bool_t b_found = 0;
1007 for( i = 0; i < I_STREAMS; i++ )
1011 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format",
1015 if( ( P_STREAMS[i].mFormatID == 'IAC3' ||
1016 P_STREAMS[i].mFormatID == kAudioFormat60958AC3 ) &&
1017 !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
1022 if( ( P_STREAMS[i].mFormatID != aout_classes[j].mFormatID ) ||
1023 ( P_STREAMS[i].mChannelsPerFrame !=
1024 aout_classes[j].mChannelsPerFrame ) )
1035 p_sys->p_options = (struct aout_option_t *)
1036 realloc( p_sys->p_options,
1037 ( p_sys->i_options + 1 ) *
1038 sizeof( struct aout_option_t ) );
1039 if( p_sys->p_options == NULL )
1041 msg_Err( p_aout, "out of memory" );
1042 free( (void *)P_STREAMS );
1043 return( VLC_ENOMEM );
1046 #define AOUT_OPTION p_sys->p_options[p_sys->i_options]
1048 snprintf( AOUT_OPTION.sz_option,
1049 sizeof( AOUT_OPTION.sz_option ) /
1050 sizeof( AOUT_OPTION.sz_option[0] ) - 1,
1053 p_dev->psz_device_name,
1054 aout_classes[j].psz_class );
1056 AOUT_OPTION.i_sid = i_sid;
1057 AOUT_OPTION.i_dev = i_dev;
1058 AOUT_OPTION.i_idx = i_idx;
1059 AOUT_OPTION.i_sdx = i;
1060 AOUT_OPTION.i_cdx = j;
1071 return( VLC_SUCCESS );
1074 /*****************************************************************************
1076 *****************************************************************************/
1077 static void FreeStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
1080 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1081 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
1083 free( (void *)p_dev->pp_streams[i_idx] );
1086 /*****************************************************************************
1088 *****************************************************************************/
1089 static int InitDevice( aout_instance_t * p_aout )
1093 unsigned int i_option;
1094 vlc_bool_t b_found = VLC_FALSE;
1095 UInt32 i, i_stream, i_param_size;
1097 struct aout_dev_t * p_dev;
1098 struct aout_option_t * p_option;
1099 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1101 if( var_Get( p_aout, "audio-device", &val ) < 0 )
1103 msg_Err( p_aout, "audio-device var does not exist" );
1104 return( VLC_ENOVAR );
1107 if( !sscanf( val.psz_string, "%d:", &i_option ) ||
1108 p_sys->i_options <= i_option )
1113 free( (void *)val.psz_string );
1115 p_option = &p_sys->p_options[i_option];
1116 p_dev = &p_sys->p_devices[p_option->i_dev];
1118 msg_Dbg( p_aout, "getting device [%ld]", p_option->i_dev );
1120 i_param_size = sizeof( p_sys->b_dev_alive );
1121 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
1122 kAudioDevicePropertyDeviceIsAlive,
1123 &i_param_size, &p_sys->b_dev_alive );
1126 msg_Err( p_aout, "AudioDeviceGetProperty failed: %4.4s",
1128 return( VLC_EGENERIC );
1131 #define P_STREAMS p_dev->pp_streams[p_option->i_idx]
1132 #define I_STREAMS p_dev->pi_streams[p_option->i_idx]
1134 for( i = 0; i < I_STREAMS; i++ )
1136 if( P_STREAMS[i].mFormatID ==
1137 aout_classes[p_option->i_cdx].mFormatID &&
1138 P_STREAMS[i].mChannelsPerFrame ==
1139 aout_classes[p_option->i_cdx].mChannelsPerFrame &&
1140 P_STREAMS[i].mSampleRate == p_aout->output.output.i_rate )
1147 i_stream = b_found ? i : p_option->i_sdx;
1149 i_param_size = sizeof( p_sys->sfmt_revert );
1150 err = AudioStreamGetProperty( p_option->i_sid, 0,
1151 kAudioStreamPropertyPhysicalFormat,
1153 (void *)&p_sys->sfmt_revert );
1156 msg_Err( p_aout, "AudioStreamGetPropertyInfo failed: [%4.4s]",
1158 return( VLC_EGENERIC );
1161 if( memcmp( &P_STREAMS[i_stream], &p_sys->sfmt_revert,
1162 sizeof( p_sys->sfmt_revert ) ) != 0 )
1165 struct timespec timeout;
1166 struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
1168 vlc_cond_init( p_aout, &w.cond );
1169 vlc_mutex_init( p_aout, &w.lock );
1171 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "stream format",
1172 p_sys->sfmt_revert ) );
1174 err = AudioStreamAddPropertyListener( p_option->i_sid, 0,
1175 kAudioStreamPropertyPhysicalFormat,
1176 StreamListener, (void *)&w );
1179 msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]",
1181 vlc_mutex_destroy( &w.lock );
1182 vlc_cond_destroy( &w.cond );
1183 return( VLC_EGENERIC );
1186 vlc_mutex_lock( &w.lock );
1188 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting format",
1189 P_STREAMS[i_stream] ) );
1191 err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
1192 kAudioStreamPropertyPhysicalFormat,
1193 sizeof( P_STREAMS[i_stream] ),
1194 &P_STREAMS[i_stream] );
1197 msg_Err( p_aout, "AudioStreamSetProperty failed: [%4.4s]",
1199 vlc_mutex_unlock( &w.lock );
1200 vlc_mutex_destroy( &w.lock );
1201 vlc_cond_destroy( &w.cond );
1202 return( VLC_EGENERIC );
1205 gettimeofday( &now, NULL );
1206 timeout.tv_sec = now.tv_sec;
1207 timeout.tv_nsec = (now.tv_usec + 100000) * 1000;
1209 pthread_cond_timedwait( &w.cond.cond, &w.lock.mutex, &timeout );
1210 vlc_mutex_unlock( &w.lock );
1212 if( GetStreamID( p_dev->devid, p_option->i_idx + 1,
1213 &p_option->i_sid ) )
1215 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed",
1216 p_option->i_dev, p_option->i_idx );
1217 vlc_mutex_destroy( &w.lock );
1218 vlc_cond_destroy( &w.cond );
1219 return( VLC_EGENERIC );
1222 err = AudioStreamRemovePropertyListener( p_option->i_sid, 0,
1223 kAudioStreamPropertyPhysicalFormat, StreamListener );
1227 "AudioStreamRemovePropertyListener failed: [%4.4s]",
1229 vlc_mutex_destroy( &w.lock );
1230 vlc_cond_destroy( &w.cond );
1231 return( VLC_EGENERIC );
1234 vlc_mutex_destroy( &w.lock );
1235 vlc_cond_destroy( &w.cond );
1237 p_sys->b_revert_sfmt = VLC_TRUE;
1243 err = AudioDeviceAddPropertyListener( p_dev->devid, 0, FALSE,
1244 kAudioDevicePropertyDeviceIsAlive,
1245 DeviceListener, (void *)p_aout );
1248 msg_Err( p_aout, "AudioDeviceAddPropertyListener failed: [%4.4s]",
1250 return( VLC_EGENERIC );
1253 config_PutInt( p_aout, "macosx-adev", i_option );
1255 p_sys->i_sel_opt = i_option;
1256 p_sys->devid = p_dev->devid;
1258 return( VLC_SUCCESS );
1261 /*****************************************************************************
1263 *****************************************************************************/
1264 static void FreeDevice( aout_instance_t * p_aout )
1268 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1270 if( p_sys->b_revert_sfmt )
1272 struct aout_dev_t * p_dev;
1273 struct aout_option_t * p_option;
1275 p_option = &p_sys->p_options[p_sys->i_sel_opt];
1276 p_dev = &p_sys->p_devices[p_option->i_dev];
1278 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "reverting to format",
1279 p_sys->sfmt_revert ) );
1281 if( GetStreamID( p_dev->devid, p_option->i_idx + 1,
1282 &p_option->i_sid ) )
1284 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed",
1285 p_option->i_dev, p_option->i_idx );
1289 err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
1290 kAudioStreamPropertyPhysicalFormat,
1291 sizeof( p_sys->sfmt_revert ),
1292 &p_sys->sfmt_revert );
1295 msg_Err( p_aout, "AudioStreamSetProperty failed: [%4.4s]",
1301 err = AudioDeviceRemovePropertyListener( p_sys->devid, 0, FALSE,
1302 kAudioDevicePropertyDeviceIsAlive,
1306 msg_Err( p_aout, "AudioDeviceRemovePropertyListener failed: [%4.4s]",
1311 /*****************************************************************************
1313 *****************************************************************************/
1314 static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
1315 void * inClientData )
1317 OSStatus err = noErr;
1319 aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1320 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1322 switch( inPropertyID )
1324 case kAudioHardwarePropertyDevices:
1330 if( p_sys->b_dev_alive )
1332 i_idx = p_sys->p_options[p_sys->i_sel_opt].i_idx;
1333 i_sdx = p_sys->p_options[p_sys->i_sel_opt].i_sdx;
1336 FreeHardwareInfo( p_aout );
1338 if( InitHardwareInfo( p_aout ) )
1340 msg_Err( p_aout, "InitHardwareInfo failed" );
1344 if( p_sys->b_dev_alive )
1348 for( i = 0; i < p_sys->i_options; i++ )
1350 if( p_sys->p_devices[p_sys->p_options[i].i_dev].devid ==
1351 p_sys->devid && p_sys->p_options[i].i_idx == i_idx &&
1352 p_sys->p_options[i].i_sdx == i_sdx )
1360 var_Destroy( p_aout, "audio-device" );
1361 InitDeviceVar( p_aout, i_option, !p_sys->b_dev_alive );
1369 /*****************************************************************************
1371 *****************************************************************************/
1372 static OSStatus DeviceListener( AudioDeviceID inDevice,
1375 AudioDevicePropertyID inPropertyID,
1376 void *inClientData )
1378 UInt32 i_param_size;
1379 OSStatus err = noErr;
1381 aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1382 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1384 switch( inPropertyID )
1386 case kAudioDevicePropertyDeviceIsAlive:
1388 i_param_size = sizeof( p_sys->b_dev_alive );
1389 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
1390 kAudioDevicePropertyDeviceIsAlive,
1391 &i_param_size, &p_sys->b_dev_alive );
1394 msg_Err( p_aout, "AudioDeviceGetProperty failed: %4.4s",
1404 /*****************************************************************************
1406 *****************************************************************************/
1407 static OSStatus StreamListener( AudioStreamID inStream,
1409 AudioDevicePropertyID inPropertyID,
1410 void * inClientData )
1412 OSStatus err = noErr;
1414 struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1416 switch( inPropertyID )
1418 case kAudioStreamPropertyPhysicalFormat:
1419 vlc_mutex_lock( &w->lock );
1420 vlc_cond_signal( &w->cond );
1421 vlc_mutex_unlock( &w->lock );
1431 /*****************************************************************************
1433 *****************************************************************************/
1434 static void InitDeviceVar( aout_instance_t * p_aout, int i_option,
1435 vlc_bool_t b_change )
1440 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1442 if( i_option == -1 || i_option >= (int)p_sys->i_options )
1444 for( i = 0; i < p_sys->i_options; i++ )
1446 if( p_sys->p_options[i].i_dev == p_sys->i_def_dev )
1454 var_Create( p_aout, "audio-device", VLC_VAR_STRING |
1455 VLC_VAR_HASCHOICE );
1457 for( i = 0; i < p_sys->i_options; i++ )
1459 val.psz_string = p_sys->p_options[i].sz_option;
1460 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
1462 if( !b_change && i == (UInt32)i_option )
1464 p_sys->i_sel_opt = i;
1465 var_Set( p_aout, "audio-device", val );
1466 config_PutInt( p_aout, "macosx-adev", i_option );
1470 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
1475 val.psz_string = p_sys->p_options[i_option].sz_option;
1476 var_Set( p_aout, "audio-device", val );
1479 val.b_bool = VLC_TRUE;
1480 var_Set( p_aout, "intf-change", val );