2003-05-21 23:15:46 +02:00
/*
2009-05-04 16:53:47 +02:00
* CoreAudio audio output driver for Mac OS X
2003-05-21 23:15:46 +02:00
*
2008-06-06 02:45:35 +02:00
* original copyright ( C ) Timothy J . Wood - Aug 2000
* ported to MPlayer libao2 by Dan Christiansen
2003-05-21 23:15:46 +02:00
*
2008-06-06 02:45:35 +02:00
* The S / PDIF part of the code is based on the auhal audio output
* module from VideoLAN :
* Copyright ( c ) 2006 Derk - Jan Hartman < hartman at videolan dot org >
2003-05-21 23:15:46 +02:00
*
2008-06-06 02:45:35 +02:00
* This file is part of MPlayer .
2007-12-22 18:33:28 +01:00
*
2008-06-06 02:45:35 +02:00
* MPlayer is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
2003-05-21 23:15:46 +02:00
*
2008-06-06 02:45:35 +02:00
* MPlayer is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
2003-05-21 23:15:46 +02:00
*
2008-06-06 02:45:35 +02:00
* You should have received a copy of the GNU General Public License along
* along with MPlayer ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2003-05-21 23:15:46 +02:00
*/
/*
* The MacOS X CoreAudio framework doesn ' t mesh as simply as some
* simpler frameworks do . This is due to the fact that CoreAudio pulls
* audio samples rather than having them pushed at it ( which is nice
2008-06-06 02:45:35 +02:00
* when you are wanting to do good buffering of audio ) .
2003-05-21 23:15:46 +02:00
*
2008-06-06 02:45:35 +02:00
* AC - 3 and MPEG audio passthrough is possible , but has never been tested
* due to lack of a soundcard that supports it .
2003-05-21 23:15:46 +02:00
*/
2005-06-05 16:47:26 +02:00
# include <CoreServices/CoreServices.h>
# include <AudioUnit/AudioUnit.h>
# include <AudioToolbox/AudioToolbox.h>
2003-05-21 23:15:46 +02:00
# include <stdio.h>
# include <string.h>
2005-03-03 14:46:54 +01:00
# include <stdlib.h>
2003-05-21 23:15:46 +02:00
# include <inttypes.h>
2007-10-11 04:00:05 +02:00
# include <sys/types.h>
# include <unistd.h>
2003-05-21 23:15:46 +02:00
2005-01-12 23:00:02 +01:00
# include "config.h"
2004-12-07 03:24:15 +01:00
# include "mp_msg.h"
2003-05-21 23:15:46 +02:00
# include "audio_out.h"
# include "audio_out_internal.h"
2004-12-27 18:30:15 +01:00
# include "libaf/af_format.h"
2007-10-11 04:00:05 +02:00
# include "osdep/timer.h"
2009-05-03 22:57:37 +02:00
# include "libavutil/fifo.h"
2010-07-11 23:08:57 +02:00
# include "subopt-helper.h"
2003-05-21 23:15:46 +02:00
2009-03-06 20:43:12 +01:00
static const ao_info_t info =
2003-05-21 23:15:46 +02:00
{
" Darwin/Mac OS X native audio output " ,
2009-05-04 16:53:47 +02:00
" coreaudio " ,
2005-06-05 16:47:26 +02:00
" Timothy J. Wood & Dan Christiansen & Chris Roccati " ,
2003-05-21 23:15:46 +02:00
" "
} ;
2009-05-04 16:53:47 +02:00
LIBAO_EXTERN ( coreaudio )
2003-05-21 23:15:46 +02:00
2003-05-22 18:12:54 +02:00
/* Prefix for all mp_msg() calls */
2009-05-04 16:53:47 +02:00
# define ao_msg(a, b, c...) mp_msg(a, b, "AO: [coreaudio] " c)
2003-05-22 18:12:54 +02:00
2011-01-07 22:30:14 +01:00
# if MAC_OS_X_VERSION_MAX_ALLOWED <= 1040
/* AudioDeviceIOProcID does not exist in Mac OS X 10.4. We can emulate
* this by using AudioDeviceAddIOProc ( ) and AudioDeviceRemoveIOProc ( ) . */
# define AudioDeviceIOProcID AudioDeviceIOProc
# define AudioDeviceDestroyIOProcID AudioDeviceRemoveIOProc
static OSStatus AudioDeviceCreateIOProcID ( AudioDeviceID dev ,
AudioDeviceIOProc proc ,
void * data ,
AudioDeviceIOProcID * procid )
{
* procid = proc ;
return AudioDeviceAddIOProc ( dev , proc , data ) ;
}
# endif
2009-05-04 16:53:47 +02:00
typedef struct ao_coreaudio_s
2003-05-21 23:15:46 +02:00
{
2007-10-11 04:00:05 +02:00
AudioDeviceID i_selected_dev ; /* Keeps DeviceID of the selected device. */
int b_supports_digital ; /* Does the currently selected device support digital mode? */
int b_digital ; /* Are we running in digital mode? */
2007-11-11 03:54:57 +01:00
int b_muted ; /* Are we muted in digital mode? */
2007-10-11 04:00:05 +02:00
2010-07-11 23:00:37 +02:00
AudioDeviceIOProcID renderCallback ; /* Render callback used for SPDIF */
2005-06-05 16:47:26 +02:00
/* AudioUnit */
AudioUnit theOutputUnit ;
2007-10-11 04:00:05 +02:00
/* CoreAudio SPDIF mode specific */
pid_t i_hog_pid ; /* Keeps the pid of our hog status. */
AudioStreamID i_stream_id ; /* The StreamID that has a cac3 streamformat */
int i_stream_index ; /* The index of i_stream_id in an AudioBufferList */
AudioStreamBasicDescription stream_format ; /* The format we changed the stream to */
AudioStreamBasicDescription sfmt_revert ; /* The original format of the stream */
int b_revert ; /* Whether we need to revert the stream format */
int b_changed_mixing ; /* Whether we need to set the mixing mode back */
int b_stream_format_changed ; /* Flag for main thread to reset stream's format to digital and reset buffer */
/* Original common part */
2005-06-05 16:47:26 +02:00
int packetSize ;
2006-08-18 03:19:19 +02:00
int paused ;
2003-05-21 23:15:46 +02:00
/* Ring-buffer */
2009-05-03 22:57:37 +02:00
AVFifoBuffer * buffer ;
unsigned int buffer_len ; ///< must always be num_chunks * chunk_size
2005-02-22 21:54:31 +01:00
unsigned int num_chunks ;
unsigned int chunk_size ;
2009-05-04 16:53:47 +02:00
} ao_coreaudio_t ;
2003-05-21 23:15:46 +02:00
2009-05-04 16:53:47 +02:00
static ao_coreaudio_t * ao = NULL ;
2003-05-21 23:15:46 +02:00
2005-02-22 21:54:31 +01:00
/**
* \ brief add data to ringbuffer
*/
static int write_buffer ( unsigned char * data , int len ) {
2009-05-03 22:57:37 +02:00
int free = ao - > buffer_len - av_fifo_size ( ao - > buffer ) ;
2005-02-22 21:54:31 +01:00
if ( len > free ) len = free ;
2009-05-03 22:57:37 +02:00
return av_fifo_generic_write ( ao - > buffer , data , len , NULL ) ;
2003-05-21 23:15:46 +02:00
}
2005-02-22 21:54:31 +01:00
/**
* \ brief remove data from ringbuffer
*/
2003-05-21 23:15:46 +02:00
static int read_buffer ( unsigned char * data , int len ) {
2009-05-03 22:57:37 +02:00
int buffered = av_fifo_size ( ao - > buffer ) ;
2005-02-22 21:54:31 +01:00
if ( len > buffered ) len = buffered ;
2011-10-23 14:03:13 +02:00
if ( data )
av_fifo_generic_read ( ao - > buffer , data , len , NULL ) ;
else
av_fifo_drain ( ao - > buffer , len ) ;
2009-08-08 10:17:35 +02:00
return len ;
2003-05-21 23:15:46 +02:00
}
2010-02-23 08:54:43 +01:00
static OSStatus theRenderProc ( void * inRefCon ,
AudioUnitRenderActionFlags * inActionFlags ,
const AudioTimeStamp * inTimeStamp ,
UInt32 inBusNumber , UInt32 inNumFrames ,
AudioBufferList * ioData )
2003-05-21 23:15:46 +02:00
{
2009-05-03 22:57:37 +02:00
int amt = av_fifo_size ( ao - > buffer ) ;
2005-06-14 14:41:42 +02:00
int req = ( inNumFrames ) * ao - > packetSize ;
2003-05-21 23:15:46 +02:00
2005-06-05 16:47:26 +02:00
if ( amt > req )
amt = req ;
if ( amt )
read_buffer ( ( unsigned char * ) ioData - > mBuffers [ 0 ] . mData , amt ) ;
2005-06-29 20:17:19 +02:00
else audio_pause ( ) ;
2005-06-14 14:41:42 +02:00
ioData - > mBuffers [ 0 ] . mDataByteSize = amt ;
2005-06-05 16:47:26 +02:00
return noErr ;
2003-05-21 23:15:46 +02:00
}
static int control ( int cmd , void * arg ) {
2006-11-29 00:05:45 +01:00
ao_control_vol_t * control_vol ;
OSStatus err ;
2007-05-28 10:42:35 +02:00
Float32 vol ;
2003-05-21 23:15:46 +02:00
switch ( cmd ) {
2005-01-01 04:51:08 +01:00
case AOCONTROL_GET_VOLUME :
2006-11-29 00:05:45 +01:00
control_vol = ( ao_control_vol_t * ) arg ;
2007-10-11 04:00:05 +02:00
if ( ao - > b_digital ) {
// Digital output has no volume adjust.
return CONTROL_FALSE ;
}
2006-11-29 00:05:45 +01:00
err = AudioUnitGetParameter ( ao - > theOutputUnit , kHALOutputParam_Volume , kAudioUnitScope_Global , 0 , & vol ) ;
if ( err = = 0 ) {
// printf("GET VOL=%f\n", vol);
control_vol - > left = control_vol - > right = vol * 100.0 / 4.0 ;
return CONTROL_TRUE ;
}
else {
2007-10-11 04:00:05 +02:00
ao_msg ( MSGT_AO , MSGL_WARN , " could not get HAL output volume: [%4.4s] \n " , ( char * ) & err ) ;
2006-11-29 00:05:45 +01:00
return CONTROL_FALSE ;
}
2005-01-01 04:51:08 +01:00
case AOCONTROL_SET_VOLUME :
2007-11-11 03:54:57 +01:00
control_vol = ( ao_control_vol_t * ) arg ;
if ( ao - > b_digital ) {
2007-10-11 04:00:05 +02:00
// Digital output can not set volume. Here we have to return true
// to make mixer forget it. Else mixer will add a soft filter,
// that's not we expected and the filter not support ac3 stream
// will cause mplayer die.
2007-11-11 03:54:57 +01:00
// Although not support set volume, but at least we support mute.
// MPlayer set mute by set volume to zero, we handle it.
if ( control_vol - > left = = 0 & & control_vol - > right = = 0 )
ao - > b_muted = 1 ;
else
ao - > b_muted = 0 ;
2007-10-11 04:00:05 +02:00
return CONTROL_TRUE ;
2007-11-11 03:54:57 +01:00
}
2009-05-13 04:58:57 +02:00
2006-11-29 00:05:45 +01:00
vol = ( control_vol - > left + control_vol - > right ) * 4.0 / 200.0 ;
err = AudioUnitSetParameter ( ao - > theOutputUnit , kHALOutputParam_Volume , kAudioUnitScope_Global , 0 , vol , 0 ) ;
if ( err = = 0 ) {
// printf("SET VOL=%f\n", vol);
return CONTROL_TRUE ;
}
else {
2007-10-11 04:00:05 +02:00
ao_msg ( MSGT_AO , MSGL_WARN , " could not set HAL output volume: [%4.4s] \n " , ( char * ) & err ) ;
2006-11-29 00:05:45 +01:00
return CONTROL_FALSE ;
}
2005-06-05 16:47:26 +02:00
/* Everything is currently unimplemented */
2003-05-21 23:15:46 +02:00
default :
2003-05-22 18:12:54 +02:00
return CONTROL_FALSE ;
2003-05-21 23:15:46 +02:00
}
2009-05-13 04:58:57 +02:00
2003-05-21 23:15:46 +02:00
}
2007-10-11 04:00:05 +02:00
static void print_format ( int lev , const char * str , const AudioStreamBasicDescription * f ) {
2005-01-01 04:51:08 +01:00
uint32_t flags = ( uint32_t ) f - > mFormatFlags ;
2010-07-11 22:53:57 +02:00
ao_msg ( MSGT_AO , lev , " %s %7.1fHz % " PRIu32 " bit [%c%c%c%c][% " PRIu32 " ][% " PRIu32 " ][% " PRIu32 " ][% " PRIu32 " ][% " PRIu32 " ] %s %s %s%s%s%s \n " ,
2005-01-03 04:34:18 +01:00
str , f - > mSampleRate , f - > mBitsPerChannel ,
2005-01-01 04:51:08 +01:00
( int ) ( f - > mFormatID & 0xff000000 ) > > 24 ,
( int ) ( f - > mFormatID & 0x00ff0000 ) > > 16 ,
( int ) ( f - > mFormatID & 0x0000ff00 ) > > 8 ,
( int ) ( f - > mFormatID & 0x000000ff ) > > 0 ,
2007-10-11 04:00:05 +02:00
f - > mFormatFlags , f - > mBytesPerPacket ,
f - > mFramesPerPacket , f - > mBytesPerFrame ,
f - > mChannelsPerFrame ,
2005-01-01 04:51:08 +01:00
( flags & kAudioFormatFlagIsFloat ) ? " float " : " int " ,
( flags & kAudioFormatFlagIsBigEndian ) ? " BE " : " LE " ,
( flags & kAudioFormatFlagIsSignedInteger ) ? " S " : " U " ,
( flags & kAudioFormatFlagIsPacked ) ? " packed " : " " ,
( flags & kAudioFormatFlagIsAlignedHigh ) ? " aligned " : " " ,
( flags & kAudioFormatFlagIsNonInterleaved ) ? " ni " : " " ) ;
}
2010-07-11 23:00:37 +02:00
static OSStatus GetAudioProperty ( AudioObjectID id ,
AudioObjectPropertySelector selector ,
UInt32 outSize , void * outData )
{
AudioObjectPropertyAddress property_address ;
property_address . mSelector = selector ;
property_address . mScope = kAudioObjectPropertyScopeGlobal ;
property_address . mElement = kAudioObjectPropertyElementMaster ;
return AudioObjectGetPropertyData ( id , & property_address , 0 , NULL , & outSize , outData ) ;
}
static UInt32 GetAudioPropertyArray ( AudioObjectID id ,
AudioObjectPropertySelector selector ,
AudioObjectPropertyScope scope ,
void * * outData )
{
OSStatus err ;
AudioObjectPropertyAddress property_address ;
UInt32 i_param_size ;
property_address . mSelector = selector ;
property_address . mScope = scope ;
property_address . mElement = kAudioObjectPropertyElementMaster ;
err = AudioObjectGetPropertyDataSize ( id , & property_address , 0 , NULL , & i_param_size ) ;
if ( err ! = noErr )
return 0 ;
* outData = malloc ( i_param_size ) ;
err = AudioObjectGetPropertyData ( id , & property_address , 0 , NULL , & i_param_size , * outData ) ;
if ( err ! = noErr ) {
free ( * outData ) ;
return 0 ;
}
return i_param_size ;
}
static UInt32 GetGlobalAudioPropertyArray ( AudioObjectID id ,
AudioObjectPropertySelector selector ,
void * * outData )
{
return GetAudioPropertyArray ( id , selector , kAudioObjectPropertyScopeGlobal , outData ) ;
}
static OSStatus GetAudioPropertyString ( AudioObjectID id ,
AudioObjectPropertySelector selector ,
char * * outData )
{
OSStatus err ;
AudioObjectPropertyAddress property_address ;
UInt32 i_param_size ;
CFStringRef string ;
CFIndex string_length ;
property_address . mSelector = selector ;
property_address . mScope = kAudioObjectPropertyScopeGlobal ;
property_address . mElement = kAudioObjectPropertyElementMaster ;
i_param_size = sizeof ( CFStringRef ) ;
err = AudioObjectGetPropertyData ( id , & property_address , 0 , NULL , & i_param_size , & string ) ;
if ( err ! = noErr )
return err ;
string_length = CFStringGetMaximumSizeForEncoding ( CFStringGetLength ( string ) ,
kCFStringEncodingASCII ) ;
* outData = malloc ( string_length + 1 ) ;
CFStringGetCString ( string , * outData , string_length + 1 , kCFStringEncodingASCII ) ;
CFRelease ( string ) ;
return err ;
}
static OSStatus SetAudioProperty ( AudioObjectID id ,
AudioObjectPropertySelector selector ,
UInt32 inDataSize , void * inData )
{
AudioObjectPropertyAddress property_address ;
property_address . mSelector = selector ;
property_address . mScope = kAudioObjectPropertyScopeGlobal ;
property_address . mElement = kAudioObjectPropertyElementMaster ;
return AudioObjectSetPropertyData ( id , & property_address , 0 , NULL , inDataSize , inData ) ;
}
static Boolean IsAudioPropertySettable ( AudioObjectID id ,
AudioObjectPropertySelector selector ,
Boolean * outData )
{
AudioObjectPropertyAddress property_address ;
property_address . mSelector = selector ;
property_address . mScope = kAudioObjectPropertyScopeGlobal ;
property_address . mElement = kAudioObjectPropertyElementMaster ;
return AudioObjectIsPropertySettable ( id , & property_address , outData ) ;
}
2005-01-01 04:51:08 +01:00
2007-10-11 04:00:05 +02:00
static int AudioDeviceSupportsDigital ( AudioDeviceID i_dev_id ) ;
static int AudioStreamSupportsDigital ( AudioStreamID i_stream_id ) ;
2009-05-04 19:35:26 +02:00
static int OpenSPDIF ( void ) ;
2007-10-11 04:00:05 +02:00
static int AudioStreamChangeFormat ( AudioStreamID i_stream_id , AudioStreamBasicDescription change_format ) ;
static OSStatus RenderCallbackSPDIF ( AudioDeviceID inDevice ,
const AudioTimeStamp * inNow ,
const void * inInputData ,
const AudioTimeStamp * inInputTime ,
AudioBufferList * outOutputData ,
const AudioTimeStamp * inOutputTime ,
void * threadGlobals ) ;
2010-07-11 23:00:37 +02:00
static OSStatus StreamListener ( AudioObjectID inObjectID ,
UInt32 inNumberAddresses ,
const AudioObjectPropertyAddress inAddresses [ ] ,
void * inClientData ) ;
static OSStatus DeviceListener ( AudioObjectID inObjectID ,
UInt32 inNumberAddresses ,
const AudioObjectPropertyAddress inAddresses [ ] ,
void * inClientData ) ;
2007-10-11 04:00:05 +02:00
2010-07-11 23:08:57 +02:00
static void print_help ( void )
{
OSStatus err ;
UInt32 i_param_size ;
int num_devices ;
AudioDeviceID * devids ;
char * device_name ;
mp_msg ( MSGT_AO , MSGL_FATAL ,
" \n -ao coreaudio commandline help: \n "
" Example: mplayer -ao coreaudio:device_id=266 \n "
" open Core Audio with output device ID 266. \n "
" \n Options: \n "
" device_id \n "
" ID of output device to use (0 = default device) \n "
" help \n "
" This help including list of available devices. \n "
" \n "
" Available output devices: \n " ) ;
i_param_size = GetGlobalAudioPropertyArray ( kAudioObjectSystemObject , kAudioHardwarePropertyDevices , ( void * * ) & devids ) ;
if ( ! i_param_size ) {
mp_msg ( MSGT_AO , MSGL_FATAL , " Failed to get list of output devices. \n " ) ;
return ;
}
num_devices = i_param_size / sizeof ( AudioDeviceID ) ;
for ( int i = 0 ; i < num_devices ; + + i ) {
err = GetAudioPropertyString ( devids [ i ] , kAudioObjectPropertyName , & device_name ) ;
if ( err = = noErr ) {
mp_msg ( MSGT_AO , MSGL_FATAL , " %s (id: % " PRIu32 " ) \n " , device_name , devids [ i ] ) ;
free ( device_name ) ;
} else
mp_msg ( MSGT_AO , MSGL_FATAL , " Unknown (id: % " PRIu32 " ) \n " , devids [ i ] ) ;
}
mp_msg ( MSGT_AO , MSGL_FATAL , " \n " ) ;
free ( devids ) ;
}
2003-05-21 23:15:46 +02:00
static int init ( int rate , int channels , int format , int flags )
{
2007-05-28 10:42:35 +02:00
AudioStreamBasicDescription inDesc ;
2009-05-13 04:58:57 +02:00
ComponentDescription desc ;
Component comp ;
2005-06-05 16:47:26 +02:00
AURenderCallbackStruct renderCallback ;
OSStatus err ;
2010-07-12 19:43:37 +02:00
UInt32 size , maxFrames , b_alive ;
2007-10-11 04:00:05 +02:00
char * psz_name ;
AudioDeviceID devid_def = 0 ;
2010-07-12 22:19:09 +02:00
int device_id , display_help = 0 ;
2010-07-11 23:08:57 +02:00
const opt_t subopts [ ] = {
2010-07-12 22:19:09 +02:00
{ " device_id " , OPT_ARG_INT , & device_id , NULL } ,
{ " help " , OPT_ARG_BOOL , & display_help , NULL } ,
2010-07-11 23:08:57 +02:00
{ NULL }
} ;
// set defaults
device_id = 0 ;
2010-07-12 22:19:09 +02:00
if ( subopt_parse ( ao_subdevice , subopts ) ! = 0 | | display_help ) {
2010-07-11 23:08:57 +02:00
print_help ( ) ;
2010-07-12 22:19:09 +02:00
if ( ! display_help )
return 0 ;
2010-07-11 23:08:57 +02:00
}
2007-10-11 04:00:05 +02:00
ao_msg ( MSGT_AO , MSGL_V , " init([%dHz][%dch][%s][%d]) \n " , rate , channels , af_fmt2str_short ( format ) , flags ) ;
2005-06-05 16:47:26 +02:00
2009-05-04 16:53:47 +02:00
ao = calloc ( 1 , sizeof ( ao_coreaudio_t ) ) ;
2007-10-11 04:00:05 +02:00
ao - > i_selected_dev = 0 ;
ao - > b_supports_digital = 0 ;
ao - > b_digital = 0 ;
2007-11-11 03:54:57 +01:00
ao - > b_muted = 0 ;
2007-10-11 04:00:05 +02:00
ao - > b_stream_format_changed = 0 ;
ao - > i_hog_pid = - 1 ;
ao - > i_stream_id = 0 ;
ao - > i_stream_index = - 1 ;
ao - > b_revert = 0 ;
ao - > b_changed_mixing = 0 ;
2012-01-06 16:25:52 +01:00
global_ao - > no_persistent_volume = true ;
2010-07-11 23:08:57 +02:00
if ( device_id = = 0 ) {
2007-10-11 04:00:05 +02:00
/* Find the ID of the default Device. */
2010-07-11 23:00:37 +02:00
err = GetAudioProperty ( kAudioObjectSystemObject ,
kAudioHardwarePropertyDefaultOutputDevice ,
sizeof ( UInt32 ) , & devid_def ) ;
2007-10-11 04:00:05 +02:00
if ( err ! = noErr )
{
ao_msg ( MSGT_AO , MSGL_WARN , " could not get default audio device: [%4.4s] \n " , ( char * ) & err ) ;
2007-12-04 13:38:31 +01:00
goto err_out ;
2007-10-11 04:00:05 +02:00
}
2010-07-11 23:08:57 +02:00
} else {
devid_def = device_id ;
}
2007-10-11 04:00:05 +02:00
2010-07-11 23:08:57 +02:00
/* Retrieve the name of the device. */
err = GetAudioPropertyString ( devid_def ,
kAudioObjectPropertyName ,
& psz_name ) ;
if ( err ! = noErr )
{
ao_msg ( MSGT_AO , MSGL_WARN , " could not get default audio device name: [%4.4s] \n " , ( char * ) & err ) ;
goto err_out ;
}
2007-10-11 04:00:05 +02:00
2010-07-11 23:08:57 +02:00
ao_msg ( MSGT_AO , MSGL_V , " got audio output device ID: % " PRIu32 " Name: %s \n " , devid_def , psz_name ) ;
2007-10-11 04:00:05 +02:00
2010-07-11 23:08:57 +02:00
/* Probe whether device support S/PDIF stream output if input is AC3 stream. */
if ( AF_FORMAT_IS_AC3 ( format ) ) {
2007-10-11 04:00:05 +02:00
if ( AudioDeviceSupportsDigital ( devid_def ) )
{
ao - > b_supports_digital = 1 ;
}
2010-08-18 00:39:24 +02:00
ao_msg ( MSGT_AO , MSGL_V ,
" probe default audio output device about support for digital s/pdif output: %d \n " ,
ao - > b_supports_digital ) ;
2007-10-11 04:00:05 +02:00
}
2005-06-05 16:47:26 +02:00
2010-07-11 23:08:57 +02:00
free ( psz_name ) ;
// Save selected device id
ao - > i_selected_dev = devid_def ;
2005-06-05 16:47:26 +02:00
// Build Description for the input format
inDesc . mSampleRate = rate ;
2007-10-11 04:00:05 +02:00
inDesc . mFormatID = ao - > b_supports_digital ? kAudioFormat60958AC3 : kAudioFormatLinearPCM ;
2005-06-05 16:47:26 +02:00
inDesc . mChannelsPerFrame = channels ;
2010-01-11 20:26:33 +01:00
inDesc . mBitsPerChannel = af_fmt2bits ( format ) ;
2003-05-21 23:15:46 +02:00
2005-06-05 16:47:26 +02:00
if ( ( format & AF_FORMAT_POINT_MASK ) = = AF_FORMAT_F ) {
// float
inDesc . mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked ;
2003-05-21 23:15:46 +02:00
}
2005-06-05 16:47:26 +02:00
else if ( ( format & AF_FORMAT_SIGN_MASK ) = = AF_FORMAT_SI ) {
// signed int
inDesc . mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked ;
2003-05-21 23:15:46 +02:00
}
2005-06-05 16:47:26 +02:00
else {
// unsigned int
inDesc . mFormatFlags = kAudioFormatFlagIsPacked ;
}
2010-01-11 21:29:33 +01:00
if ( ( format & AF_FORMAT_END_MASK ) = = AF_FORMAT_BE )
2007-10-11 04:00:05 +02:00
inDesc . mFormatFlags | = kAudioFormatFlagIsBigEndian ;
2005-06-05 16:47:26 +02:00
inDesc . mFramesPerPacket = 1 ;
ao - > packetSize = inDesc . mBytesPerPacket = inDesc . mBytesPerFrame = inDesc . mFramesPerPacket * channels * ( inDesc . mBitsPerChannel / 8 ) ;
2007-10-11 04:00:05 +02:00
print_format ( MSGL_V , " source: " , & inDesc ) ;
if ( ao - > b_supports_digital )
{
b_alive = 1 ;
2010-07-11 23:00:37 +02:00
err = GetAudioProperty ( ao - > i_selected_dev ,
kAudioDevicePropertyDeviceIsAlive ,
sizeof ( UInt32 ) , & b_alive ) ;
2007-10-11 04:00:05 +02:00
if ( err ! = noErr )
ao_msg ( MSGT_AO , MSGL_WARN , " could not check whether device is alive: [%4.4s] \n " , ( char * ) & err ) ;
if ( ! b_alive )
ao_msg ( MSGT_AO , MSGL_WARN , " device is not alive \n " ) ;
2010-07-11 23:08:57 +02:00
2007-10-11 04:00:05 +02:00
/* S/PDIF output need device in HogMode. */
2010-07-11 23:00:37 +02:00
err = GetAudioProperty ( ao - > i_selected_dev ,
kAudioDevicePropertyHogMode ,
sizeof ( pid_t ) , & ao - > i_hog_pid ) ;
2007-10-11 04:00:05 +02:00
if ( err ! = noErr )
{
/* This is not a fatal error. Some drivers simply don't support this property. */
ao_msg ( MSGT_AO , MSGL_WARN , " could not check whether device is hogged: [%4.4s] \n " ,
( char * ) & err ) ;
ao - > i_hog_pid = - 1 ;
}
if ( ao - > i_hog_pid ! = - 1 & & ao - > i_hog_pid ! = getpid ( ) )
{
ao_msg ( MSGT_AO , MSGL_WARN , " Selected audio device is exclusively in use by another program. \n " ) ;
2007-12-04 13:38:31 +01:00
goto err_out ;
2007-10-11 04:00:05 +02:00
}
ao - > stream_format = inDesc ;
return OpenSPDIF ( ) ;
}
2005-01-01 04:51:08 +01:00
2007-10-11 04:00:05 +02:00
/* original analog output code */
2005-06-05 16:47:26 +02:00
desc . componentType = kAudioUnitType_Output ;
2010-07-11 23:08:57 +02:00
desc . componentSubType = ( device_id = = 0 ) ? kAudioUnitSubType_DefaultOutput : kAudioUnitSubType_HALOutput ;
2005-06-05 16:47:26 +02:00
desc . componentManufacturer = kAudioUnitManufacturer_Apple ;
desc . componentFlags = 0 ;
desc . componentFlagsMask = 0 ;
2009-05-13 04:58:57 +02:00
2005-06-05 16:47:26 +02:00
comp = FindNextComponent ( NULL , & desc ) ; //Finds an component that meets the desc spec's
if ( comp = = NULL ) {
ao_msg ( MSGT_AO , MSGL_WARN , " Unable to find Output Unit component \n " ) ;
2007-12-04 13:38:31 +01:00
goto err_out ;
2005-06-05 16:47:26 +02:00
}
2009-05-13 04:58:57 +02:00
2005-06-05 16:47:26 +02:00
err = OpenAComponent ( comp , & ( ao - > theOutputUnit ) ) ; //gains access to the services provided by the component
if ( err ) {
2007-10-11 04:00:05 +02:00
ao_msg ( MSGT_AO , MSGL_WARN , " Unable to open Output Unit component: [%4.4s] \n " , ( char * ) & err ) ;
2007-12-04 13:38:31 +01:00
goto err_out ;
2005-06-05 16:47:26 +02:00
}
2005-01-01 04:51:08 +01:00
2009-05-13 04:58:57 +02:00
// Initialize AudioUnit
2005-06-05 16:47:26 +02:00
err = AudioUnitInitialize ( ao - > theOutputUnit ) ;
if ( err ) {
2007-10-11 04:00:05 +02:00
ao_msg ( MSGT_AO , MSGL_WARN , " Unable to initialize Output Unit component: [%4.4s] \n " , ( char * ) & err ) ;
2007-12-04 13:38:31 +01:00
goto err_out1 ;
2005-06-05 16:47:26 +02:00
}
2005-01-03 04:34:18 +01:00
2005-06-05 16:47:26 +02:00
size = sizeof ( AudioStreamBasicDescription ) ;
2005-06-14 14:41:42 +02:00
err = AudioUnitSetProperty ( ao - > theOutputUnit , kAudioUnitProperty_StreamFormat , kAudioUnitScope_Input , 0 , & inDesc , size ) ;
2005-01-03 04:34:18 +01:00
2005-06-05 16:47:26 +02:00
if ( err ) {
2007-10-11 04:00:05 +02:00
ao_msg ( MSGT_AO , MSGL_WARN , " Unable to set the input format: [%4.4s] \n " , ( char * ) & err ) ;
2007-12-04 13:38:31 +01:00
goto err_out2 ;
2005-06-05 16:47:26 +02:00
}
2003-05-21 23:15:46 +02:00
2005-07-19 16:36:13 +02:00
size = sizeof ( UInt32 ) ;
err = AudioUnitGetProperty ( ao - > theOutputUnit , kAudioDevicePropertyBufferSize , kAudioUnitScope_Input , 0 , & maxFrames , & size ) ;
2009-05-13 04:58:57 +02:00
2005-07-19 16:36:13 +02:00
if ( err )
{
2007-10-11 04:00:05 +02:00
ao_msg ( MSGT_AO , MSGL_WARN , " AudioUnitGetProperty returned [%4.4s] when getting kAudioDevicePropertyBufferSize \n " , ( char * ) & err ) ;
2007-12-04 13:38:31 +01:00
goto err_out2 ;
2005-06-05 16:47:26 +02:00
}
2003-05-22 18:12:54 +02:00
2010-07-11 23:08:57 +02:00
//Set the Current Device to the Default Output Unit.
err = AudioUnitSetProperty ( ao - > theOutputUnit , kAudioOutputUnitProperty_CurrentDevice , kAudioUnitScope_Global , 0 , & ao - > i_selected_dev , sizeof ( ao - > i_selected_dev ) ) ;
2005-07-19 16:36:13 +02:00
ao - > chunk_size = maxFrames ; //*inDesc.mBytesPerFrame;
2009-05-13 04:58:57 +02:00
2006-08-18 03:19:19 +02:00
ao_data . samplerate = inDesc . mSampleRate ;
ao_data . channels = inDesc . mChannelsPerFrame ;
ao_data . bps = ao_data . samplerate * inDesc . mBytesPerFrame ;
ao_data . outburst = ao - > chunk_size ;
ao_data . buffersize = ao_data . bps ;
ao - > num_chunks = ( ao_data . bps + ao - > chunk_size - 1 ) / ao - > chunk_size ;
2009-05-03 22:57:37 +02:00
ao - > buffer_len = ao - > num_chunks * ao - > chunk_size ;
ao - > buffer = av_fifo_alloc ( ao - > buffer_len ) ;
2009-05-13 04:58:57 +02:00
2006-08-18 03:19:19 +02:00
ao_msg ( MSGT_AO , MSGL_V , " using %5d chunks of %d bytes (buffer len %d bytes) \n " , ( int ) ao - > num_chunks , ( int ) ao - > chunk_size , ( int ) ao - > buffer_len ) ;
2005-06-05 16:47:26 +02:00
renderCallback . inputProc = theRenderProc ;
renderCallback . inputProcRefCon = 0 ;
err = AudioUnitSetProperty ( ao - > theOutputUnit , kAudioUnitProperty_SetRenderCallback , kAudioUnitScope_Input , 0 , & renderCallback , sizeof ( AURenderCallbackStruct ) ) ;
if ( err ) {
2007-10-11 04:00:05 +02:00
ao_msg ( MSGT_AO , MSGL_WARN , " Unable to set the render callback: [%4.4s] \n " , ( char * ) & err ) ;
2007-12-04 13:38:31 +01:00
goto err_out2 ;
2005-06-05 16:47:26 +02:00
}
2003-05-21 23:15:46 +02:00
2006-08-18 03:19:19 +02:00
reset ( ) ;
2009-05-13 04:58:57 +02:00
2003-05-21 23:15:46 +02:00
return CONTROL_OK ;
2007-12-04 13:38:31 +01:00
err_out2 :
AudioUnitUninitialize ( ao - > theOutputUnit ) ;
err_out1 :
CloseComponent ( ao - > theOutputUnit ) ;
err_out :
2009-05-03 22:57:37 +02:00
av_fifo_free ( ao - > buffer ) ;
2007-12-04 13:38:31 +01:00
free ( ao ) ;
ao = NULL ;
2009-05-13 04:58:57 +02:00
return CONTROL_FALSE ;
2003-05-21 23:15:46 +02:00
}
2007-10-11 04:00:05 +02:00
/*****************************************************************************
* Setup a encoded digital stream ( SPDIF )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-05-04 19:35:26 +02:00
static int OpenSPDIF ( void )
2007-10-11 04:00:05 +02:00
{
2010-07-11 23:08:57 +02:00
OSStatus err = noErr ;
UInt32 i_param_size , b_mix = 0 ;
Boolean b_writeable = 0 ;
AudioStreamID * p_streams = NULL ;
int i , i_streams = 0 ;
2010-07-11 23:00:37 +02:00
AudioObjectPropertyAddress property_address ;
2007-10-11 04:00:05 +02:00
/* Start doing the SPDIF setup process. */
ao - > b_digital = 1 ;
/* Hog the device. */
ao - > i_hog_pid = getpid ( ) ;
2010-07-11 23:00:37 +02:00
err = SetAudioProperty ( ao - > i_selected_dev ,
kAudioDevicePropertyHogMode ,
sizeof ( ao - > i_hog_pid ) , & ao - > i_hog_pid ) ;
2007-10-11 04:00:05 +02:00
if ( err ! = noErr )
{
ao_msg ( MSGT_AO , MSGL_WARN , " failed to set hogmode: [%4.4s] \n " , ( char * ) & err ) ;
2007-12-04 13:38:31 +01:00
ao - > i_hog_pid = - 1 ;
goto err_out ;
2007-10-11 04:00:05 +02:00
}
2011-08-19 15:20:43 +02:00
property_address . mSelector = kAudioDevicePropertySupportsMixing ;
property_address . mScope = kAudioObjectPropertyScopeGlobal ;
property_address . mElement = kAudioObjectPropertyElementMaster ;
2007-10-11 04:00:05 +02:00
/* Set mixable to false if we are allowed to. */
2011-08-19 15:20:43 +02:00
if ( AudioObjectHasProperty ( ao - > i_selected_dev , & property_address ) ) {
/* Set mixable to false if we are allowed to. */
err = IsAudioPropertySettable ( ao - > i_selected_dev ,
kAudioDevicePropertySupportsMixing ,
& b_writeable ) ;
err = GetAudioProperty ( ao - > i_selected_dev ,
2010-07-11 23:00:37 +02:00
kAudioDevicePropertySupportsMixing ,
sizeof ( UInt32 ) , & b_mix ) ;
2011-08-19 15:20:43 +02:00
if ( err = = noErr & & b_writeable )
{
b_mix = 0 ;
err = SetAudioProperty ( ao - > i_selected_dev ,
kAudioDevicePropertySupportsMixing ,
sizeof ( UInt32 ) , & b_mix ) ;
ao - > b_changed_mixing = 1 ;
}
if ( err ! = noErr )
{
ao_msg ( MSGT_AO , MSGL_WARN , " failed to set mixmode: [%4.4s] \n " , ( char * ) & err ) ;
goto err_out ;
}
2007-10-11 04:00:05 +02:00
}
/* Get a list of all the streams on this device. */
2010-07-11 23:00:37 +02:00
i_param_size = GetAudioPropertyArray ( ao - > i_selected_dev ,
kAudioDevicePropertyStreams ,
kAudioDevicePropertyScopeOutput ,
( void * * ) & p_streams ) ;
2007-10-11 04:00:05 +02:00
2010-07-11 23:00:37 +02:00
if ( ! i_param_size ) {
ao_msg ( MSGT_AO , MSGL_WARN , " could not get number of streams. \n " ) ;
2007-12-04 13:38:31 +01:00
goto err_out ;
2007-10-11 04:00:05 +02:00
}
2010-07-11 23:00:37 +02:00
i_streams = i_param_size / sizeof ( AudioStreamID ) ;
2007-10-11 04:00:05 +02:00
ao_msg ( MSGT_AO , MSGL_V , " current device stream number: %d \n " , i_streams ) ;
for ( i = 0 ; i < i_streams & & ao - > i_stream_index < 0 ; + + i )
{
/* Find a stream with a cac3 stream. */
2011-08-19 15:20:43 +02:00
AudioStreamRangedDescription * p_format_list = NULL ;
2007-10-11 04:00:05 +02:00
int i_formats = 0 , j = 0 , b_digital = 0 ;
2010-07-11 23:00:37 +02:00
i_param_size = GetGlobalAudioPropertyArray ( p_streams [ i ] ,
2011-08-19 15:20:43 +02:00
kAudioStreamPropertyAvailablePhysicalFormats ,
2010-07-11 23:00:37 +02:00
( void * * ) & p_format_list ) ;
2007-10-11 04:00:05 +02:00
2010-07-11 23:00:37 +02:00
if ( ! i_param_size ) {
2010-08-18 00:39:24 +02:00
ao_msg ( MSGT_AO , MSGL_WARN ,
" Could not get number of stream formats. \n " ) ;
2007-10-11 04:00:05 +02:00
continue ;
}
2011-08-19 15:20:43 +02:00
i_formats = i_param_size / sizeof ( AudioStreamRangedDescription ) ;
2007-10-11 04:00:05 +02:00
/* Check if one of the supported formats is a digital format. */
for ( j = 0 ; j < i_formats ; + + j )
{
2011-08-19 15:20:43 +02:00
if ( p_format_list [ j ] . mFormat . mFormatID = = ' IAC3 ' | |
p_format_list [ j ] . mFormat . mFormatID = = ' iac3 ' | |
p_format_list [ j ] . mFormat . mFormatID = = kAudioFormat60958AC3 | |
p_format_list [ j ] . mFormat . mFormatID = = kAudioFormatAC3 )
2007-10-11 04:00:05 +02:00
{
b_digital = 1 ;
break ;
}
}
if ( b_digital )
{
/* If this stream supports a digital (cac3) format, then set it. */
int i_requested_rate_format = - 1 ;
int i_current_rate_format = - 1 ;
int i_backup_rate_format = - 1 ;
ao - > i_stream_id = p_streams [ i ] ;
ao - > i_stream_index = i ;
if ( ao - > b_revert = = 0 )
{
/* Retrieve the original format of this stream first if not done so already. */
2010-07-11 23:00:37 +02:00
err = GetAudioProperty ( ao - > i_stream_id ,
kAudioStreamPropertyPhysicalFormat ,
sizeof ( ao - > sfmt_revert ) , & ao - > sfmt_revert ) ;
2007-10-11 04:00:05 +02:00
if ( err ! = noErr )
{
2010-08-18 00:39:24 +02:00
ao_msg ( MSGT_AO , MSGL_WARN ,
" Could not retrieve the original stream format: [%4.4s] \n " ,
( char * ) & err ) ;
2010-11-14 10:12:34 +01:00
free ( p_format_list ) ;
2007-10-11 04:00:05 +02:00
continue ;
}
ao - > b_revert = 1 ;
}
for ( j = 0 ; j < i_formats ; + + j )
2011-08-19 15:20:43 +02:00
if ( p_format_list [ j ] . mFormat . mFormatID = = ' IAC3 ' | |
p_format_list [ j ] . mFormat . mFormatID = = ' iac3 ' | |
p_format_list [ j ] . mFormat . mFormatID = = kAudioFormat60958AC3 | |
p_format_list [ j ] . mFormat . mFormatID = = kAudioFormatAC3 )
2007-10-11 04:00:05 +02:00
{
2011-08-19 15:20:43 +02:00
if ( p_format_list [ j ] . mFormat . mSampleRate = = ao - > stream_format . mSampleRate )
2007-10-11 04:00:05 +02:00
{
i_requested_rate_format = j ;
break ;
}
2011-08-19 15:20:43 +02:00
if ( p_format_list [ j ] . mFormat . mSampleRate = = ao - > sfmt_revert . mSampleRate )
2007-10-11 04:00:05 +02:00
i_current_rate_format = j ;
2011-08-19 15:20:43 +02:00
else if ( i_backup_rate_format < 0 | | p_format_list [ j ] . mFormat . mSampleRate > p_format_list [ i_backup_rate_format ] . mFormat . mSampleRate )
2007-10-11 04:00:05 +02:00
i_backup_rate_format = j ;
}
if ( i_requested_rate_format > = 0 ) /* We prefer to output at the samplerate of the original audio. */
2011-08-19 15:20:43 +02:00
ao - > stream_format = p_format_list [ i_requested_rate_format ] . mFormat ;
2007-10-11 04:00:05 +02:00
else if ( i_current_rate_format > = 0 ) /* If not possible, we will try to use the current samplerate of the device. */
2011-08-19 15:20:43 +02:00
ao - > stream_format = p_format_list [ i_current_rate_format ] . mFormat ;
else ao - > stream_format = p_format_list [ i_backup_rate_format ] . mFormat ; /* And if we have to, any digital format will be just fine (highest rate possible). */
2007-10-11 04:00:05 +02:00
}
2010-11-14 10:12:34 +01:00
free ( p_format_list ) ;
2007-10-11 04:00:05 +02:00
}
2010-11-14 10:12:34 +01:00
free ( p_streams ) ;
2007-10-11 04:00:05 +02:00
if ( ao - > i_stream_index < 0 )
{
2010-08-18 00:39:24 +02:00
ao_msg ( MSGT_AO , MSGL_WARN ,
" Cannot find any digital output stream format when OpenSPDIF(). \n " ) ;
2007-12-04 13:38:31 +01:00
goto err_out ;
2007-10-11 04:00:05 +02:00
}
print_format ( MSGL_V , " original stream format: " , & ao - > sfmt_revert ) ;
if ( ! AudioStreamChangeFormat ( ao - > i_stream_id , ao - > stream_format ) )
2007-12-04 13:38:31 +01:00
goto err_out ;
2007-10-11 04:00:05 +02:00
2010-07-11 23:00:37 +02:00
property_address . mSelector = kAudioDevicePropertyDeviceHasChanged ;
property_address . mScope = kAudioObjectPropertyScopeGlobal ;
property_address . mElement = kAudioObjectPropertyElementMaster ;
err = AudioObjectAddPropertyListener ( ao - > i_selected_dev ,
& property_address ,
2007-10-11 04:00:05 +02:00
DeviceListener ,
NULL ) ;
if ( err ! = noErr )
ao_msg ( MSGT_AO , MSGL_WARN , " AudioDeviceAddPropertyListener for kAudioDevicePropertyDeviceHasChanged failed: [%4.4s] \n " , ( char * ) & err ) ;
/* FIXME: If output stream is not native byte-order, we need change endian somewhere. */
/* Although there's no such case reported. */
2009-07-26 21:53:00 +02:00
# if HAVE_BIGENDIAN
2007-10-11 04:00:05 +02:00
if ( ! ( ao - > stream_format . mFormatFlags & kAudioFormatFlagIsBigEndian ) )
# else
2010-10-08 10:56:20 +02:00
/* tell mplayer that we need a byteswap on AC3 streams, */
if ( ao - > stream_format . mFormatID & kAudioFormat60958AC3 )
ao_data . format = AF_FORMAT_AC3_LE ;
2007-10-11 04:00:05 +02:00
if ( ao - > stream_format . mFormatFlags & kAudioFormatFlagIsBigEndian )
# endif
2010-08-18 00:39:24 +02:00
ao_msg ( MSGT_AO , MSGL_WARN ,
" Output stream has non-native byte order, digital output may fail. \n " ) ;
2007-10-11 04:00:05 +02:00
/* For ac3/dts, just use packet size 6144 bytes as chunk size. */
ao - > chunk_size = ao - > stream_format . mBytesPerPacket ;
ao_data . samplerate = ao - > stream_format . mSampleRate ;
ao_data . channels = ao - > stream_format . mChannelsPerFrame ;
ao_data . bps = ao_data . samplerate * ( ao - > stream_format . mBytesPerPacket / ao - > stream_format . mFramesPerPacket ) ;
ao_data . outburst = ao - > chunk_size ;
ao_data . buffersize = ao_data . bps ;
ao - > num_chunks = ( ao_data . bps + ao - > chunk_size - 1 ) / ao - > chunk_size ;
2009-05-03 22:57:37 +02:00
ao - > buffer_len = ao - > num_chunks * ao - > chunk_size ;
ao - > buffer = av_fifo_alloc ( ao - > buffer_len ) ;
2007-10-11 04:00:05 +02:00
ao_msg ( MSGT_AO , MSGL_V , " using %5d chunks of %d bytes (buffer len %d bytes) \n " , ( int ) ao - > num_chunks , ( int ) ao - > chunk_size , ( int ) ao - > buffer_len ) ;
2010-07-11 23:00:37 +02:00
/* Create IOProc callback. */
err = AudioDeviceCreateIOProcID ( ao - > i_selected_dev ,
( AudioDeviceIOProc ) RenderCallbackSPDIF ,
( void * ) ao ,
& ao - > renderCallback ) ;
if ( err ! = noErr | | ao - > renderCallback = = NULL )
2007-10-11 04:00:05 +02:00
{
ao_msg ( MSGT_AO , MSGL_WARN , " AudioDeviceAddIOProc failed: [%4.4s] \n " , ( char * ) & err ) ;
2007-12-04 13:38:31 +01:00
goto err_out1 ;
2007-10-11 04:00:05 +02:00
}
reset ( ) ;
return CONTROL_TRUE ;
2007-12-04 13:38:31 +01:00
err_out1 :
if ( ao - > b_revert )
AudioStreamChangeFormat ( ao - > i_stream_id , ao - > sfmt_revert ) ;
err_out :
if ( ao - > b_changed_mixing & & ao - > sfmt_revert . mFormatID ! = kAudioFormat60958AC3 )
{
int b_mix = 1 ;
2010-07-11 23:00:37 +02:00
err = SetAudioProperty ( ao - > i_selected_dev ,
kAudioDevicePropertySupportsMixing ,
sizeof ( int ) , & b_mix ) ;
2007-12-04 13:38:31 +01:00
if ( err ! = noErr )
ao_msg ( MSGT_AO , MSGL_WARN , " failed to set mixmode: [%4.4s] \n " ,
( char * ) & err ) ;
}
if ( ao - > i_hog_pid = = getpid ( ) )
{
ao - > i_hog_pid = - 1 ;
2010-07-11 23:00:37 +02:00
err = SetAudioProperty ( ao - > i_selected_dev ,
kAudioDevicePropertyHogMode ,
sizeof ( ao - > i_hog_pid ) , & ao - > i_hog_pid ) ;
2007-12-04 13:38:31 +01:00
if ( err ! = noErr )
ao_msg ( MSGT_AO , MSGL_WARN , " Could not release hogmode: [%4.4s] \n " ,
( char * ) & err ) ;
}
2009-05-03 22:57:37 +02:00
av_fifo_free ( ao - > buffer ) ;
2007-12-04 13:38:31 +01:00
free ( ao ) ;
ao = NULL ;
2009-05-13 04:58:57 +02:00
return CONTROL_FALSE ;
2007-10-11 04:00:05 +02:00
}
/*****************************************************************************
* AudioDeviceSupportsDigital : Check i_dev_id for digital stream support .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int AudioDeviceSupportsDigital ( AudioDeviceID i_dev_id )
{
UInt32 i_param_size = 0 ;
AudioStreamID * p_streams = NULL ;
int i = 0 , i_streams = 0 ;
int b_return = CONTROL_FALSE ;
/* Retrieve all the output streams. */
2010-07-11 23:00:37 +02:00
i_param_size = GetAudioPropertyArray ( i_dev_id ,
kAudioDevicePropertyStreams ,
kAudioDevicePropertyScopeOutput ,
( void * * ) & p_streams ) ;
2007-10-11 04:00:05 +02:00
2010-07-11 23:00:37 +02:00
if ( ! i_param_size ) {
ao_msg ( MSGT_AO , MSGL_WARN , " could not get number of streams. \n " ) ;
2007-10-11 04:00:05 +02:00
return CONTROL_FALSE ;
}
2010-07-11 23:00:37 +02:00
i_streams = i_param_size / sizeof ( AudioStreamID ) ;
2007-10-11 04:00:05 +02:00
for ( i = 0 ; i < i_streams ; + + i )
{
if ( AudioStreamSupportsDigital ( p_streams [ i ] ) )
b_return = CONTROL_OK ;
}
free ( p_streams ) ;
return b_return ;
}
/*****************************************************************************
* AudioStreamSupportsDigital : Check i_stream_id for digital stream support .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int AudioStreamSupportsDigital ( AudioStreamID i_stream_id )
{
UInt32 i_param_size ;
2011-08-19 15:20:43 +02:00
AudioStreamRangedDescription * p_format_list = NULL ;
2007-10-11 04:00:05 +02:00
int i , i_formats , b_return = CONTROL_FALSE ;
/* Retrieve all the stream formats supported by each output stream. */
2010-07-11 23:00:37 +02:00
i_param_size = GetGlobalAudioPropertyArray ( i_stream_id ,
2011-08-19 15:20:43 +02:00
kAudioStreamPropertyAvailablePhysicalFormats ,
2010-07-11 23:00:37 +02:00
( void * * ) & p_format_list ) ;
2007-10-11 04:00:05 +02:00
2010-07-11 23:00:37 +02:00
if ( ! i_param_size ) {
2010-08-18 00:39:24 +02:00
ao_msg ( MSGT_AO , MSGL_WARN , " Could not get number of stream formats. \n " ) ;
2007-10-11 04:00:05 +02:00
return CONTROL_FALSE ;
}
2011-08-19 15:20:43 +02:00
i_formats = i_param_size / sizeof ( AudioStreamRangedDescription ) ;
2007-10-11 04:00:05 +02:00
for ( i = 0 ; i < i_formats ; + + i )
{
2011-08-19 15:20:43 +02:00
print_format ( MSGL_V , " supported format: " , & ( p_format_list [ i ] . mFormat ) ) ;
2007-10-11 04:00:05 +02:00
2011-08-19 15:20:43 +02:00
if ( p_format_list [ i ] . mFormat . mFormatID = = ' IAC3 ' | |
p_format_list [ i ] . mFormat . mFormatID = = ' iac3 ' | |
p_format_list [ i ] . mFormat . mFormatID = = kAudioFormat60958AC3 | |
p_format_list [ i ] . mFormat . mFormatID = = kAudioFormatAC3 )
2007-10-11 04:00:05 +02:00
b_return = CONTROL_OK ;
}
free ( p_format_list ) ;
return b_return ;
}
/*****************************************************************************
* AudioStreamChangeFormat : Change i_stream_id to change_format
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int AudioStreamChangeFormat ( AudioStreamID i_stream_id , AudioStreamBasicDescription change_format )
{
OSStatus err = noErr ;
int i ;
2010-07-11 23:00:37 +02:00
AudioObjectPropertyAddress property_address ;
2007-10-11 04:00:05 +02:00
2007-11-10 03:07:34 +01:00
static volatile int stream_format_changed ;
stream_format_changed = 0 ;
2007-10-11 04:00:05 +02:00
print_format ( MSGL_V , " setting stream format: " , & change_format ) ;
/* Install the callback. */
2010-07-11 23:00:37 +02:00
property_address . mSelector = kAudioStreamPropertyPhysicalFormat ;
property_address . mScope = kAudioObjectPropertyScopeGlobal ;
property_address . mElement = kAudioObjectPropertyElementMaster ;
err = AudioObjectAddPropertyListener ( i_stream_id ,
& property_address ,
2007-11-10 03:07:34 +01:00
StreamListener ,
( void * ) & stream_format_changed ) ;
2007-10-11 04:00:05 +02:00
if ( err ! = noErr )
{
ao_msg ( MSGT_AO , MSGL_WARN , " AudioStreamAddPropertyListener failed: [%4.4s] \n " , ( char * ) & err ) ;
return CONTROL_FALSE ;
}
/* Change the format. */
2010-07-11 23:00:37 +02:00
err = SetAudioProperty ( i_stream_id ,
kAudioStreamPropertyPhysicalFormat ,
sizeof ( AudioStreamBasicDescription ) , & change_format ) ;
2007-10-11 04:00:05 +02:00
if ( err ! = noErr )
{
ao_msg ( MSGT_AO , MSGL_WARN , " could not set the stream format: [%4.4s] \n " , ( char * ) & err ) ;
return CONTROL_FALSE ;
}
2007-11-10 03:07:34 +01:00
/* The AudioStreamSetProperty is not only asynchronious,
2007-10-11 04:00:05 +02:00
* it is also not Atomic , in its behaviour .
* Therefore we check 5 times before we really give up .
* FIXME : failing isn ' t actually implemented yet . */
for ( i = 0 ; i < 5 ; + + i )
{
AudioStreamBasicDescription actual_format ;
2007-11-10 03:07:34 +01:00
int j ;
for ( j = 0 ; ! stream_format_changed & & j < 50 ; + + j )
usec_sleep ( 10000 ) ;
if ( stream_format_changed )
stream_format_changed = 0 ;
else
2007-10-11 04:00:05 +02:00
ao_msg ( MSGT_AO , MSGL_V , " reached timeout \n " ) ;
2010-07-11 23:00:37 +02:00
err = GetAudioProperty ( i_stream_id ,
kAudioStreamPropertyPhysicalFormat ,
sizeof ( AudioStreamBasicDescription ) , & actual_format ) ;
2007-10-11 04:00:05 +02:00
print_format ( MSGL_V , " actual format in use: " , & actual_format ) ;
if ( actual_format . mSampleRate = = change_format . mSampleRate & &
actual_format . mFormatID = = change_format . mFormatID & &
actual_format . mFramesPerPacket = = change_format . mFramesPerPacket )
{
/* The right format is now active. */
break ;
}
/* We need to check again. */
}
/* Removing the property listener. */
2010-07-11 23:00:37 +02:00
err = AudioObjectRemovePropertyListener ( i_stream_id ,
& property_address ,
StreamListener ,
( void * ) & stream_format_changed ) ;
2007-10-11 04:00:05 +02:00
if ( err ! = noErr )
{
ao_msg ( MSGT_AO , MSGL_WARN , " AudioStreamRemovePropertyListener failed: [%4.4s] \n " , ( char * ) & err ) ;
return CONTROL_FALSE ;
}
return CONTROL_TRUE ;
}
/*****************************************************************************
* RenderCallbackSPDIF : callback for SPDIF audio output
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static OSStatus RenderCallbackSPDIF ( AudioDeviceID inDevice ,
const AudioTimeStamp * inNow ,
const void * inInputData ,
const AudioTimeStamp * inInputTime ,
AudioBufferList * outOutputData ,
const AudioTimeStamp * inOutputTime ,
void * threadGlobals )
{
2009-05-03 22:57:37 +02:00
int amt = av_fifo_size ( ao - > buffer ) ;
2007-10-11 04:00:05 +02:00
int req = outOutputData - > mBuffers [ ao - > i_stream_index ] . mDataByteSize ;
if ( amt > req )
amt = req ;
if ( amt )
2007-11-11 03:54:57 +01:00
read_buffer ( ao - > b_muted ? NULL : ( unsigned char * ) outOutputData - > mBuffers [ ao - > i_stream_index ] . mData , amt ) ;
2007-10-11 04:00:05 +02:00
return noErr ;
}
2003-05-21 23:15:46 +02:00
static int play ( void * output_samples , int num_bytes , int flags )
2009-05-13 04:58:57 +02:00
{
2007-10-11 04:00:05 +02:00
int wrote , b_digital ;
2010-07-11 23:14:48 +02:00
SInt32 exit_reason ;
2007-10-11 04:00:05 +02:00
// Check whether we need to reset the digital output stream.
if ( ao - > b_digital & & ao - > b_stream_format_changed )
{
ao - > b_stream_format_changed = 0 ;
b_digital = AudioStreamSupportsDigital ( ao - > i_stream_id ) ;
if ( b_digital )
{
2010-08-18 00:39:24 +02:00
/* Current stream supports digital format output, let's set it. */
ao_msg ( MSGT_AO , MSGL_V ,
" Detected current stream supports digital, try to restore digital output... \n " ) ;
2007-10-11 04:00:05 +02:00
if ( ! AudioStreamChangeFormat ( ao - > i_stream_id , ao - > stream_format ) )
{
2010-08-18 00:39:24 +02:00
ao_msg ( MSGT_AO , MSGL_WARN , " Restoring digital output failed. \n " ) ;
2007-10-11 04:00:05 +02:00
}
else
{
2010-08-18 00:39:24 +02:00
ao_msg ( MSGT_AO , MSGL_WARN , " Restoring digital output succeeded. \n " ) ;
2007-10-11 04:00:05 +02:00
reset ( ) ;
}
}
else
2010-08-18 00:39:24 +02:00
ao_msg ( MSGT_AO , MSGL_V , " Detected current stream does not support digital. \n " ) ;
2007-10-11 04:00:05 +02:00
}
2006-08-18 03:19:19 +02:00
2007-10-11 04:00:05 +02:00
wrote = write_buffer ( output_samples , num_bytes ) ;
audio_resume ( ) ;
2010-07-11 23:14:48 +02:00
do {
exit_reason = CFRunLoopRunInMode ( kCFRunLoopDefaultMode , 0.01 , true ) ;
} while ( exit_reason = = kCFRunLoopRunHandledSource ) ;
2007-10-11 04:00:05 +02:00
return wrote ;
2003-05-21 23:15:46 +02:00
}
/* set variables and buffer to initial state */
2006-07-06 06:30:19 +02:00
static void reset ( void )
2003-05-21 23:15:46 +02:00
{
2005-02-22 21:54:31 +01:00
audio_pause ( ) ;
2009-05-03 22:57:37 +02:00
av_fifo_reset ( ao - > buffer ) ;
2003-05-21 23:15:46 +02:00
}
/* return available space */
2006-07-06 06:30:19 +02:00
static int get_space ( void )
2003-05-21 23:15:46 +02:00
{
2009-05-03 22:57:37 +02:00
return ao - > buffer_len - av_fifo_size ( ao - > buffer ) ;
2003-05-21 23:15:46 +02:00
}
2003-05-22 18:12:54 +02:00
/* return delay until audio is played */
2006-07-06 06:30:19 +02:00
static float get_delay ( void )
2003-05-21 23:15:46 +02:00
{
2005-02-22 21:54:31 +01:00
// inaccurate, should also contain the data buffered e.g. by the OS
2009-05-03 22:57:37 +02:00
return ( float ) av_fifo_size ( ao - > buffer ) / ( float ) ao_data . bps ;
2003-05-21 23:15:46 +02:00
}
/* unload plugin and deregister from coreaudio */
2004-04-06 19:55:36 +02:00
static void uninit ( int immed )
2003-05-21 23:15:46 +02:00
{
2007-10-11 04:00:05 +02:00
OSStatus err = noErr ;
2003-05-21 23:15:46 +02:00
2006-08-18 03:19:19 +02:00
if ( ! immed ) {
2009-05-03 22:57:37 +02:00
long long timeleft = ( 1000000LL * av_fifo_size ( ao - > buffer ) ) / ao_data . bps ;
ao_msg ( MSGT_AO , MSGL_DBG2 , " %d bytes left @%d bps (%d usec) \n " , av_fifo_size ( ao - > buffer ) , ao_data . bps , ( int ) timeleft ) ;
2006-08-18 03:19:19 +02:00
usec_sleep ( ( int ) timeleft ) ;
}
2003-05-21 23:15:46 +02:00
2007-10-11 04:00:05 +02:00
if ( ! ao - > b_digital ) {
AudioOutputUnitStop ( ao - > theOutputUnit ) ;
AudioUnitUninitialize ( ao - > theOutputUnit ) ;
CloseComponent ( ao - > theOutputUnit ) ;
}
else {
/* Stop device. */
2010-07-11 23:00:37 +02:00
err = AudioDeviceStop ( ao - > i_selected_dev , ao - > renderCallback ) ;
2007-10-11 04:00:05 +02:00
if ( err ! = noErr )
ao_msg ( MSGT_AO , MSGL_WARN , " AudioDeviceStop failed: [%4.4s] \n " , ( char * ) & err ) ;
/* Remove IOProc callback. */
2010-07-11 23:00:37 +02:00
err = AudioDeviceDestroyIOProcID ( ao - > i_selected_dev , ao - > renderCallback ) ;
2007-10-11 04:00:05 +02:00
if ( err ! = noErr )
ao_msg ( MSGT_AO , MSGL_WARN , " AudioDeviceRemoveIOProc failed: [%4.4s] \n " , ( char * ) & err ) ;
if ( ao - > b_revert )
AudioStreamChangeFormat ( ao - > i_stream_id , ao - > sfmt_revert ) ;
if ( ao - > b_changed_mixing & & ao - > sfmt_revert . mFormatID ! = kAudioFormat60958AC3 )
{
2010-07-11 23:02:53 +02:00
UInt32 b_mix ;
2011-08-19 15:20:43 +02:00
Boolean b_writeable = 0 ;
2007-10-11 04:00:05 +02:00
/* Revert mixable to true if we are allowed to. */
2010-07-11 23:00:37 +02:00
err = IsAudioPropertySettable ( ao - > i_selected_dev ,
kAudioDevicePropertySupportsMixing ,
& b_writeable ) ;
err = GetAudioProperty ( ao - > i_selected_dev ,
kAudioDevicePropertySupportsMixing ,
sizeof ( UInt32 ) , & b_mix ) ;
2011-08-19 15:20:43 +02:00
if ( err = = noErr & & b_writeable )
2007-10-11 04:00:05 +02:00
{
b_mix = 1 ;
2010-07-11 23:00:37 +02:00
err = SetAudioProperty ( ao - > i_selected_dev ,
kAudioDevicePropertySupportsMixing ,
sizeof ( UInt32 ) , & b_mix ) ;
2007-10-11 04:00:05 +02:00
}
if ( err ! = noErr )
ao_msg ( MSGT_AO , MSGL_WARN , " failed to set mixmode: [%4.4s] \n " , ( char * ) & err ) ;
}
if ( ao - > i_hog_pid = = getpid ( ) )
{
ao - > i_hog_pid = - 1 ;
2010-07-11 23:00:37 +02:00
err = SetAudioProperty ( ao - > i_selected_dev ,
kAudioDevicePropertyHogMode ,
sizeof ( ao - > i_hog_pid ) , & ao - > i_hog_pid ) ;
2007-10-11 04:00:05 +02:00
if ( err ! = noErr ) ao_msg ( MSGT_AO , MSGL_WARN , " Could not release hogmode: [%4.4s] \n " , ( char * ) & err ) ;
}
}
2003-05-21 23:15:46 +02:00
2009-05-03 22:57:37 +02:00
av_fifo_free ( ao - > buffer ) ;
2003-05-21 23:15:46 +02:00
free ( ao ) ;
2005-06-29 20:17:19 +02:00
ao = NULL ;
2003-05-21 23:15:46 +02:00
}
/* stop playing, keep buffers (for pause) */
2006-07-06 06:30:19 +02:00
static void audio_pause ( void )
2003-05-21 23:15:46 +02:00
{
2007-10-11 04:00:05 +02:00
OSErr err = noErr ;
/* Stop callback. */
if ( ! ao - > b_digital )
{
err = AudioOutputUnitStop ( ao - > theOutputUnit ) ;
if ( err ! = noErr )
ao_msg ( MSGT_AO , MSGL_WARN , " AudioOutputUnitStop returned [%4.4s] \n " , ( char * ) & err ) ;
}
else
{
2010-07-11 23:00:37 +02:00
err = AudioDeviceStop ( ao - > i_selected_dev , ao - > renderCallback ) ;
2007-10-11 04:00:05 +02:00
if ( err ! = noErr )
ao_msg ( MSGT_AO , MSGL_WARN , " AudioDeviceStop failed: [%4.4s] \n " , ( char * ) & err ) ;
}
ao - > paused = 1 ;
2003-05-21 23:15:46 +02:00
}
/* resume playing, after audio_pause() */
2006-07-06 06:30:19 +02:00
static void audio_resume ( void )
2003-05-21 23:15:46 +02:00
{
2007-10-11 04:00:05 +02:00
OSErr err = noErr ;
if ( ! ao - > paused )
return ;
/* Start callback. */
if ( ! ao - > b_digital )
{
err = AudioOutputUnitStart ( ao - > theOutputUnit ) ;
if ( err ! = noErr )
ao_msg ( MSGT_AO , MSGL_WARN , " AudioOutputUnitStart returned [%4.4s] \n " , ( char * ) & err ) ;
}
else
{
2010-07-11 23:00:37 +02:00
err = AudioDeviceStart ( ao - > i_selected_dev , ao - > renderCallback ) ;
2007-10-11 04:00:05 +02:00
if ( err ! = noErr )
ao_msg ( MSGT_AO , MSGL_WARN , " AudioDeviceStart failed: [%4.4s] \n " , ( char * ) & err ) ;
}
ao - > paused = 0 ;
}
/*****************************************************************************
* StreamListener
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-07-11 23:00:37 +02:00
static OSStatus StreamListener ( AudioObjectID inObjectID ,
UInt32 inNumberAddresses ,
const AudioObjectPropertyAddress inAddresses [ ] ,
void * inClientData )
2007-10-11 04:00:05 +02:00
{
2010-07-11 23:00:37 +02:00
for ( int i = 0 ; i < inNumberAddresses ; + + i )
2007-10-11 04:00:05 +02:00
{
2010-07-11 23:00:37 +02:00
if ( inAddresses [ i ] . mSelector = = kAudioStreamPropertyPhysicalFormat ) {
ao_msg ( MSGT_AO , MSGL_WARN , " got notify kAudioStreamPropertyPhysicalFormat changed. \n " ) ;
2007-11-10 03:07:34 +01:00
if ( inClientData )
* ( volatile int * ) inClientData = 1 ;
2007-10-11 04:00:05 +02:00
break ;
2010-07-11 23:00:37 +02:00
}
2007-10-11 04:00:05 +02:00
}
return noErr ;
}
2010-07-11 23:00:37 +02:00
static OSStatus DeviceListener ( AudioObjectID inObjectID ,
UInt32 inNumberAddresses ,
const AudioObjectPropertyAddress inAddresses [ ] ,
void * inClientData )
2007-10-11 04:00:05 +02:00
{
2010-07-11 23:00:37 +02:00
for ( int i = 0 ; i < inNumberAddresses ; + + i )
2007-10-11 04:00:05 +02:00
{
2010-07-11 23:00:37 +02:00
if ( inAddresses [ i ] . mSelector = = kAudioDevicePropertyDeviceHasChanged ) {
2007-10-11 04:00:05 +02:00
ao_msg ( MSGT_AO , MSGL_WARN , " got notify kAudioDevicePropertyDeviceHasChanged. \n " ) ;
ao - > b_stream_format_changed = 1 ;
break ;
2010-07-11 23:00:37 +02:00
}
2007-10-11 04:00:05 +02:00
}
return noErr ;
2003-05-21 23:15:46 +02:00
}