1 /*****************************************************************************
2 * aout.m: CoreAudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2002-2003 VideoLAN
5 * $Id: aout.m,v 1.24 2003/02/23 05:53:53 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 AudioDeviceClassPCM2 = 1 << 1,
58 AudioDeviceClassPCM4 = 1 << 2,
59 AudioDeviceClassPCM6 = 1 << 3,
60 AudioDeviceClassPCM8 = 1 << 4
63 static struct aout_class_t
66 UInt32 mChannelsPerFrame;
67 enum AudioDeviceClass class;
68 const char * psz_class;
72 { /* old A/52 format type */
79 { /* new A/52 format type */
87 kAudioFormatLinearPCM,
94 kAudioFormatLinearPCM,
101 kAudioFormatLinearPCM,
103 AudioDeviceClassPCM6,
108 kAudioFormatLinearPCM,
110 AudioDeviceClassPCM8,
115 #define N_AOUT_CLASSES (sizeof(aout_classes)/sizeof(aout_classes[0]))
117 /*****************************************************************************
119 ****************************************************************************/
128 /*****************************************************************************
130 ****************************************************************************/
134 char * psz_device_name;
137 AudioStreamBasicDescription ** pp_streams;
140 /*****************************************************************************
141 * aout_sys_t: private audio output method descriptor
142 *****************************************************************************
143 * This structure is part of the audio output thread descriptor.
144 * It describes the CoreAudio specific properties of an output thread.
145 *****************************************************************************/
152 struct aout_dev_t * p_devices;
155 struct aout_option_t * p_options;
158 AudioStreamBasicDescription stream_format;
161 UInt32 i_buffer_size;
165 /*****************************************************************************
167 *****************************************************************************/
168 static int InitHardwareInfo ( aout_instance_t * p_aout );
169 static int InitDeviceInfo ( UInt32 i_dev, aout_instance_t * p_aout );
170 static void FreeDeviceInfo ( UInt32 i_dev, aout_instance_t * p_aout );
171 static void FreeHardwareInfo ( aout_instance_t * p_aout );
172 static int InitDevice ( aout_instance_t * p_aout );
173 static void FreeDevice ( aout_instance_t * p_aout );
174 static int GetStreamID ( AudioDeviceID devid, UInt32 i_idx,
175 AudioStreamID * p_sid );
176 static int InitStreamInfo ( UInt32 i_dev, aout_instance_t * p_aout,
178 static void FreeStreamInfo ( UInt32 i_dev, aout_instance_t * p_aout,
180 static void InitDeviceVar ( aout_instance_t * p_aout, int i_option,
181 vlc_bool_t b_change );
183 static void Play ( aout_instance_t * p_aout );
185 static OSStatus IOCallback ( AudioDeviceID inDevice,
186 const AudioTimeStamp * inNow,
187 const void * inInputData,
188 const AudioTimeStamp * inInputTime,
189 AudioBufferList * outOutputData,
190 const AudioTimeStamp * inOutputTime,
191 void * threadGlobals );
193 static OSStatus HardwareListener ( AudioHardwarePropertyID inPropertyID,
194 void * inClientData );
196 static OSStatus DeviceListener ( AudioDeviceID inDevice,
199 AudioDevicePropertyID inPropertyID,
200 void * inClientData );
202 static OSStatus StreamListener ( AudioStreamID inStream,
204 AudioDevicePropertyID inPropertyID,
205 void * inClientData );
207 /*****************************************************************************
208 * Open: open a CoreAudio HAL device
209 *****************************************************************************/
210 int E_(OpenAudio)( vlc_object_t * p_this )
214 struct aout_sys_t * p_sys;
215 aout_instance_t * p_aout = (aout_instance_t *)p_this;
217 /* Allocate structure */
218 p_sys = (struct aout_sys_t *)malloc( sizeof( struct aout_sys_t ) );
221 msg_Err( p_aout, "out of memory" );
222 return( VLC_ENOMEM );
225 memset( p_sys, 0, sizeof( struct aout_sys_t ) );
227 p_aout->output.p_sys = p_sys;
228 p_aout->output.pf_play = Play;
230 vlc_mutex_init( p_aout, &p_sys->lock );
232 if( InitHardwareInfo( p_aout ) )
234 msg_Err( p_aout, "InitHardwareInfo failed" );
235 vlc_mutex_destroy( &p_sys->lock );
236 free( (void *)p_sys );
237 return( VLC_EGENERIC );
240 if( var_Type( p_aout, "audio-device" ) == 0 )
242 InitDeviceVar( p_aout, config_GetInt( p_aout, "macosx-adev" ),
246 if( InitDevice( p_aout ) )
248 msg_Err( p_aout, "InitDevice failed" );
249 FreeHardwareInfo( p_aout );
250 vlc_mutex_destroy( &p_sys->lock );
251 free( (void *)p_sys );
252 return( VLC_EGENERIC );
255 /* Get a description of the stream format */
256 i_param_size = sizeof( AudioStreamBasicDescription );
257 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
258 kAudioDevicePropertyStreamFormat,
259 &i_param_size, &p_sys->stream_format );
262 msg_Err( p_aout, "failed to get stream format: [%4.4s]",
264 FreeDevice( p_aout );
265 FreeHardwareInfo( p_aout );
266 vlc_mutex_destroy( &p_sys->lock );
267 free( (void *)p_sys );
268 return( VLC_EGENERIC );
271 /* Set the output sample rate */
272 p_aout->output.output.i_rate =
273 (unsigned int)p_sys->stream_format.mSampleRate;
275 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "using format",
276 p_sys->stream_format ) );
278 /* Get the buffer size */
279 i_param_size = sizeof( p_sys->i_buffer_size );
280 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
281 kAudioDevicePropertyBufferSize,
282 &i_param_size, &p_sys->i_buffer_size );
285 msg_Err( p_aout, "failed to get buffer size: [%4.4s]",
287 FreeDevice( p_aout );
288 FreeHardwareInfo( p_aout );
289 vlc_mutex_destroy( &p_sys->lock );
290 free( (void *)p_sys );
291 return( VLC_EGENERIC );
294 msg_Dbg( p_aout, "device buffer size: [%ld]", p_sys->i_buffer_size );
296 /* If we do AC3 over SPDIF, set buffer size to one AC3 frame */
297 if( ( p_sys->stream_format.mFormatID == kAudioFormat60958AC3 ||
298 p_sys->stream_format.mFormatID == 'IAC3' ) &&
299 p_sys->i_buffer_size != AOUT_SPDIF_SIZE )
301 p_sys->i_buffer_size = AOUT_SPDIF_SIZE;
302 i_param_size = sizeof( p_sys->i_buffer_size );
303 err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
304 kAudioDevicePropertyBufferSize,
305 i_param_size, &p_sys->i_buffer_size );
308 msg_Err( p_aout, "failed to set buffer size: [%4.4s]",
310 FreeDevice( p_aout );
311 FreeHardwareInfo( p_aout );
312 vlc_mutex_destroy( &p_sys->lock );
313 free( (void *)p_sys );
314 return( VLC_EGENERIC );
317 msg_Dbg( p_aout, "device buffer size set to: [%ld]",
318 p_sys->i_buffer_size );
321 switch( p_sys->stream_format.mFormatID )
323 case kAudioFormatLinearPCM:
324 p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
326 switch( p_sys->stream_format.mChannelsPerFrame )
329 p_aout->output.output.i_physical_channels =
330 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
334 p_aout->output.output.i_physical_channels =
335 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
336 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
340 p_aout->output.output.i_physical_channels =
341 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
342 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
343 AOUT_CHAN_CENTER | AOUT_CHAN_LFE;
347 p_aout->output.output.i_physical_channels =
348 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
349 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
350 AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
351 AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT;
355 msg_Err( p_aout, "unknown channel count: [%ld]",
356 p_sys->stream_format.mChannelsPerFrame );
357 FreeDevice( p_aout );
358 FreeHardwareInfo( p_aout );
359 vlc_mutex_destroy( &p_sys->lock );
360 free( (void *)p_sys );
361 return( VLC_EGENERIC );
364 p_aout->output.i_nb_samples = (int)( p_sys->i_buffer_size /
365 p_sys->stream_format.mBytesPerFrame );
367 aout_VolumeSoftInit( p_aout );
371 case kAudioFormat60958AC3:
372 p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
373 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
374 p_aout->output.output.i_frame_length = A52_FRAME_NB;
375 p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
377 aout_VolumeNoneInit( p_aout );
381 msg_Err( p_aout, "unknown hardware format: [%4.4s]",
382 (char *)&p_sys->stream_format.mFormatID );
383 FreeDevice( p_aout );
384 FreeHardwareInfo( p_aout );
385 vlc_mutex_destroy( &p_sys->lock );
386 free( (void *)p_sys );
387 return( VLC_EGENERIC );
390 /* Set buffer frame size */
391 i_param_size = sizeof( p_aout->output.i_nb_samples );
392 err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
393 kAudioDevicePropertyBufferFrameSize,
395 &p_aout->output.i_nb_samples );
398 msg_Err( p_aout, "failed to set buffer frame size: [%4.4s]",
400 FreeDevice( p_aout );
401 FreeHardwareInfo( p_aout );
402 vlc_mutex_destroy( &p_sys->lock );
403 free( (void *)p_sys );
404 return( VLC_EGENERIC );
407 msg_Dbg( p_aout, "device buffer frame size set to: [%d]",
408 p_aout->output.i_nb_samples );
411 err = AudioDeviceAddIOProc( p_sys->devid,
412 (AudioDeviceIOProc)IOCallback,
416 msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]",
418 FreeDevice( p_aout );
419 FreeHardwareInfo( p_aout );
420 vlc_mutex_destroy( &p_sys->lock );
421 free( (void *)p_sys );
422 return( VLC_EGENERIC );
426 err = AudioDeviceStart( p_sys->devid, (AudioDeviceIOProc)IOCallback );
429 msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]",
432 err = AudioDeviceRemoveIOProc( p_sys->devid,
433 (AudioDeviceIOProc)IOCallback );
436 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
440 FreeDevice( p_aout );
441 FreeHardwareInfo( p_aout );
442 vlc_mutex_destroy( &p_sys->lock );
443 free( (void *)p_sys );
445 return( VLC_EGENERIC );
448 err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
453 msg_Err( p_aout, "AudioHardwareAddPropertyListener failed: %4.4s",
457 err = AudioDeviceStop( p_sys->devid,
458 (AudioDeviceIOProc)IOCallback );
461 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
465 /* Remove callback */
466 err = AudioDeviceRemoveIOProc( p_sys->devid,
467 (AudioDeviceIOProc)IOCallback );
470 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
474 FreeDevice( p_aout );
475 FreeHardwareInfo( p_aout );
476 vlc_mutex_destroy( &p_sys->lock );
477 free( (void *)p_sys );
479 return( VLC_EGENERIC );
482 /* Let's pray for the following operation to be atomic... */
483 p_sys->clock_diff = - (mtime_t)
484 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
485 p_sys->clock_diff += mdate();
487 return( VLC_SUCCESS );
490 /*****************************************************************************
491 * Close: close the CoreAudio HAL device
492 *****************************************************************************/
493 void E_(CloseAudio)( aout_instance_t * p_aout )
496 struct aout_sys_t * p_sys = p_aout->output.p_sys;
498 if( p_sys->b_dev_alive )
501 err = AudioDeviceStop( p_sys->devid,
502 (AudioDeviceIOProc)IOCallback );
505 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
509 /* Remove callback */
510 err = AudioDeviceRemoveIOProc( p_sys->devid,
511 (AudioDeviceIOProc)IOCallback );
514 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
518 FreeDevice( p_aout );
521 err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
525 msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]",
529 FreeHardwareInfo( p_aout );
531 vlc_mutex_destroy( &p_sys->lock );
536 /*****************************************************************************
537 * Play: nothing to do
538 *****************************************************************************/
539 static void Play( aout_instance_t * p_aout )
543 /*****************************************************************************
544 * IOCallback: callback for audio output
545 *****************************************************************************/
546 static OSStatus IOCallback( AudioDeviceID inDevice,
547 const AudioTimeStamp * inNow,
548 const void * inInputData,
549 const AudioTimeStamp * inInputTime,
550 AudioBufferList * outOutputData,
551 const AudioTimeStamp * inOutputTime,
552 void * threadGlobals )
554 aout_buffer_t * p_buffer;
555 AudioTimeStamp host_time;
556 mtime_t current_date;
558 aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
559 struct aout_sys_t * p_sys = p_aout->output.p_sys;
561 host_time.mFlags = kAudioTimeStampHostTimeValid;
562 AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
563 current_date = p_sys->clock_diff +
564 AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
566 #define B_SPDI (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i'))
567 p_buffer = aout_OutputNextBuffer( p_aout, current_date, B_SPDI );
570 if( p_buffer != NULL )
572 /* move data into output data buffer */
573 BlockMoveData( p_buffer->p_buffer,
574 outOutputData->mBuffers[ 0 ].mData,
575 p_sys->i_buffer_size );
577 aout_BufferFree( p_buffer );
581 if( p_aout->output.output.i_format == VLC_FOURCC('f','l','3','2') )
583 UInt32 i, i_size = p_sys->i_buffer_size / sizeof(float);
584 float * p = (float *)outOutputData->mBuffers[ 0 ].mData;
586 for( i = 0; i < i_size; i++ )
593 memset( outOutputData->mBuffers[ 0 ].mData,
594 0, p_sys->i_buffer_size );
601 /*****************************************************************************
603 *****************************************************************************/
604 static int InitHardwareInfo( aout_instance_t * p_aout )
607 UInt32 i, i_param_size;
608 AudioDeviceID devid_def;
609 AudioDeviceID * p_devices;
611 struct aout_sys_t * p_sys = p_aout->output.p_sys;
613 vlc_mutex_lock( &p_sys->lock );
615 /* Get number of devices */
616 err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
617 &i_param_size, NULL );
620 msg_Err( p_aout, "AudioHardwareGetPropertyInfo failed: [%4.4s]",
622 vlc_mutex_unlock( &p_sys->lock );
623 return( VLC_EGENERIC );
626 p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
628 if( p_sys->i_devices < 1 )
630 msg_Err( p_aout, "no devices found" );
631 vlc_mutex_unlock( &p_sys->lock );
632 return( VLC_EGENERIC );
635 msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
637 /* Allocate DeviceID array */
638 p_devices = (AudioDeviceID *)malloc( i_param_size );
639 if( p_devices == NULL )
641 msg_Err( p_aout, "out of memory" );
642 vlc_mutex_unlock( &p_sys->lock );
643 return( VLC_ENOMEM );
646 /* Populate DeviceID array */
647 err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
648 &i_param_size, (void *)p_devices );
651 msg_Err( p_aout, "AudioHardwareGetProperty failed: [%4.4s]",
653 free( (void *)p_devices );
654 vlc_mutex_unlock( &p_sys->lock );
655 return( VLC_EGENERIC );
658 i_param_size = sizeof( AudioDeviceID );
659 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
660 &i_param_size, (void *)&devid_def );
663 msg_Err( p_aout, "AudioHardwareGetProperty failed: [%4.4s]",
665 free( (void *)p_devices );
666 vlc_mutex_unlock( &p_sys->lock );
667 return( VLC_EGENERIC );
670 p_sys->p_devices = (struct aout_dev_t *)
671 malloc( sizeof( struct aout_dev_t ) * p_sys->i_devices );
672 if( p_sys->p_devices == NULL )
674 msg_Err( p_aout, "out of memory" );
675 free( (void *)p_devices );
676 vlc_mutex_unlock( &p_sys->lock );
677 return( VLC_ENOMEM );
680 p_sys->i_options = 0;
681 p_sys->p_options = NULL;
683 for( i = 0; i < p_sys->i_devices; i++ )
685 p_sys->p_devices[i].devid = p_devices[i];
687 if( p_devices[i] == devid_def )
689 p_sys->i_def_dev = i;
692 if( InitDeviceInfo( i, p_aout ) )
696 msg_Err( p_aout, "InitDeviceInfo(%ld) failed", i );
698 for( j = 0; j < i; j++ )
700 FreeDeviceInfo( j, p_aout );
703 free( (void *)p_sys->p_devices );
704 free( (void *)p_devices );
706 vlc_mutex_unlock( &p_sys->lock );
708 return( VLC_EGENERIC );
712 free( (void *)p_devices );
714 p_sys->b_hwinfo = VLC_TRUE;
716 vlc_mutex_unlock( &p_sys->lock );
718 return( VLC_SUCCESS );
721 /*****************************************************************************
723 *****************************************************************************/
724 static int InitDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
727 UInt32 i, i_param_size;
728 AudioBufferList * p_buffer_list;
730 struct aout_sys_t * p_sys = p_aout->output.p_sys;
731 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
733 /* Get length of device name */
734 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
735 kAudioDevicePropertyDeviceName,
736 &i_param_size, NULL );
739 msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
741 return( VLC_EGENERIC );
744 /* Allocate memory for device name */
745 p_dev->psz_device_name = (char *)malloc( i_param_size );
746 if( p_dev->psz_device_name == NULL )
748 msg_Err( p_aout, "out of memory" );
749 return( VLC_ENOMEM );
752 /* Get device name */
753 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
754 kAudioDevicePropertyDeviceName,
755 &i_param_size, p_dev->psz_device_name );
758 msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
760 free( (void *)p_dev->psz_device_name );
761 return( VLC_EGENERIC );
764 msg_Dbg( p_aout, "device [%ld] has name [%s]",
765 i_dev, p_dev->psz_device_name );
767 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
768 kAudioDevicePropertyStreamConfiguration,
769 &i_param_size, NULL );
772 msg_Err( p_aout, "AudioDeviceGetPropertyInfo failed: [%4.4s]",
774 free( (void *)p_dev->psz_device_name );
775 return( VLC_EGENERIC );
778 p_buffer_list = (AudioBufferList *)malloc( i_param_size );
779 if( p_buffer_list == NULL )
781 msg_Err( p_aout, "out of memory" );
782 free( (void *)p_dev->psz_device_name );
783 return( VLC_ENOMEM );
786 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
787 kAudioDevicePropertyStreamConfiguration,
788 &i_param_size, p_buffer_list );
791 msg_Err( p_aout, "AudioDeviceGetProperty failed: [%4.4s]",
793 free( (void *)p_dev->psz_device_name );
794 free( (void *)p_buffer_list );
795 return( VLC_EGENERIC );
798 p_dev->i_streams = p_buffer_list->mNumberBuffers;
799 free( (void *)p_buffer_list );
801 msg_Dbg( p_aout, "device [%ld] has [%ld] streams",
802 i_dev, p_dev->i_streams );
804 p_dev->pi_streams = (UInt32 *)malloc( p_dev->i_streams *
805 sizeof( *p_dev->pi_streams ) );
806 if( p_dev->pi_streams == NULL )
808 msg_Err( p_aout, "out of memory" );
809 free( (void *)p_dev->psz_device_name );
810 return( VLC_ENOMEM );
813 p_dev->pp_streams = (AudioStreamBasicDescription **)
814 malloc( p_dev->i_streams *
815 sizeof( *p_dev->pp_streams ) );
816 if( p_dev->pp_streams == NULL )
818 msg_Err( p_aout, "out of memory" );
819 free( (void *)p_dev->psz_device_name );
820 free( (void *)p_dev->pi_streams );
821 return( VLC_ENOMEM );
824 for( i = 0; i < p_dev->i_streams; i++ )
826 if( InitStreamInfo( i_dev, p_aout, i ) )
830 msg_Err( p_aout, "InitStreamInfo(%ld, %ld) failed", i_dev, i );
832 for( j = 0; j < i; j++ )
834 FreeStreamInfo( i_dev, p_aout, j );
837 free( (void *)p_dev->psz_device_name );
838 free( (void *)p_dev->pi_streams );
839 free( (void *)p_dev->pp_streams );
841 return( VLC_EGENERIC );
845 return( VLC_SUCCESS );
848 /*****************************************************************************
850 *****************************************************************************/
851 static void FreeDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
855 struct aout_sys_t * p_sys = p_aout->output.p_sys;
856 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
858 for( i = 0; i < p_dev->i_streams; i++ )
860 FreeStreamInfo( i_dev, p_aout, i );
863 free( (void *)p_dev->pp_streams );
864 free( (void *)p_dev->pi_streams );
865 free( (void *)p_dev->psz_device_name );
868 /*****************************************************************************
870 *****************************************************************************/
871 static void FreeHardwareInfo( aout_instance_t * p_aout )
875 struct aout_sys_t * p_sys = p_aout->output.p_sys;
877 vlc_mutex_lock( &p_sys->lock );
879 if( !p_sys->b_hwinfo )
881 vlc_mutex_unlock( &p_sys->lock );
885 for( i = 0; i < p_sys->i_devices; i++ )
887 FreeDeviceInfo( i, p_aout );
890 free( (void *)p_sys->p_options );
891 free( (void *)p_sys->p_devices );
893 p_sys->b_hwinfo = VLC_FALSE;
895 vlc_mutex_unlock( &p_sys->lock );
898 /*****************************************************************************
900 *****************************************************************************/
901 static int GetStreamID( AudioDeviceID devid, UInt32 i_idx,
902 AudioStreamID * p_sid )
906 AudioStreamID * p_stream_list;
908 err = AudioDeviceGetPropertyInfo( devid, 0, FALSE,
909 kAudioDevicePropertyStreams,
910 &i_param_size, NULL );
913 return( VLC_EGENERIC );
916 p_stream_list = (AudioStreamID *)malloc( i_param_size );
917 if( p_stream_list == NULL )
919 return( VLC_ENOMEM );
922 err = AudioDeviceGetProperty( devid, 0, FALSE,
923 kAudioDevicePropertyStreams,
924 &i_param_size, p_stream_list );
927 free( (void *)p_stream_list );
928 return( VLC_EGENERIC );
931 *p_sid = p_stream_list[i_idx - 1];
933 free( (void *)p_stream_list );
935 return( VLC_SUCCESS );
938 /*****************************************************************************
940 *****************************************************************************/
941 static int InitStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
946 UInt32 i, j, i_param_size;
948 struct aout_sys_t * p_sys = p_aout->output.p_sys;
949 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
951 if( GetStreamID( p_dev->devid, i_idx + 1, &i_sid ) )
953 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed", i_dev, i_idx );
954 return( VLC_EGENERIC );
957 err = AudioStreamGetPropertyInfo( i_sid, 0,
958 kAudioStreamPropertyPhysicalFormats,
959 &i_param_size, NULL );
962 msg_Err( p_aout, "AudioStreamGetPropertyInfo failed: [%4.4s]",
964 return( VLC_EGENERIC );
967 #define P_STREAMS p_dev->pp_streams[i_idx]
968 #define I_STREAMS p_dev->pi_streams[i_idx]
970 I_STREAMS = i_param_size / sizeof( AudioStreamBasicDescription );
972 P_STREAMS = (AudioStreamBasicDescription *)malloc( i_param_size );
973 if( P_STREAMS == NULL )
975 msg_Err( p_aout, "out of memory" );
976 return( VLC_ENOMEM );
979 memset( P_STREAMS, 0, i_param_size );
981 err = AudioStreamGetProperty( i_sid, 0,
982 kAudioStreamPropertyPhysicalFormats,
983 &i_param_size, P_STREAMS );
986 msg_Err( p_aout, "AudioStreamGetProperty failed: [%4.4s]",
988 free( (void *)P_STREAMS );
989 return( VLC_EGENERIC );
992 for( j = 0; j < N_AOUT_CLASSES; j++ )
994 vlc_bool_t b_found = 0;
996 for( i = 0; i < I_STREAMS; i++ )
1000 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format",
1004 if( ( P_STREAMS[i].mFormatID == 'IAC3' ||
1005 P_STREAMS[i].mFormatID == kAudioFormat60958AC3 ) &&
1006 !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
1011 if( ( P_STREAMS[i].mFormatID != aout_classes[j].mFormatID ) ||
1012 ( P_STREAMS[i].mChannelsPerFrame !=
1013 aout_classes[j].mChannelsPerFrame ) )
1024 p_sys->p_options = (struct aout_option_t *)
1025 realloc( p_sys->p_options,
1026 ( p_sys->i_options + 1 ) *
1027 sizeof( struct aout_option_t ) );
1028 if( p_sys->p_options == NULL )
1030 msg_Err( p_aout, "out of memory" );
1031 free( (void *)P_STREAMS );
1032 return( VLC_ENOMEM );
1035 #define AOUT_OPTION p_sys->p_options[p_sys->i_options]
1037 snprintf( AOUT_OPTION.sz_option,
1038 sizeof( AOUT_OPTION.sz_option ) /
1039 sizeof( AOUT_OPTION.sz_option[0] ) - 1,
1042 p_dev->psz_device_name,
1043 aout_classes[j].psz_class );
1045 AOUT_OPTION.i_sid = i_sid;
1046 AOUT_OPTION.i_dev = i_dev;
1047 AOUT_OPTION.i_idx = i_idx;
1048 AOUT_OPTION.i_sdx = i;
1049 AOUT_OPTION.i_cdx = j;
1060 return( VLC_SUCCESS );
1063 /*****************************************************************************
1065 *****************************************************************************/
1066 static void FreeStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
1069 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1070 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
1072 free( (void *)p_dev->pp_streams[i_idx] );
1075 /*****************************************************************************
1077 *****************************************************************************/
1078 static int InitDevice( aout_instance_t * p_aout )
1082 unsigned int i_option;
1083 vlc_bool_t b_found = VLC_FALSE;
1084 UInt32 i, i_stream, i_param_size;
1085 AudioStreamBasicDescription desc;
1087 struct aout_dev_t * p_dev;
1088 struct aout_option_t * p_option;
1089 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1091 if( var_Get( p_aout, "audio-device", &val ) < 0 )
1093 msg_Err( p_aout, "audio-device var does not exist" );
1094 return( VLC_ENOVAR );
1097 if( !sscanf( val.psz_string, "%d:", &i_option ) ||
1098 p_sys->i_options <= i_option )
1103 free( (void *)val.psz_string );
1105 p_option = &p_sys->p_options[i_option];
1106 p_dev = &p_sys->p_devices[p_option->i_dev];
1108 msg_Dbg( p_aout, "getting device [%ld]", p_option->i_dev );
1110 i_param_size = sizeof( p_sys->b_dev_alive );
1111 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
1112 kAudioDevicePropertyDeviceIsAlive,
1113 &i_param_size, &p_sys->b_dev_alive );
1116 msg_Err( p_aout, "AudioDeviceGetProperty failed: %4.4s",
1118 return( VLC_EGENERIC );
1121 #define P_STREAMS p_dev->pp_streams[p_option->i_idx]
1122 #define I_STREAMS p_dev->pi_streams[p_option->i_idx]
1124 for( i = 0; i < I_STREAMS; i++ )
1126 if( P_STREAMS[i].mFormatID ==
1127 aout_classes[p_option->i_cdx].mFormatID &&
1128 P_STREAMS[i].mSampleRate == p_aout->output.output.i_rate )
1135 i_stream = b_found ? i : p_option->i_sdx;
1137 i_param_size = sizeof( desc );
1138 memset( &desc, 0, i_param_size );
1139 err = AudioStreamGetProperty( p_option->i_sid, 0,
1140 kAudioStreamPropertyPhysicalFormat,
1141 &i_param_size, (void *)&desc );
1144 msg_Err( p_aout, "AudioStreamGetPropertyInfo failed: [%4.4s]",
1146 return( VLC_EGENERIC );
1149 if( memcmp( &P_STREAMS[i_stream], &desc, sizeof( desc ) ) != 0 )
1151 struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
1153 vlc_cond_init( p_aout, &w.cond );
1154 vlc_mutex_init( p_aout, &w.lock );
1156 err = AudioStreamAddPropertyListener( p_option->i_sid, 0,
1157 kAudioStreamPropertyPhysicalFormat,
1158 StreamListener, (void *)&w );
1161 msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]",
1163 vlc_mutex_destroy( &w.lock );
1164 vlc_cond_destroy( &w.cond );
1165 return( VLC_EGENERIC );
1168 vlc_mutex_lock( &w.lock );
1170 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting format",
1171 P_STREAMS[i_stream] ) );
1173 err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
1174 kAudioStreamPropertyPhysicalFormat,
1175 sizeof( P_STREAMS[i_stream] ),
1176 &P_STREAMS[i_stream] );
1179 msg_Err( p_aout, "AudioStreamSetProperty failed: [%4.4s]",
1181 vlc_mutex_unlock( &w.lock );
1182 vlc_mutex_destroy( &w.lock );
1183 vlc_cond_destroy( &w.cond );
1184 return( VLC_EGENERIC );
1187 vlc_cond_wait( &w.cond, &w.lock );
1188 vlc_mutex_unlock( &w.lock );
1190 vlc_mutex_destroy( &w.lock );
1191 vlc_cond_destroy( &w.cond );
1193 if( GetStreamID( p_dev->devid, p_option->i_idx + 1,
1194 &p_option->i_sid ) )
1196 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed",
1197 p_option->i_dev, p_option->i_idx );
1198 return( VLC_EGENERIC );
1201 err = AudioStreamRemovePropertyListener( p_option->i_sid, 0,
1202 kAudioStreamPropertyPhysicalFormat, StreamListener );
1206 "AudioStreamRemovePropertyListener failed: [%4.4s]",
1209 return( VLC_EGENERIC );
1216 err = AudioDeviceAddPropertyListener( p_dev->devid, 0, FALSE,
1217 kAudioDevicePropertyDeviceIsAlive,
1218 DeviceListener, (void *)p_aout );
1221 msg_Err( p_aout, "AudioDeviceAddPropertyListener failed: [%4.4s]",
1223 return( VLC_EGENERIC );
1226 config_PutInt( p_aout, "macosx-adev", i_option );
1228 p_sys->i_sel_opt = i_option;
1229 p_sys->devid = p_dev->devid;
1231 return( VLC_SUCCESS );
1234 /*****************************************************************************
1236 *****************************************************************************/
1237 static void FreeDevice( aout_instance_t * p_aout )
1241 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1243 err = AudioDeviceRemovePropertyListener( p_sys->devid, 0, FALSE,
1244 kAudioDevicePropertyDeviceIsAlive,
1248 msg_Err( p_aout, "AudioDeviceRemovePropertyListener failed: [%4.4s]",
1253 /*****************************************************************************
1255 *****************************************************************************/
1256 static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
1257 void * inClientData )
1259 OSStatus err = noErr;
1261 aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1262 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1264 switch( inPropertyID )
1266 case kAudioHardwarePropertyDevices:
1272 if( p_sys->b_dev_alive )
1274 i_idx = p_sys->p_options[p_sys->i_sel_opt].i_idx;
1275 i_sdx = p_sys->p_options[p_sys->i_sel_opt].i_sdx;
1278 FreeHardwareInfo( p_aout );
1280 if( InitHardwareInfo( p_aout ) )
1282 msg_Err( p_aout, "InitHardwareInfo failed" );
1286 if( p_sys->b_dev_alive )
1290 for( i = 0; i < p_sys->i_options; i++ )
1292 if( p_sys->p_devices[p_sys->p_options[i].i_dev].devid ==
1293 p_sys->devid && p_sys->p_options[i].i_idx == i_idx &&
1294 p_sys->p_options[i].i_sdx == i_sdx )
1302 var_Destroy( p_aout, "audio-device" );
1303 InitDeviceVar( p_aout, i_option, !p_sys->b_dev_alive );
1311 /*****************************************************************************
1313 *****************************************************************************/
1314 static OSStatus DeviceListener( AudioDeviceID inDevice,
1317 AudioDevicePropertyID inPropertyID,
1318 void *inClientData )
1320 UInt32 i_param_size;
1321 OSStatus err = noErr;
1323 aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1324 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1326 switch( inPropertyID )
1328 case kAudioDevicePropertyDeviceIsAlive:
1330 i_param_size = sizeof( p_sys->b_dev_alive );
1331 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
1332 kAudioDevicePropertyDeviceIsAlive,
1333 &i_param_size, &p_sys->b_dev_alive );
1336 msg_Err( p_aout, "AudioDeviceGetProperty failed: %4.4s",
1346 /*****************************************************************************
1348 *****************************************************************************/
1349 static OSStatus StreamListener( AudioStreamID inStream,
1351 AudioDevicePropertyID inPropertyID,
1352 void * inClientData )
1354 OSStatus err = noErr;
1356 struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1358 switch( inPropertyID )
1360 case kAudioStreamPropertyPhysicalFormat:
1361 vlc_mutex_lock( &w->lock );
1362 vlc_cond_signal( &w->cond );
1363 vlc_mutex_unlock( &w->lock );
1373 /*****************************************************************************
1375 *****************************************************************************/
1376 static void InitDeviceVar( aout_instance_t * p_aout, int i_option,
1377 vlc_bool_t b_change )
1382 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1384 if( i_option == -1 || i_option >= (int)p_sys->i_options )
1386 for( i = 0; i < p_sys->i_options; i++ )
1388 if( p_sys->p_options[i].i_dev == p_sys->i_def_dev )
1396 var_Create( p_aout, "audio-device", VLC_VAR_STRING |
1397 VLC_VAR_HASCHOICE );
1399 for( i = 0; i < p_sys->i_options; i++ )
1401 val.psz_string = p_sys->p_options[i].sz_option;
1402 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val );
1404 if( !b_change && i == (UInt32)i_option )
1406 p_sys->i_sel_opt = i;
1407 var_Set( p_aout, "audio-device", val );
1408 config_PutInt( p_aout, "macosx-adev", i_option );
1412 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
1417 val.psz_string = p_sys->p_options[i_option].sz_option;
1418 var_Set( p_aout, "audio-device", val );
1421 val.b_bool = VLC_TRUE;
1422 var_Set( p_aout, "intf-change", val );