1 /*****************************************************************************
2 * coreaudio.c: CoreAudio output plugin
3 *****************************************************************************
4 * Copyright (C) 2002-2003 VideoLAN
5 * $Id: coreaudio.c,v 1.10 2004/01/25 18:53:07 gbazin 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")
219 #define ADEV_LONGTEXT N_("Choose a number corresponding to the number of an " \
220 "audio device, as listed in your 'Audio Device' menu. This device will " \
221 "then be used by default for audio playback.")
224 set_description( _("CoreAudio output") );
225 set_capability( "audio output", 100 );
226 set_callbacks( Open, Close );
227 add_integer( "coreaudio-dev", -1, NULL, ADEV_TEXT, ADEV_LONGTEXT, VLC_FALSE );
230 /*****************************************************************************
231 * Open: open a CoreAudio HAL device
232 *****************************************************************************/
233 static int Open( vlc_object_t * p_this )
237 struct aout_sys_t * p_sys;
238 aout_instance_t * p_aout = (aout_instance_t *)p_this;
240 /* Allocate structure */
241 p_sys = (struct aout_sys_t *)malloc( sizeof( struct aout_sys_t ) );
244 msg_Err( p_aout, "out of memory" );
245 return( VLC_ENOMEM );
248 memset( p_sys, 0, sizeof( struct aout_sys_t ) );
250 p_aout->output.p_sys = p_sys;
251 p_aout->output.pf_play = Play;
253 vlc_mutex_init( p_aout, &p_sys->lock );
255 if( InitHardwareInfo( p_aout ) )
257 msg_Err( p_aout, "InitHardwareInfo failed" );
258 vlc_mutex_destroy( &p_sys->lock );
259 free( (void *)p_sys );
260 return( VLC_EGENERIC );
263 if( var_Type( p_aout, "audio-device" ) == 0 )
265 InitDeviceVar( p_aout, config_GetInt( p_aout, "coreaudio-dev" ),
269 if( InitDevice( p_aout ) )
271 msg_Err( p_aout, "InitDevice failed" );
272 FreeHardwareInfo( p_aout );
273 vlc_mutex_destroy( &p_sys->lock );
274 free( (void *)p_sys );
275 return( VLC_EGENERIC );
278 /* Get a description of the stream format */
279 i_param_size = sizeof( AudioStreamBasicDescription );
280 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
281 kAudioDevicePropertyStreamFormat,
282 &i_param_size, &p_sys->stream_format );
285 msg_Err( p_aout, "failed to get stream format: [%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 /* Set the output sample rate */
295 p_aout->output.output.i_rate =
296 (unsigned int)p_sys->stream_format.mSampleRate;
298 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "using format",
299 p_sys->stream_format ) );
301 /* Get the buffer size */
302 i_param_size = sizeof( p_sys->i_buffer_size );
303 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
304 kAudioDevicePropertyBufferSize,
305 &i_param_size, &p_sys->i_buffer_size );
308 msg_Err( p_aout, "failed to get 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: [%ld]", p_sys->i_buffer_size );
319 /* If we do AC3 over SPDIF, set buffer size to one AC3 frame */
320 if( ( p_sys->stream_format.mFormatID == kAudioFormat60958AC3 ||
321 p_sys->stream_format.mFormatID == 'IAC3' ) &&
322 p_sys->i_buffer_size != AOUT_SPDIF_SIZE )
324 p_sys->i_buffer_size = AOUT_SPDIF_SIZE;
325 i_param_size = sizeof( p_sys->i_buffer_size );
326 err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
327 kAudioDevicePropertyBufferSize,
328 i_param_size, &p_sys->i_buffer_size );
331 msg_Err( p_aout, "failed to set buffer size: [%4.4s]",
333 FreeDevice( p_aout );
334 FreeHardwareInfo( p_aout );
335 vlc_mutex_destroy( &p_sys->lock );
336 free( (void *)p_sys );
337 return( VLC_EGENERIC );
340 msg_Dbg( p_aout, "device buffer size set to: [%ld]",
341 p_sys->i_buffer_size );
343 /* Set buffer frame size */
344 i_param_size = sizeof( p_aout->output.i_nb_samples );
345 err = AudioDeviceSetProperty( p_sys->devid, 0, 0, FALSE,
346 kAudioDevicePropertyBufferFrameSize,
348 &p_aout->output.i_nb_samples );
351 msg_Err( p_aout, "failed to set buffer frame size: [%4.4s]",
353 FreeDevice( p_aout );
354 FreeHardwareInfo( p_aout );
355 vlc_mutex_destroy( &p_sys->lock );
356 free( (void *)p_sys );
357 return( VLC_EGENERIC );
360 msg_Dbg( p_aout, "device buffer frame size set to: [%d]",
361 p_aout->output.i_nb_samples );
364 switch( p_sys->stream_format.mFormatID )
366 case kAudioFormatLinearPCM:
367 p_aout->output.output.i_format = VLC_FOURCC('f','l','3','2');
369 switch( p_sys->stream_format.mChannelsPerFrame )
372 p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER;
376 p_aout->output.output.i_physical_channels =
377 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT;
381 p_aout->output.output.i_physical_channels =
382 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
383 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT;
387 p_aout->output.output.i_physical_channels =
388 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
389 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
390 AOUT_CHAN_CENTER | AOUT_CHAN_LFE;
394 p_aout->output.output.i_physical_channels =
395 AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT |
396 AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT |
397 AOUT_CHAN_CENTER | AOUT_CHAN_LFE |
398 AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT;
402 msg_Err( p_aout, "unknown channel count: [%ld]",
403 p_sys->stream_format.mChannelsPerFrame );
404 FreeDevice( p_aout );
405 FreeHardwareInfo( p_aout );
406 vlc_mutex_destroy( &p_sys->lock );
407 free( (void *)p_sys );
408 return( VLC_EGENERIC );
411 p_aout->output.i_nb_samples = (int)( p_sys->i_buffer_size /
412 p_sys->stream_format.mBytesPerFrame );
414 aout_VolumeSoftInit( p_aout );
418 case kAudioFormat60958AC3:
419 p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i');
420 p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE;
421 p_aout->output.output.i_frame_length = A52_FRAME_NB;
422 p_aout->output.i_nb_samples = p_aout->output.output.i_frame_length;
424 aout_VolumeNoneInit( p_aout );
428 msg_Err( p_aout, "unknown hardware format: [%4.4s]",
429 (char *)&p_sys->stream_format.mFormatID );
430 FreeDevice( p_aout );
431 FreeHardwareInfo( p_aout );
432 vlc_mutex_destroy( &p_sys->lock );
433 free( (void *)p_sys );
434 return( VLC_EGENERIC );
438 err = AudioDeviceAddIOProc( p_sys->devid,
439 (AudioDeviceIOProc)IOCallback,
443 msg_Err( p_aout, "AudioDeviceAddIOProc failed: [%4.4s]",
445 FreeDevice( p_aout );
446 FreeHardwareInfo( p_aout );
447 vlc_mutex_destroy( &p_sys->lock );
448 free( (void *)p_sys );
449 return( VLC_EGENERIC );
453 err = AudioDeviceStart( p_sys->devid, (AudioDeviceIOProc)IOCallback );
456 msg_Err( p_aout, "AudioDeviceStart failed: [%4.4s]",
459 err = AudioDeviceRemoveIOProc( p_sys->devid,
460 (AudioDeviceIOProc)IOCallback );
463 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
467 FreeDevice( p_aout );
468 FreeHardwareInfo( p_aout );
469 vlc_mutex_destroy( &p_sys->lock );
470 free( (void *)p_sys );
472 return( VLC_EGENERIC );
475 err = AudioHardwareAddPropertyListener( kAudioHardwarePropertyDevices,
480 msg_Err( p_aout, "AudioHardwareAddPropertyListener failed: %4.4s",
484 err = AudioDeviceStop( p_sys->devid,
485 (AudioDeviceIOProc)IOCallback );
488 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
492 /* Remove callback */
493 err = AudioDeviceRemoveIOProc( p_sys->devid,
494 (AudioDeviceIOProc)IOCallback );
497 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
501 FreeDevice( p_aout );
502 FreeHardwareInfo( p_aout );
503 vlc_mutex_destroy( &p_sys->lock );
504 free( (void *)p_sys );
506 return( VLC_EGENERIC );
509 /* Let's pray for the following operation to be atomic... */
510 p_sys->clock_diff = - (mtime_t)
511 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
512 p_sys->clock_diff += mdate();
514 return( VLC_SUCCESS );
517 /*****************************************************************************
518 * Close: close the CoreAudio HAL device
519 *****************************************************************************/
520 static void Close( vlc_object_t * p_this )
523 aout_instance_t * p_aout = (aout_instance_t *)p_this;
524 struct aout_sys_t * p_sys = p_aout->output.p_sys;
526 if( p_sys->b_dev_alive )
529 err = AudioDeviceStop( p_sys->devid,
530 (AudioDeviceIOProc)IOCallback );
533 msg_Err( p_aout, "AudioDeviceStop failed: [%4.4s]",
537 /* Remove callback */
538 err = AudioDeviceRemoveIOProc( p_sys->devid,
539 (AudioDeviceIOProc)IOCallback );
542 msg_Err( p_aout, "AudioDeviceRemoveIOProc failed: [%4.4s]",
546 FreeDevice( p_aout );
549 err = AudioHardwareRemovePropertyListener( kAudioHardwarePropertyDevices,
553 msg_Err( p_aout, "AudioHardwareRemovePropertyListener failed: [%4.4s]",
557 FreeHardwareInfo( p_aout );
559 vlc_mutex_destroy( &p_sys->lock );
564 /*****************************************************************************
565 * Play: nothing to do
566 *****************************************************************************/
567 static void Play( aout_instance_t * p_aout )
571 /*****************************************************************************
572 * IOCallback: callback for audio output
573 *****************************************************************************/
574 static OSStatus IOCallback( AudioDeviceID inDevice,
575 const AudioTimeStamp * inNow,
576 const void * inInputData,
577 const AudioTimeStamp * inInputTime,
578 AudioBufferList * outOutputData,
579 const AudioTimeStamp * inOutputTime,
580 void * threadGlobals )
582 aout_buffer_t * p_buffer;
583 AudioTimeStamp host_time;
584 mtime_t current_date;
586 aout_instance_t * p_aout = (aout_instance_t *)threadGlobals;
587 struct aout_sys_t * p_sys = p_aout->output.p_sys;
589 host_time.mFlags = kAudioTimeStampHostTimeValid;
590 AudioDeviceTranslateTime( inDevice, inOutputTime, &host_time );
593 p_sys->clock_diff = - (mtime_t)
594 AudioConvertHostTimeToNanos( AudioGetCurrentHostTime() ) / 1000;
595 p_sys->clock_diff += mdate();
598 current_date = p_sys->clock_diff +
599 AudioConvertHostTimeToNanos( host_time.mHostTime ) / 1000;
601 #define B_SPDI (p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i'))
602 p_buffer = aout_OutputNextBuffer( p_aout, current_date, B_SPDI );
605 if( p_buffer != NULL )
607 /* move data into output data buffer */
608 p_aout->p_vlc->pf_memcpy( outOutputData->mBuffers[ 0 ].mData,
610 p_sys->i_buffer_size );
612 aout_BufferFree( p_buffer );
616 if( p_aout->output.output.i_format == VLC_FOURCC('f','l','3','2') )
618 UInt32 i, i_size = p_sys->i_buffer_size / sizeof(float);
619 float * p = (float *)outOutputData->mBuffers[ 0 ].mData;
621 for( i = 0; i < i_size; i++ )
628 memset( outOutputData->mBuffers[ 0 ].mData,
629 0, p_sys->i_buffer_size );
636 /*****************************************************************************
638 *****************************************************************************/
639 static int InitHardwareInfo( aout_instance_t * p_aout )
642 UInt32 i, i_param_size;
643 AudioDeviceID devid_def;
644 AudioDeviceID * p_devices;
646 struct aout_sys_t * p_sys = p_aout->output.p_sys;
648 vlc_mutex_lock( &p_sys->lock );
650 /* Get number of devices */
651 err = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices,
652 &i_param_size, NULL );
655 msg_Err( p_aout, "could not get number of devices: [%4.4s]",
657 vlc_mutex_unlock( &p_sys->lock );
658 return( VLC_EGENERIC );
661 p_sys->i_devices = i_param_size / sizeof( AudioDeviceID );
663 if( p_sys->i_devices < 1 )
665 msg_Err( p_aout, "no devices found" );
666 vlc_mutex_unlock( &p_sys->lock );
667 return( VLC_EGENERIC );
670 msg_Dbg( p_aout, "system has [%ld] device(s)", p_sys->i_devices );
672 /* Allocate DeviceID array */
673 p_devices = (AudioDeviceID *)malloc( i_param_size );
674 if( p_devices == NULL )
676 msg_Err( p_aout, "out of memory" );
677 vlc_mutex_unlock( &p_sys->lock );
678 return( VLC_ENOMEM );
681 /* Populate DeviceID array */
682 err = AudioHardwareGetProperty( kAudioHardwarePropertyDevices,
683 &i_param_size, (void *)p_devices );
686 msg_Err( p_aout, "could not get the device ID's: [%4.4s]",
688 free( (void *)p_devices );
689 vlc_mutex_unlock( &p_sys->lock );
690 return( VLC_EGENERIC );
693 i_param_size = sizeof( AudioDeviceID );
694 err = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice,
695 &i_param_size, (void *)&devid_def );
698 msg_Err( p_aout, "could not get default audio device: [%4.4s]",
700 free( (void *)p_devices );
701 vlc_mutex_unlock( &p_sys->lock );
702 return( VLC_EGENERIC );
705 p_sys->p_devices = (struct aout_dev_t *)
706 malloc( sizeof( struct aout_dev_t ) * p_sys->i_devices );
707 if( p_sys->p_devices == NULL )
709 msg_Err( p_aout, "out of memory" );
710 free( (void *)p_devices );
711 vlc_mutex_unlock( &p_sys->lock );
712 return( VLC_ENOMEM );
715 p_sys->i_options = 0;
716 p_sys->p_options = NULL;
718 for( i = 0; i < p_sys->i_devices; i++ )
720 p_sys->p_devices[i].devid = p_devices[i];
722 if( p_devices[i] == devid_def )
724 p_sys->i_def_dev = i;
727 if( InitDeviceInfo( i, p_aout ) )
731 msg_Err( p_aout, "InitDeviceInfo(%ld) failed", i );
733 for( j = 0; j < i; j++ )
735 FreeDeviceInfo( j, p_aout );
738 free( (void *)p_sys->p_devices );
739 free( (void *)p_devices );
741 vlc_mutex_unlock( &p_sys->lock );
743 return( VLC_EGENERIC );
747 free( (void *)p_devices );
749 p_sys->b_hwinfo = VLC_TRUE;
751 vlc_mutex_unlock( &p_sys->lock );
753 return( VLC_SUCCESS );
756 /*****************************************************************************
758 *****************************************************************************/
759 static int InitDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
762 UInt32 i, i_param_size;
763 AudioBufferList * p_buffer_list;
765 struct aout_sys_t * p_sys = p_aout->output.p_sys;
766 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
768 /* Get length of device name */
769 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
770 kAudioDevicePropertyDeviceName,
771 &i_param_size, NULL );
774 msg_Err( p_aout, "could not get size of devicename: [%4.4s]",
776 return( VLC_EGENERIC );
779 /* Allocate memory for device name */
780 p_dev->psz_device_name = (char *)malloc( i_param_size );
781 if( p_dev->psz_device_name == NULL )
783 msg_Err( p_aout, "out of memory" );
784 return( VLC_ENOMEM );
787 /* Get device name */
788 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
789 kAudioDevicePropertyDeviceName,
790 &i_param_size, p_dev->psz_device_name );
793 msg_Err( p_aout, "could not get devicename: [%4.4s]",
795 free( (void *)p_dev->psz_device_name );
796 return( VLC_EGENERIC );
799 msg_Dbg( p_aout, "device [%ld] has name [%s]",
800 i_dev, p_dev->psz_device_name );
802 err = AudioDeviceGetPropertyInfo( p_dev->devid, 0, FALSE,
803 kAudioDevicePropertyStreamConfiguration,
804 &i_param_size, NULL );
807 msg_Err( p_aout, "could not get size of stream configuration: [%4.4s]",
809 free( (void *)p_dev->psz_device_name );
810 return( VLC_EGENERIC );
813 p_buffer_list = (AudioBufferList *)malloc( i_param_size );
814 if( p_buffer_list == NULL )
816 msg_Err( p_aout, "out of memory" );
817 free( (void *)p_dev->psz_device_name );
818 return( VLC_ENOMEM );
821 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
822 kAudioDevicePropertyStreamConfiguration,
823 &i_param_size, p_buffer_list );
826 msg_Err( p_aout, "could not get stream configuration: [%4.4s]",
828 free( (void *)p_dev->psz_device_name );
829 free( (void *)p_buffer_list );
830 return( VLC_EGENERIC );
833 p_dev->i_streams = p_buffer_list->mNumberBuffers;
834 free( (void *)p_buffer_list );
836 msg_Dbg( p_aout, "device [%ld] has [%ld] streams",
837 i_dev, p_dev->i_streams );
839 p_dev->pi_streams = (UInt32 *)malloc( p_dev->i_streams *
840 sizeof( *p_dev->pi_streams ) );
841 if( p_dev->pi_streams == NULL )
843 msg_Err( p_aout, "out of memory" );
844 free( (void *)p_dev->psz_device_name );
845 return( VLC_ENOMEM );
848 p_dev->pp_streams = (AudioStreamBasicDescription **)
849 malloc( p_dev->i_streams *
850 sizeof( *p_dev->pp_streams ) );
851 if( p_dev->pp_streams == NULL )
853 msg_Err( p_aout, "out of memory" );
854 free( (void *)p_dev->psz_device_name );
855 free( (void *)p_dev->pi_streams );
856 return( VLC_ENOMEM );
859 for( i = 0; i < p_dev->i_streams; i++ )
861 if( InitStreamInfo( i_dev, p_aout, i ) )
865 msg_Err( p_aout, "InitStreamInfo(%ld, %ld) failed", i_dev, i );
867 for( j = 0; j < i; j++ )
869 FreeStreamInfo( i_dev, p_aout, j );
872 free( (void *)p_dev->psz_device_name );
873 free( (void *)p_dev->pi_streams );
874 free( (void *)p_dev->pp_streams );
876 return( VLC_EGENERIC );
880 return( VLC_SUCCESS );
883 /*****************************************************************************
885 *****************************************************************************/
886 static void FreeDeviceInfo( UInt32 i_dev, aout_instance_t * p_aout )
890 struct aout_sys_t * p_sys = p_aout->output.p_sys;
891 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
893 for( i = 0; i < p_dev->i_streams; i++ )
895 FreeStreamInfo( i_dev, p_aout, i );
898 free( (void *)p_dev->pp_streams );
899 free( (void *)p_dev->pi_streams );
900 free( (void *)p_dev->psz_device_name );
903 /*****************************************************************************
905 *****************************************************************************/
906 static void FreeHardwareInfo( aout_instance_t * p_aout )
910 struct aout_sys_t * p_sys = p_aout->output.p_sys;
912 vlc_mutex_lock( &p_sys->lock );
914 if( !p_sys->b_hwinfo )
916 vlc_mutex_unlock( &p_sys->lock );
920 for( i = 0; i < p_sys->i_devices; i++ )
922 FreeDeviceInfo( i, p_aout );
925 free( (void *)p_sys->p_options );
926 free( (void *)p_sys->p_devices );
928 p_sys->b_hwinfo = VLC_FALSE;
930 vlc_mutex_unlock( &p_sys->lock );
933 /*****************************************************************************
935 *****************************************************************************/
936 static int GetStreamID( AudioDeviceID devid, UInt32 i_idx,
937 AudioStreamID * p_sid )
941 AudioStreamID * p_stream_list;
943 err = AudioDeviceGetPropertyInfo( devid, 0, FALSE,
944 kAudioDevicePropertyStreams,
945 &i_param_size, NULL );
948 return( VLC_EGENERIC );
951 p_stream_list = (AudioStreamID *)malloc( i_param_size );
952 if( p_stream_list == NULL )
954 return( VLC_ENOMEM );
957 err = AudioDeviceGetProperty( devid, 0, FALSE,
958 kAudioDevicePropertyStreams,
959 &i_param_size, p_stream_list );
962 free( (void *)p_stream_list );
963 return( VLC_EGENERIC );
966 *p_sid = p_stream_list[i_idx - 1];
968 free( (void *)p_stream_list );
970 return( VLC_SUCCESS );
973 /*****************************************************************************
975 *****************************************************************************/
976 static int InitStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
981 UInt32 i, j, i_param_size;
983 struct aout_sys_t * p_sys = p_aout->output.p_sys;
984 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
986 if( GetStreamID( p_dev->devid, i_idx + 1, &i_sid ) )
988 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed", i_dev, i_idx );
989 return( VLC_EGENERIC );
992 err = AudioStreamGetPropertyInfo( i_sid, 0,
993 kAudioStreamPropertyPhysicalFormats,
994 &i_param_size, NULL );
997 msg_Err( p_aout, "could not retrieve the number of streams: [%4.4s]",
999 return( VLC_EGENERIC );
1002 #define P_STREAMS p_dev->pp_streams[i_idx]
1003 #define I_STREAMS p_dev->pi_streams[i_idx]
1005 I_STREAMS = i_param_size / sizeof( AudioStreamBasicDescription );
1007 P_STREAMS = (AudioStreamBasicDescription *)malloc( i_param_size );
1008 if( P_STREAMS == NULL )
1010 msg_Err( p_aout, "out of memory" );
1011 return( VLC_ENOMEM );
1014 memset( P_STREAMS, 0, i_param_size );
1016 err = AudioStreamGetProperty( i_sid, 0,
1017 kAudioStreamPropertyPhysicalFormats,
1018 &i_param_size, P_STREAMS );
1021 msg_Err( p_aout, "could no get the streams: [%4.4s]",
1023 free( (void *)P_STREAMS );
1024 return( VLC_EGENERIC );
1027 for( j = 0; j < N_AOUT_CLASSES; j++ )
1029 vlc_bool_t b_found = 0;
1031 for( i = 0; i < I_STREAMS; i++ )
1035 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "supported format",
1039 if( ( P_STREAMS[i].mFormatID == 'IAC3' ||
1040 P_STREAMS[i].mFormatID == kAudioFormat60958AC3 ) &&
1041 !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) )
1046 if( ( P_STREAMS[i].mFormatID != aout_classes[j].mFormatID ) ||
1047 ( P_STREAMS[i].mChannelsPerFrame !=
1048 aout_classes[j].mChannelsPerFrame ) )
1059 p_sys->p_options = (struct aout_option_t *)
1060 realloc( p_sys->p_options,
1061 ( p_sys->i_options + 1 ) *
1062 sizeof( struct aout_option_t ) );
1063 if( p_sys->p_options == NULL )
1065 msg_Err( p_aout, "out of memory" );
1066 free( (void *)P_STREAMS );
1067 return( VLC_ENOMEM );
1070 #define AOUT_OPTION p_sys->p_options[p_sys->i_options]
1072 snprintf( AOUT_OPTION.sz_option,
1073 sizeof( AOUT_OPTION.sz_option ) /
1074 sizeof( AOUT_OPTION.sz_option[0] ) - 1,
1077 p_dev->psz_device_name,
1078 aout_classes[j].psz_class );
1080 AOUT_OPTION.i_sid = i_sid;
1081 AOUT_OPTION.i_dev = i_dev;
1082 AOUT_OPTION.i_idx = i_idx;
1083 AOUT_OPTION.i_sdx = i;
1084 AOUT_OPTION.i_cdx = j;
1095 return( VLC_SUCCESS );
1098 /*****************************************************************************
1100 *****************************************************************************/
1101 static void FreeStreamInfo( UInt32 i_dev, aout_instance_t * p_aout,
1104 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1105 struct aout_dev_t * p_dev = &p_sys->p_devices[i_dev];
1107 free( (void *)p_dev->pp_streams[i_idx] );
1110 /*****************************************************************************
1112 *****************************************************************************/
1113 static int InitDevice( aout_instance_t * p_aout )
1117 unsigned int i_option;
1118 vlc_bool_t b_found = VLC_FALSE;
1119 UInt32 i, i_stream, i_param_size;
1121 struct aout_dev_t * p_dev;
1122 struct aout_option_t * p_option;
1123 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1125 if( var_Get( p_aout, "audio-device", &val ) < 0 )
1127 msg_Err( p_aout, "audio-device var does not exist" );
1128 return( VLC_ENOVAR );
1131 i_option = val.i_int;
1132 p_option = &p_sys->p_options[i_option];
1133 p_dev = &p_sys->p_devices[p_option->i_dev];
1135 msg_Dbg( p_aout, "getting device [%ld]", p_option->i_dev );
1137 i_param_size = sizeof( p_sys->b_dev_alive );
1138 err = AudioDeviceGetProperty( p_dev->devid, 0, FALSE,
1139 kAudioDevicePropertyDeviceIsAlive,
1140 &i_param_size, &p_sys->b_dev_alive );
1143 msg_Err( p_aout, "could not check whether device is alive: %4.4s",
1145 return( VLC_EGENERIC );
1148 #define P_STREAMS p_dev->pp_streams[p_option->i_idx]
1149 #define I_STREAMS p_dev->pi_streams[p_option->i_idx]
1151 for( i = 0; i < I_STREAMS; i++ )
1153 if( P_STREAMS[i].mFormatID ==
1154 aout_classes[p_option->i_cdx].mFormatID &&
1155 P_STREAMS[i].mChannelsPerFrame ==
1156 aout_classes[p_option->i_cdx].mChannelsPerFrame &&
1157 P_STREAMS[i].mSampleRate == p_aout->output.output.i_rate )
1164 i_stream = b_found ? i : p_option->i_sdx;
1166 i_param_size = sizeof( p_sys->sfmt_revert );
1167 err = AudioStreamGetProperty( p_option->i_sid, 0,
1168 kAudioStreamPropertyPhysicalFormat,
1170 (void *)&p_sys->sfmt_revert );
1173 msg_Err( p_aout, "could not retrieve the original streamformat: [%4.4s]",
1175 return( VLC_EGENERIC );
1178 if( memcmp( &P_STREAMS[i_stream], &p_sys->sfmt_revert,
1179 sizeof( p_sys->sfmt_revert ) ) != 0 )
1182 struct timespec timeout;
1183 struct { vlc_mutex_t lock; vlc_cond_t cond; } w;
1185 vlc_cond_init( p_aout, &w.cond );
1186 vlc_mutex_init( p_aout, &w.lock );
1188 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "stream format",
1189 p_sys->sfmt_revert ) );
1191 err = AudioStreamAddPropertyListener( p_option->i_sid, 0,
1192 kAudioStreamPropertyPhysicalFormat,
1193 StreamListener, (void *)&w );
1196 msg_Err( p_aout, "AudioStreamAddPropertyListener failed: [%4.4s]",
1198 vlc_mutex_destroy( &w.lock );
1199 vlc_cond_destroy( &w.cond );
1200 return( VLC_EGENERIC );
1203 vlc_mutex_lock( &w.lock );
1205 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "setting format",
1206 P_STREAMS[i_stream] ) );
1208 err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
1209 kAudioStreamPropertyPhysicalFormat,
1210 sizeof( P_STREAMS[i_stream] ),
1211 &P_STREAMS[i_stream] );
1214 msg_Err( p_aout, "could not set the stream format: [%4.4s]",
1216 vlc_mutex_unlock( &w.lock );
1217 vlc_mutex_destroy( &w.lock );
1218 vlc_cond_destroy( &w.cond );
1219 return( VLC_EGENERIC );
1222 gettimeofday( &now, NULL );
1223 timeout.tv_sec = now.tv_sec;
1224 timeout.tv_nsec = (now.tv_usec + 100000) * 1000;
1226 pthread_cond_timedwait( &w.cond.cond, &w.lock.mutex, &timeout );
1227 vlc_mutex_unlock( &w.lock );
1229 if( GetStreamID( p_dev->devid, p_option->i_idx + 1,
1230 &p_option->i_sid ) )
1232 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed",
1233 p_option->i_dev, p_option->i_idx );
1234 vlc_mutex_destroy( &w.lock );
1235 vlc_cond_destroy( &w.cond );
1236 return( VLC_EGENERIC );
1239 err = AudioStreamRemovePropertyListener( p_option->i_sid, 0,
1240 kAudioStreamPropertyPhysicalFormat, StreamListener );
1244 "AudioStreamRemovePropertyListener failed: [%4.4s]",
1246 vlc_mutex_destroy( &w.lock );
1247 vlc_cond_destroy( &w.cond );
1248 return( VLC_EGENERIC );
1251 vlc_mutex_destroy( &w.lock );
1252 vlc_cond_destroy( &w.cond );
1254 p_sys->b_revert_sfmt = VLC_TRUE;
1260 err = AudioDeviceAddPropertyListener( p_dev->devid, 0, FALSE,
1261 kAudioDevicePropertyDeviceIsAlive,
1262 DeviceListener, (void *)p_aout );
1265 msg_Err( p_aout, "AudioDeviceAddPropertyListener failed: [%4.4s]",
1267 return( VLC_EGENERIC );
1270 config_PutInt( p_aout, "coreaudio-dev", i_option );
1272 p_sys->i_sel_opt = i_option;
1273 p_sys->devid = p_dev->devid;
1275 return( VLC_SUCCESS );
1278 /*****************************************************************************
1280 *****************************************************************************/
1281 static void FreeDevice( aout_instance_t * p_aout )
1285 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1287 if( p_sys->b_revert_sfmt )
1289 struct aout_dev_t * p_dev;
1290 struct aout_option_t * p_option;
1292 p_option = &p_sys->p_options[p_sys->i_sel_opt];
1293 p_dev = &p_sys->p_devices[p_option->i_dev];
1295 msg_Dbg( p_aout, STREAM_FORMAT_MSG( "reverting to format",
1296 p_sys->sfmt_revert ) );
1298 if( GetStreamID( p_dev->devid, p_option->i_idx + 1,
1299 &p_option->i_sid ) )
1301 msg_Err( p_aout, "GetStreamID(%ld, %ld) failed",
1302 p_option->i_dev, p_option->i_idx );
1306 err = AudioStreamSetProperty( p_option->i_sid, 0, 0,
1307 kAudioStreamPropertyPhysicalFormat,
1308 sizeof( p_sys->sfmt_revert ),
1309 &p_sys->sfmt_revert );
1312 msg_Err( p_aout, "AudioStreamSetProperty revert format failed: [%4.4s]",
1318 err = AudioDeviceRemovePropertyListener( p_sys->devid, 0, FALSE,
1319 kAudioDevicePropertyDeviceIsAlive,
1323 msg_Err( p_aout, "AudioDeviceRemovePropertyListener failed: [%4.4s]",
1328 /*****************************************************************************
1330 *****************************************************************************/
1331 static OSStatus HardwareListener( AudioHardwarePropertyID inPropertyID,
1332 void * inClientData )
1334 OSStatus err = noErr;
1336 aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1337 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1339 switch( inPropertyID )
1341 case kAudioHardwarePropertyDevices:
1347 if( p_sys->b_dev_alive )
1349 i_idx = p_sys->p_options[p_sys->i_sel_opt].i_idx;
1350 i_sdx = p_sys->p_options[p_sys->i_sel_opt].i_sdx;
1353 FreeHardwareInfo( p_aout );
1355 if( InitHardwareInfo( p_aout ) )
1357 msg_Err( p_aout, "InitHardwareInfo failed" );
1361 if( p_sys->b_dev_alive )
1365 for( i = 0; i < p_sys->i_options; i++ )
1367 if( p_sys->p_devices[p_sys->p_options[i].i_dev].devid ==
1368 p_sys->devid && p_sys->p_options[i].i_idx == i_idx &&
1369 p_sys->p_options[i].i_sdx == i_sdx )
1377 var_Destroy( p_aout, "audio-device" );
1378 InitDeviceVar( p_aout, i_option, !p_sys->b_dev_alive );
1386 /*****************************************************************************
1388 *****************************************************************************/
1389 static OSStatus DeviceListener( AudioDeviceID inDevice,
1392 AudioDevicePropertyID inPropertyID,
1393 void *inClientData )
1395 UInt32 i_param_size;
1396 OSStatus err = noErr;
1398 aout_instance_t * p_aout = (aout_instance_t *)inClientData;
1399 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1401 switch( inPropertyID )
1403 case kAudioDevicePropertyDeviceIsAlive:
1405 i_param_size = sizeof( p_sys->b_dev_alive );
1406 err = AudioDeviceGetProperty( p_sys->devid, 0, FALSE,
1407 kAudioDevicePropertyDeviceIsAlive,
1408 &i_param_size, &p_sys->b_dev_alive );
1411 msg_Err( p_aout, "could not determine wether device is alive: %4.4s",
1421 /*****************************************************************************
1423 *****************************************************************************/
1424 static OSStatus StreamListener( AudioStreamID inStream,
1426 AudioDevicePropertyID inPropertyID,
1427 void * inClientData )
1429 OSStatus err = noErr;
1431 struct { vlc_mutex_t lock; vlc_cond_t cond; } * w = inClientData;
1433 switch( inPropertyID )
1435 case kAudioStreamPropertyPhysicalFormat:
1436 vlc_mutex_lock( &w->lock );
1437 vlc_cond_signal( &w->cond );
1438 vlc_mutex_unlock( &w->lock );
1448 /*****************************************************************************
1450 *****************************************************************************/
1451 static void InitDeviceVar( aout_instance_t * p_aout, int i_option,
1452 vlc_bool_t b_change )
1455 vlc_value_t val, text;
1457 struct aout_sys_t * p_sys = p_aout->output.p_sys;
1459 if( i_option == -1 || i_option >= (int)p_sys->i_options )
1461 for( i = 0; i < p_sys->i_options; i++ )
1463 if( p_sys->p_options[i].i_dev == p_sys->i_def_dev )
1471 var_Create( p_aout, "audio-device", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE );
1472 text.psz_string = ADEV_TEXT;
1473 var_Change( p_aout, "audio-device", VLC_VAR_SETTEXT, &text, NULL );
1475 for( i = 0; i < p_sys->i_options; i++ )
1477 text.psz_string = p_sys->p_options[i].sz_option;
1479 var_Change( p_aout, "audio-device", VLC_VAR_ADDCHOICE, &val, &text );
1481 if( !b_change && i == (UInt32)i_option )
1483 p_sys->i_sel_opt = i;
1484 var_Set( p_aout, "audio-device", val );
1485 config_PutInt( p_aout, "coreaudio-dev", i_option );
1489 var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart,
1494 val.i_int = i_option;
1495 var_Set( p_aout, "audio-device", val );
1498 val.b_bool = VLC_TRUE;
1499 var_Set( p_aout, "intf-change", val );