Audio support for Mac OS X added.
git-svn-id: http://yate.null.ro/svn/yate/trunk@3154 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
parent
51b082375f
commit
2b92a31841
|
@ -27,6 +27,8 @@
|
|||
|
||||
#ifdef _WINDOWS
|
||||
#define DEFAULT_DEVICE "dsound/*"
|
||||
#elif defined(__APPLE__)
|
||||
#define DEFAULT_DEVICE "coreaudio/*"
|
||||
#elif defined(__linux__)
|
||||
#define DEFAULT_DEVICE "alsa/default"
|
||||
#else
|
||||
|
|
13
configure.in
13
configure.in
|
@ -595,6 +595,19 @@ HAVE_ALSA="yes"
|
|||
AC_MSG_RESULT([$HAVE_ALSA])
|
||||
AC_SUBST(HAVE_ALSA)
|
||||
|
||||
# check for Mac OS X audio headers
|
||||
HAVE_COREAUDIO=no
|
||||
if [[ "x$uname_os" = "xDarwin" ]]; then
|
||||
AC_MSG_CHECKING([for Mac OS X audio headers])
|
||||
AC_TRY_COMPILE([
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
],[],
|
||||
[HAVE_COREAUDIO="yes"],[]
|
||||
)
|
||||
AC_MSG_RESULT([$HAVE_COREAUDIO])
|
||||
fi
|
||||
AC_SUBST(HAVE_COREAUDIO)
|
||||
|
||||
HAVE_GSM=no
|
||||
GSM_INC=""
|
||||
|
|
|
@ -77,6 +77,10 @@ ifneq (@HAVE_ALSA@,no)
|
|||
PROGS := $(PROGS) client/alsachan.yate
|
||||
endif
|
||||
|
||||
ifneq (@HAVE_COREAUDIO@,no)
|
||||
PROGS := $(PROGS) client/coreaudio.yate
|
||||
endif
|
||||
|
||||
ifneq (@HAVE_QT4@,no)
|
||||
PROGS := $(PROGS) qt4/updater.yate
|
||||
endif
|
||||
|
@ -254,6 +258,8 @@ enumroute.yate: LOCALLIBS = @RESOLV_LIB@
|
|||
|
||||
client/alsachan.yate: LOCALLIBS = -lasound
|
||||
|
||||
client/coreaudio.yate: LOCALLIBS = -framework CoreServices -framework CoreAudio -framework AudioUnit -framework AudioToolBox
|
||||
|
||||
yiaxchan.yate: ../libs/yiax/libyateiax.a
|
||||
yiaxchan.yate: LOCALFLAGS = -I@top_srcdir@/libs/yiax
|
||||
yiaxchan.yate: LOCALLIBS = -L../libs/yiax -lyateiax
|
||||
|
|
|
@ -0,0 +1,977 @@
|
|||
/*
|
||||
* coreaudio.cpp
|
||||
* This file is part of the YATE Project http://YATE.null.ro
|
||||
*
|
||||
* CoreAudio sound channel driver for Mac OS X.
|
||||
*
|
||||
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
||||
* Copyright (C) 2004-2010 Null Team
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include <yatephone.h>
|
||||
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#define FRAME_SIZE 320
|
||||
|
||||
using namespace TelEngine;
|
||||
|
||||
namespace { //anonymous
|
||||
|
||||
class CoreAudioSource : public ThreadedSource
|
||||
{
|
||||
public:
|
||||
CoreAudioSource();
|
||||
~CoreAudioSource();
|
||||
// inherited methods
|
||||
bool init(const String& type);
|
||||
virtual void run();
|
||||
virtual void cleanup();
|
||||
virtual bool control(NamedList& params);
|
||||
|
||||
// append to the internal buffer data read from input source
|
||||
void sendData(AudioBufferList *buf);
|
||||
// provide data to the AudioConverter taken from the internal buffer
|
||||
DataBlock getData(UInt32 pkts);
|
||||
|
||||
// helper function for allocating buffers
|
||||
AudioBufferList* allocateAudioBufferList(UInt32 numChannels, UInt32 size);
|
||||
// helper function for freeing buffers
|
||||
void destroyAudioBufferList(AudioBufferList* list);
|
||||
// helper function for obtaining an AudioConverter
|
||||
OSStatus buildConverter(AudioStreamBasicDescription inFormat, AudioConverterRef* ac);
|
||||
|
||||
// obtain the output format of the AudioUnit
|
||||
inline AudioStreamBasicDescription outFormat()
|
||||
{ return m_outDevFormat; }
|
||||
|
||||
private:
|
||||
// callback for obtaining data from input source
|
||||
static OSStatus inputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp,
|
||||
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
|
||||
// default input AudioUnit
|
||||
AudioUnit fAudioUnit;
|
||||
// input buffer
|
||||
AudioBufferList* m_inAudioBuffer;
|
||||
// sample rate converter
|
||||
AudioConverterRef m_audioConvert;
|
||||
// input device id
|
||||
AudioDeviceID fInputDevID;
|
||||
// output format of the AudioUnit
|
||||
AudioStreamBasicDescription m_outDevFormat;
|
||||
// the desired format for yate, passed to the sample rate converter
|
||||
AudioStreamBasicDescription m_convertToFormat;
|
||||
// total amount of bytes sent
|
||||
unsigned int m_total;
|
||||
// check if the AudioDevice supports setting the volume
|
||||
bool m_volSettable;
|
||||
// internal buffer
|
||||
DataBlock m_data;
|
||||
};
|
||||
|
||||
class CoreAudioConsumer : public DataConsumer, public Mutex
|
||||
{
|
||||
public:
|
||||
CoreAudioConsumer();
|
||||
~CoreAudioConsumer();
|
||||
bool init(const String& type);
|
||||
// inherited methods
|
||||
virtual unsigned long Consume(const DataBlock &data, unsigned long tStamp, unsigned long flags);
|
||||
virtual bool control(NamedList& params);
|
||||
virtual void getData(AudioBufferList* buf);
|
||||
|
||||
private:
|
||||
// callback through which the AudioUnit requires data to play
|
||||
static OSStatus outputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp,
|
||||
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
|
||||
// the AudioUnit
|
||||
AudioUnit fAudioUnit;
|
||||
// total amount of data written to the output
|
||||
unsigned int m_total;
|
||||
// check if the AudioDevice supports setting the volume
|
||||
bool m_volSettable;
|
||||
// AudioDevice ID
|
||||
AudioDeviceID fOutputDevID;
|
||||
// internal buffer
|
||||
DataBlock m_data;
|
||||
};
|
||||
|
||||
class CoreAudioChan : public CallEndpoint
|
||||
{
|
||||
public:
|
||||
CoreAudioChan(const String& dev);
|
||||
~CoreAudioChan();
|
||||
bool init();
|
||||
virtual void disconnected(bool final, const char *reason);
|
||||
void answer();
|
||||
inline void setTarget(const char* target = 0)
|
||||
{ m_target = target; }
|
||||
inline const String& getTarget() const
|
||||
{ return m_target; }
|
||||
|
||||
private:
|
||||
String m_dev;
|
||||
String m_target;
|
||||
};
|
||||
|
||||
class CoreAudioHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
CoreAudioHandler(const char *name) : MessageHandler(name) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class StatusHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
StatusHandler() : MessageHandler("engine.status") { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class DropHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
DropHandler() : MessageHandler("call.drop") { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class MasqHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
MasqHandler(int prio) : MessageHandler("chan.masquerade",prio) { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class AttachHandler : public MessageHandler
|
||||
{
|
||||
public:
|
||||
AttachHandler() : MessageHandler("chan.attach") { }
|
||||
virtual bool received(Message &msg);
|
||||
};
|
||||
|
||||
class CoreAudioPlugin : public Plugin
|
||||
{
|
||||
public:
|
||||
CoreAudioPlugin();
|
||||
virtual void initialize();
|
||||
virtual bool isBusy() const;
|
||||
private:
|
||||
CoreAudioHandler* m_handler;
|
||||
};
|
||||
|
||||
static CoreAudioChan* s_audioChan = 0;
|
||||
|
||||
INIT_PLUGIN(CoreAudioPlugin);
|
||||
|
||||
// test if a device permits setting the volume
|
||||
static bool checkVolumeSettable(AudioDeviceID devId, UInt32 inChannel,Boolean isInput)
|
||||
{
|
||||
Boolean isWritable = false;
|
||||
OSStatus err = AudioDeviceGetPropertyInfo(devId,inChannel,isInput,kAudioDevicePropertyVolumeScalar,NULL,&isWritable);
|
||||
if (err != noErr) {
|
||||
DDebug(DebugInfo, "CoreAudio - Failed to get volume property err=%4.4s, %ld",(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
return isWritable;
|
||||
}
|
||||
|
||||
// callback for the sample rate converter
|
||||
OSStatus convertCallback(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData,
|
||||
AudioStreamPacketDescription** outDataPacketDescription, void* inUserData)
|
||||
{
|
||||
CoreAudioSource* src = static_cast<CoreAudioSource*> (inUserData);
|
||||
if (!src)
|
||||
return 1;
|
||||
// try to get data with the required length
|
||||
DataBlock data = src->getData(*ioNumberDataPackets);
|
||||
if (data.length() > 0)
|
||||
XDebug(DebugInfo,"CoreAudio::convertCallBack() packetsReq=%d pktsAvailable=%d", (int)*ioNumberDataPackets,data.length()/2);
|
||||
|
||||
// signal that we have no data to convert and return
|
||||
if (data.length() == 0) {
|
||||
*ioNumberDataPackets = 0;
|
||||
return 1;
|
||||
}
|
||||
// determine how much we can read into the converter's input buffer
|
||||
UInt32 maxPackets = data.length() / src->outFormat().mBytesPerFrame;
|
||||
if (*ioNumberDataPackets > maxPackets)
|
||||
*ioNumberDataPackets = maxPackets;
|
||||
else
|
||||
maxPackets = *ioNumberDataPackets;
|
||||
|
||||
// fill the converters input buffer
|
||||
ioData->mBuffers[0].mData = data.data();
|
||||
ioData->mBuffers[0].mDataByteSize = maxPackets * src->outFormat().mBytesPerFrame;
|
||||
ioData->mBuffers[0].mNumberChannels = 1;
|
||||
data.cut(-(maxPackets * src->outFormat().mBytesPerFrame));
|
||||
return noErr;
|
||||
}
|
||||
|
||||
CoreAudioSource::CoreAudioSource()
|
||||
: m_inAudioBuffer(0), m_audioConvert(NULL), fInputDevID(0), m_total(0)
|
||||
{
|
||||
Debug(DebugAll,"CoreAudioSource::CoreAudioSource() [%p]",this);
|
||||
}
|
||||
|
||||
CoreAudioSource::~CoreAudioSource()
|
||||
{
|
||||
Debug(DebugAll,"CoreAudioSource::~CoreAudioSource() [%p] total=%u",this,m_total);
|
||||
OSStatus err = AudioOutputUnitStop(fAudioUnit);
|
||||
if(err != noErr)
|
||||
Debug(DebugInfo,"CoreAudioSource::~CoreAudioSource() [%p] - Failed to stop AU",this);
|
||||
err = AudioUnitUninitialize(fAudioUnit);
|
||||
if(err != noErr)
|
||||
Debug(DebugInfo,"CoreAudioSource::~CoreAudioSource() [%p] - Failed to uninitialize AU",this);
|
||||
destroyAudioBufferList(m_inAudioBuffer);
|
||||
}
|
||||
|
||||
bool CoreAudioSource::init(const String& type)
|
||||
{
|
||||
OSStatus err = noErr;
|
||||
UInt32 param;
|
||||
|
||||
// open the AudioOutputUnit, provide description
|
||||
Component component;
|
||||
ComponentDescription description;
|
||||
description.componentType = kAudioUnitType_Output;
|
||||
description.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
description.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
description.componentFlags = 0;
|
||||
description.componentFlagsMask = 0;
|
||||
if((component = FindNextComponent(NULL,&description))) {
|
||||
err = OpenAComponent(component,&fAudioUnit);
|
||||
if(err != noErr) {
|
||||
fAudioUnit = NULL;
|
||||
Debug(DebugInfo,"CoreAUdioSource::init() [%p] - failed to open component error==%4.4s, %ld",this,(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// configure AudioOutputUnit for input, enable input on the AUHAL
|
||||
param = 1;
|
||||
err = AudioUnitSetProperty(fAudioUnit,kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Input,1,¶m,sizeof(UInt32));
|
||||
if (err == noErr) {
|
||||
// disable output on the AUHAL
|
||||
param = 0;
|
||||
err = AudioUnitSetProperty(fAudioUnit,kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Input,0,¶m,sizeof(UInt32));
|
||||
}
|
||||
else {
|
||||
Debug(DebugInfo,"CoreAUdioSource::init() [%p] - failed to configure AudioUnit for input error==%4.4s, %ld",this,(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// select the default input device
|
||||
param = sizeof(AudioDeviceID);
|
||||
err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,¶m,&fInputDevID);
|
||||
if(err != noErr) {
|
||||
Debug(DebugInfo,"CoreAudioSource::init() [%p] - failed to get input device error==%4.4s, %ld",this,(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// set the current device to the AudioUnit
|
||||
err = AudioUnitSetProperty(fAudioUnit,kAudioOutputUnitProperty_CurrentDevice,kAudioUnitScope_Global,0,&fInputDevID,sizeof(AudioDeviceID));
|
||||
if (err != noErr) {
|
||||
Debug(DebugInfo,"CoreAudioSource::init() [%p] - failed to set AU input device=%4.4s, %ld",this,(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// setup render callback
|
||||
AURenderCallbackStruct callback;
|
||||
callback.inputProc = CoreAudioSource::inputCallback;
|
||||
callback.inputProcRefCon = this;
|
||||
err = AudioUnitSetProperty(fAudioUnit,kAudioOutputUnitProperty_SetInputCallback,kAudioUnitScope_Global,0,&callback,sizeof(AURenderCallbackStruct));
|
||||
if (err != noErr) {
|
||||
Debug(DebugInfo,"CoreAudioSource::init() [%p] - could not set callback error==%4.4s, %ld",this,(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// get hardware device format
|
||||
param = sizeof(AudioStreamBasicDescription);
|
||||
AudioStreamBasicDescription devFormat;
|
||||
err = AudioUnitGetProperty(fAudioUnit,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Input,1,&devFormat,¶m);
|
||||
if(err != noErr) {
|
||||
Debug(DebugInfo,"CoreAudioSource::init() [%p] - failed to get input device AudioStreamBasicDescription error==%4.4s, %ld",this,(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
|
||||
DDebug(DebugInfo,"CoreAudioSource::init() [%p] - hardware device input format is : channels/frame=%u, sampleRate=%f, bits/channel=%u, "
|
||||
"bytes/frame=%u, frames/packet=%u, bytes/packet=%u, formatFlags=0x%x",
|
||||
this,(unsigned int)devFormat.mChannelsPerFrame,devFormat.mSampleRate,(unsigned int)devFormat.mBitsPerChannel,
|
||||
(unsigned int)devFormat.mBytesPerFrame,(unsigned int)devFormat.mFramesPerPacket,(unsigned int)devFormat.mBytesPerPacket,
|
||||
(unsigned int)devFormat.mFormatFlags);
|
||||
|
||||
m_outDevFormat.mChannelsPerFrame = 1;
|
||||
m_outDevFormat.mSampleRate = devFormat.mSampleRate;
|
||||
m_outDevFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
m_outDevFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
|
||||
m_outDevFormat.mFormatFlags &= ~kAudioFormatFlagIsBigEndian;
|
||||
#if __BIG_ENDIAN__
|
||||
m_outDevFormat.mFormatFlags |= kAudioFormatFlagIsBigEndian;
|
||||
#endif
|
||||
m_outDevFormat.mBitsPerChannel = sizeof(int16_t) * 8;
|
||||
m_outDevFormat.mBytesPerFrame = m_outDevFormat.mBitsPerChannel / 8;
|
||||
m_outDevFormat.mFramesPerPacket = 1;
|
||||
m_outDevFormat.mBytesPerPacket = m_outDevFormat.mBytesPerFrame;
|
||||
|
||||
// det the AudioUnit output data format
|
||||
err = AudioUnitSetProperty(fAudioUnit,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Output,1,&m_outDevFormat,sizeof(AudioStreamBasicDescription));
|
||||
if(err != noErr) {
|
||||
Debug(DebugInfo, "CoreAudioSource::init() [%p] - failed to set output data format error==%4.4s, %ld",this,(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
|
||||
DDebug(DebugInfo,"CoreAudioSource::init() [%p] - AudioUnit output format is : channels/frame=%u, sampleRate=%f, bits/channel=%u, "
|
||||
"bytes/frame=%u, frames/packet=%u, bytes/packet=%u, formatFlags=0x%x",
|
||||
this,(unsigned int)m_outDevFormat.mChannelsPerFrame,m_outDevFormat.mSampleRate,(unsigned int)m_outDevFormat.mBitsPerChannel,
|
||||
(unsigned int)m_outDevFormat.mBytesPerFrame,(unsigned int)m_outDevFormat.mFramesPerPacket,(unsigned int)m_outDevFormat.mBytesPerPacket,
|
||||
(unsigned int)m_outDevFormat.mFormatFlags);
|
||||
|
||||
// obtain a sample rate converter
|
||||
err = buildConverter(m_outDevFormat,&m_audioConvert);
|
||||
if (err != noErr) {
|
||||
Debug(DebugInfo,"CoreAudioSource::init() [%p] - failed to get sample rate converter error==%4.4s, %ld",this,(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the number of frames in the IO buffer
|
||||
UInt32 audioSamples;
|
||||
param = sizeof(UInt32);
|
||||
err = AudioUnitGetProperty(fAudioUnit,kAudioDevicePropertyBufferFrameSize,kAudioUnitScope_Global,0,&audioSamples,¶m);
|
||||
if(err != noErr) {
|
||||
Debug(DebugInfo,"CoreAudioSource::init() [%p] - failed to get audio sample size error==%4.4s, %ld",this,(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize the AU
|
||||
err = AudioUnitInitialize(fAudioUnit);
|
||||
if(err != noErr) {
|
||||
Debug(DebugInfo,"CoreAudioSource::init() [%p] - Failed to initialize AU error==%4.4s, %ld",this,(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// allocate AudioBufferList
|
||||
m_inAudioBuffer = allocateAudioBufferList(m_outDevFormat.mChannelsPerFrame,audioSamples * m_outDevFormat.mBytesPerFrame);
|
||||
if(m_inAudioBuffer == NULL) {
|
||||
Debug(DebugInfo,"CoreAudioSource::init() [%p] - Failed to allocate audio buffers",this);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start pulling for audio data
|
||||
err = AudioOutputUnitStart(fAudioUnit);
|
||||
if(err != noErr) {
|
||||
Debug(DebugInfo,"CoreAudioSource::init() [%p] - Failed to start the AudioUnit error==%4.4s, %ld",this,(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
Debug(DebugInfo,"CoreAudioSource::init() [%p] - AudioUnit started",this);
|
||||
|
||||
// check if the device lets us set the volume
|
||||
m_volSettable = checkVolumeSettable(fInputDevID,0,true);
|
||||
Debug(DebugAll,"CoreAudioSource::init() [%p] - volume %s settable",this,(m_volSettable ? "is" : "isn't"));
|
||||
|
||||
return start("CoreAudioSource");
|
||||
}
|
||||
|
||||
OSStatus CoreAudioSource::buildConverter(AudioStreamBasicDescription inputFormat, AudioConverterRef* ac)
|
||||
{
|
||||
m_convertToFormat.mChannelsPerFrame = 1;
|
||||
m_convertToFormat.mSampleRate = 8000.0;
|
||||
m_convertToFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
m_convertToFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
|
||||
m_convertToFormat.mFormatFlags &= ~kAudioFormatFlagIsBigEndian;
|
||||
#if __BIG_ENDIAN__
|
||||
m_convertToFormat.mFormatFlags |= kAudioFormatFlagIsBigEndian;
|
||||
#endif
|
||||
m_convertToFormat.mBitsPerChannel = sizeof(int16_t) * 8;
|
||||
m_convertToFormat.mBytesPerFrame = m_convertToFormat.mBitsPerChannel / 8;
|
||||
m_convertToFormat.mFramesPerPacket = 1;
|
||||
m_convertToFormat.mBytesPerPacket = m_convertToFormat.mBytesPerFrame;
|
||||
|
||||
DDebug(DebugInfo,"CoreAudioSource::buildConverter() [%p] - AudioConverter output format is : channels/frame=%u, sampleRate=%f, bits/channel=%u, "
|
||||
"bytes/frame=%u, frames/packet=%u, bytes/packet=%u, formatFlags=0x%x",
|
||||
this,(unsigned int)m_convertToFormat.mChannelsPerFrame,m_convertToFormat.mSampleRate,(unsigned int)m_convertToFormat.mBitsPerChannel,
|
||||
(unsigned int)m_convertToFormat.mBytesPerFrame,(unsigned int)m_convertToFormat.mFramesPerPacket,(unsigned int)m_convertToFormat.mBytesPerPacket,
|
||||
(unsigned int)m_convertToFormat.mFormatFlags);
|
||||
|
||||
OSStatus err = noErr;
|
||||
err = AudioConverterNew(&inputFormat,&m_convertToFormat,ac);
|
||||
if (err != noErr) {
|
||||
Debug(DebugInfo,"CoreAudioSource::buildConverter() [%p] failed to get converter error==%4.4s, %ld",this,(char*)&err, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// set channel map
|
||||
SInt32 channelMap[] = { 0 };
|
||||
err = AudioConverterSetProperty(*ac, kAudioConverterChannelMap, sizeof(SInt32), channelMap);
|
||||
// set converter complexity
|
||||
UInt32 size = sizeof(kAudioConverterSampleRateConverterComplexity_Mastering);
|
||||
UInt32 prop = kAudioConverterSampleRateConverterComplexity_Mastering;
|
||||
err = AudioConverterSetProperty(*ac,kAudioConverterSampleRateConverterComplexity,size,&prop);
|
||||
if (err != noErr)
|
||||
Debug(DebugInfo,"CoreAudioSource::buildConverter() [%p] failed to set converter complexity error==%4.4s, %ld",this,(char*)&err,err);
|
||||
return noErr;
|
||||
}
|
||||
|
||||
AudioBufferList* CoreAudioSource::allocateAudioBufferList(UInt32 numChannels, UInt32 size)
|
||||
{
|
||||
AudioBufferList* list;
|
||||
DDebug(DebugAll,"CoreAudioSource::allocateAudioBufferList(channels= %d,size=%d) [%p]",(int)numChannels,(int)size,this);
|
||||
list = (AudioBufferList*)calloc(1, sizeof(AudioBufferList) + numChannels * sizeof(AudioBuffer));
|
||||
if(list == NULL)
|
||||
return NULL;
|
||||
|
||||
list->mNumberBuffers = numChannels;
|
||||
for(UInt32 i = 0; i < numChannels; ++i) {
|
||||
list->mBuffers[i].mNumberChannels = 1;
|
||||
list->mBuffers[i].mDataByteSize = size;
|
||||
list->mBuffers[i].mData = malloc(size);
|
||||
if(list->mBuffers[i].mData == NULL) {
|
||||
destroyAudioBufferList(list);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void CoreAudioSource::destroyAudioBufferList(AudioBufferList* list)
|
||||
{
|
||||
DDebug(DebugAll,"CoreAudioSource::destroyAudioBufferList(list=%p) [%p]",list,this);
|
||||
if(list) {
|
||||
for(UInt32 i = 0; i < list->mNumberBuffers; i++) {
|
||||
if(list->mBuffers[i].mData)
|
||||
free(list->mBuffers[i].mData);
|
||||
}
|
||||
free(list);
|
||||
}
|
||||
}
|
||||
|
||||
void CoreAudioSource::sendData(AudioBufferList* buf)
|
||||
{
|
||||
// append to internal buffer data we receive from input
|
||||
if (!buf)
|
||||
return;
|
||||
lock();
|
||||
for (unsigned int i = 0; i < m_outDevFormat.mChannelsPerFrame; i++)
|
||||
m_data.append(buf->mBuffers[i].mData,buf->mBuffers[i].mDataByteSize);
|
||||
XDebug(DebugAll,"CoreAudioSource::sendData(buffer=%p,buffer_length=%d), internal buffer length=%d [%p]",buf,(int)buf->mBuffers[0].mDataByteSize,m_data.length(),this);
|
||||
unlock();
|
||||
}
|
||||
|
||||
DataBlock CoreAudioSource::getData(UInt32 pkts)
|
||||
{
|
||||
// return to the converter a data block with the required size or the maximum available
|
||||
DataBlock data;
|
||||
lock();
|
||||
if (pkts * m_outDevFormat.mBytesPerFrame > m_data.length())
|
||||
pkts = m_data.length() / m_outDevFormat.mBytesPerFrame;
|
||||
data.assign(m_data.data(),pkts * m_outDevFormat.mBytesPerFrame);
|
||||
m_data.cut(-pkts * m_outDevFormat.mBytesPerFrame );
|
||||
unlock();
|
||||
return data;
|
||||
}
|
||||
|
||||
OSStatus CoreAudioSource::inputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp,
|
||||
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
|
||||
{
|
||||
CoreAudioSource* source = (CoreAudioSource*) inRefCon;
|
||||
OSStatus err = noErr;
|
||||
// Render into audio buffer
|
||||
err = AudioUnitRender(source->fAudioUnit,ioActionFlags,inTimeStamp,inBusNumber,inNumberFrames,source->m_inAudioBuffer);
|
||||
if(err)
|
||||
Debug(DebugInfo,"CoreAudioSource::inputCallback() [%p] AudioUnitRender() failed with error=%4.4s, %ld",source,(char*)&err,err);
|
||||
|
||||
source->sendData(source->m_inAudioBuffer);
|
||||
return err;
|
||||
}
|
||||
|
||||
void CoreAudioSource::run()
|
||||
{
|
||||
DataBlock frame;
|
||||
AudioBufferList fillBufList;
|
||||
fillBufList.mNumberBuffers = 1;
|
||||
fillBufList.mBuffers[0].mNumberChannels = 1;
|
||||
fillBufList.mBuffers[0].mData = new char[FRAME_SIZE];
|
||||
|
||||
do {
|
||||
if (!looping())
|
||||
break;
|
||||
if (frame.length() < FRAME_SIZE) {
|
||||
// try to get more data from input and convert it to the desired sample rate
|
||||
UInt32 outBuffSize = FRAME_SIZE / m_convertToFormat.mBytesPerPacket;
|
||||
//AudioStreamPacketDescription* pktDesc = NULL;
|
||||
fillBufList.mBuffers[0].mDataByteSize = FRAME_SIZE;
|
||||
OSStatus err = AudioConverterFillComplexBuffer(m_audioConvert,convertCallback,this,&outBuffSize,&fillBufList,NULL/*pktDesc*/);
|
||||
if (err != noErr && err != 1)
|
||||
Debug(DebugInfo,"CoreAudioSource::run() - AudioConvertFillComplexBuffer() failed with error=%4.4s, %ld", (char*)&err, err);
|
||||
if (outBuffSize == 0) {
|
||||
Thread::idle();
|
||||
continue;
|
||||
}
|
||||
frame.append(fillBufList.mBuffers[0].mData,outBuffSize * m_convertToFormat.mBytesPerPacket);
|
||||
}
|
||||
|
||||
if (frame.length() >= FRAME_SIZE) {
|
||||
// we have enough data to send forward
|
||||
DataBlock data(frame.data(),FRAME_SIZE,false);
|
||||
Forward(data);
|
||||
data.clear(false);
|
||||
frame.cut(-FRAME_SIZE);
|
||||
m_total += FRAME_SIZE;
|
||||
}
|
||||
}
|
||||
while (true);
|
||||
delete [] (char*)fillBufList.mBuffers[0].mData;
|
||||
Debug(DebugAll,"CoreAudioSource [%p] end of data",this);
|
||||
}
|
||||
|
||||
void CoreAudioSource::cleanup()
|
||||
{
|
||||
Debug(DebugAll,"CoreAudioSource [%p] cleanup, total=%u",this,m_total);
|
||||
AudioConverterDispose(m_audioConvert);
|
||||
ThreadedSource::cleanup();
|
||||
}
|
||||
|
||||
bool CoreAudioSource::control(NamedList& params)
|
||||
{
|
||||
DDebug(DebugInfo,"CoreAudioSource::control() [%p]",this);
|
||||
if (!m_volSettable)
|
||||
return false;
|
||||
int vol = params.getIntValue("in_volume",-1);
|
||||
if (vol == -1) {
|
||||
Debug(DebugAll,"CoreAudioSource::control() [%p] - invalid value to set for volume",this);
|
||||
return false;
|
||||
}
|
||||
Float32 volValue = vol / 100.0;
|
||||
OSStatus err = AudioDeviceSetProperty(fInputDevID,NULL,0,true,kAudioDevicePropertyVolumeScalar,sizeof(Float32),&volValue);
|
||||
if (err != noErr)
|
||||
Debug(DebugInfo,"CoreAudioSource::control() [%p] - set volume failed with error=%4.4s, %ld",this,(char*)&err,err);
|
||||
if(params.getParam("out_volume"))
|
||||
return false;
|
||||
return err == noErr;
|
||||
}
|
||||
|
||||
|
||||
CoreAudioConsumer::CoreAudioConsumer()
|
||||
: Mutex(false,"CoreAudioConsumer"),
|
||||
m_total(0), m_volSettable(false), fOutputDevID(0)
|
||||
{
|
||||
Debug(DebugAll,"AudioSystemConsumer::AudioSystemConsumer() [%p]",this);
|
||||
}
|
||||
|
||||
CoreAudioConsumer::~CoreAudioConsumer()
|
||||
{
|
||||
Debug(DebugAll,"CoreAudioConsumer::~CoreAudioConsumer() [%p] total=%u",this,m_total);
|
||||
OSStatus err = AudioOutputUnitStop(fAudioUnit);
|
||||
if(err != noErr)
|
||||
Debug(DebugInfo,"CoreAudioConsumer::~CoreAudioConsumer() [%p] - Failed to stop output AudioUnit error=%4.4s, %ld",this,(char*)&err,err);
|
||||
err = AudioUnitUninitialize(fAudioUnit);
|
||||
if(err != noErr)
|
||||
Debug(DebugInfo,"CoreAudioConsumer::~CoreAudioConsumer() [%p] - Failed to uninitialize the AudioUnit error=%4.4s, %ld",this,(char*)&err,err);
|
||||
}
|
||||
|
||||
bool CoreAudioConsumer::init(const String& type)
|
||||
{
|
||||
OSStatus err = noErr;
|
||||
|
||||
// open the AudioOutputUnit, provide description
|
||||
Component component;
|
||||
ComponentDescription description;
|
||||
description.componentType = kAudioUnitType_Output;
|
||||
description.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
description.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
description.componentFlags = 0;
|
||||
description.componentFlagsMask = 0;
|
||||
if((component = FindNextComponent(NULL,&description))) {
|
||||
err = OpenAComponent(component,&fAudioUnit);
|
||||
if(err != noErr) {
|
||||
Debug(DebugInfo,"CoreAudioConsumer::init() [%p] - failed to open component error==%4.4s, %ld",this,(char*)&err,err);
|
||||
fAudioUnit = NULL;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// set up the callback to generate output to the output unit
|
||||
AURenderCallbackStruct callback;
|
||||
callback.inputProc = CoreAudioConsumer::outputCallback;
|
||||
callback.inputProcRefCon = this;
|
||||
err = AudioUnitSetProperty (fAudioUnit,kAudioUnitProperty_SetRenderCallback,kAudioUnitScope_Input,0,&callback,sizeof(callback));
|
||||
if (err != noErr)
|
||||
Debug(DebugInfo,"CoreAudioConsumer::init() [%p]- callback could not be set error=%4.4s, %ld",this,(char*)&err,err);
|
||||
|
||||
// provide the input format of the date we're supplying
|
||||
AudioStreamBasicDescription inputFormat;
|
||||
inputFormat.mSampleRate = 8000.0;
|
||||
inputFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
inputFormat.mBitsPerChannel = sizeof(int16_t) * 8; // = 16
|
||||
inputFormat.mBytesPerFrame = inputFormat.mBitsPerChannel / 8;
|
||||
inputFormat.mFramesPerPacket = 1;
|
||||
inputFormat.mBytesPerPacket = inputFormat.mBytesPerFrame;
|
||||
inputFormat.mChannelsPerFrame = 1;
|
||||
inputFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
|
||||
inputFormat.mFormatFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
|
||||
|
||||
err = AudioUnitSetProperty(fAudioUnit,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Input,0,&inputFormat,sizeof(AudioStreamBasicDescription));
|
||||
if (err != noErr) {
|
||||
Debug(DebugInfo,"CoreAudioConsumer::init() [%p] - set input format failed error==%4.4s, %ld",this,(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// initialize the AudioUnit
|
||||
err = AudioUnitInitialize(fAudioUnit);
|
||||
if (err != noErr) {
|
||||
Debug(DebugInfo,"CoreAudioConsumer::init() [%p] - AudioUnitInitialize failed error=%4.4s, %ld",this,(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// start the AudioUnit
|
||||
err = AudioOutputUnitStart(fAudioUnit);
|
||||
if (err != noErr) {
|
||||
Debug(DebugInfo,"CoreAudioConsumer::init() [%p] - AudioUnitStart failed error=%4.4s, %ld",this,(char*)&err,err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// get the id of the default output device
|
||||
UInt32 param = sizeof(AudioDeviceID);
|
||||
err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,¶m,&fOutputDevID);
|
||||
if(err != noErr)
|
||||
Debug(DebugMild,"CoreAudioConsumer::init() [%p] - Failed to get the device id of the output device error==%4.4s, %ld",this,(char*)&err,err);
|
||||
|
||||
m_volSettable = checkVolumeSettable(fOutputDevID,1,false);
|
||||
Debug(DebugInfo,"CoreAudioConsumer::init() volume %s settable",(m_volSettable ? "is" : "isn't"));
|
||||
return true;
|
||||
}
|
||||
|
||||
void CoreAudioConsumer::getData(AudioBufferList* buf)
|
||||
{
|
||||
if (!buf)
|
||||
return;
|
||||
// put the data into the output buffer
|
||||
UInt32 len = buf->mBuffers[0].mDataByteSize; // there should be only one buffer;
|
||||
lock();
|
||||
if (m_data.length() == 0) {
|
||||
::memset(buf->mBuffers[0].mData,0,len);
|
||||
unlock();
|
||||
return;
|
||||
}
|
||||
if (len > m_data.length())
|
||||
len = m_data.length();
|
||||
if (len > 0) {
|
||||
::memcpy(buf->mBuffers[0].mData,m_data.data(),len);
|
||||
m_data.cut(-len);
|
||||
}
|
||||
unlock();
|
||||
}
|
||||
|
||||
OSStatus CoreAudioConsumer::outputCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp,
|
||||
UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
|
||||
{
|
||||
CoreAudioConsumer* dst = static_cast<CoreAudioConsumer*>(inRefCon);
|
||||
if (!dst)
|
||||
return 1;
|
||||
XDebug(DebugAll,"CoreAudioConsumer::outputCallback() [%p] inNumberFrames=%d buffersCount=%d buffersize=%d",dst,(unsigned int)inNumberFrames,
|
||||
(unsigned int)ioData->mNumberBuffers,(unsigned int)ioData->mBuffers[0].mDataByteSize);
|
||||
dst->getData(ioData);
|
||||
return noErr;
|
||||
}
|
||||
|
||||
unsigned long CoreAudioConsumer::Consume(const DataBlock &data, unsigned long tStamp, unsigned long flags)
|
||||
{
|
||||
// append to the internal buffer received data
|
||||
if (data.null())
|
||||
return 0;
|
||||
lock();
|
||||
m_total += data.length();
|
||||
m_data.append(data);
|
||||
unlock();
|
||||
return invalidStamp();
|
||||
}
|
||||
|
||||
bool CoreAudioConsumer::control(NamedList& params)
|
||||
{
|
||||
DDebug(DebugAll,"CoreAudioConsumer::control() [%p]",this);
|
||||
if (!m_volSettable)
|
||||
return false;
|
||||
int vol = params.getIntValue("out_volume",-1);
|
||||
if (vol == -1) {
|
||||
Debug(DebugAll,"CoreAudioConsumer::control() [%p] invalid value to set for volume",this);
|
||||
return false;
|
||||
}
|
||||
Float32 volValue = vol / 100.0;
|
||||
// set the volume for the output on every channel
|
||||
OSStatus err1 = AudioDeviceSetProperty(fOutputDevID,NULL,1,false,kAudioDevicePropertyVolumeScalar,sizeof(Float32),&volValue);
|
||||
OSStatus err2 = AudioDeviceSetProperty(fOutputDevID,NULL,2,false,kAudioDevicePropertyVolumeScalar,sizeof(Float32),&volValue);
|
||||
return err1 == noErr && err2 == noErr;
|
||||
}
|
||||
|
||||
CoreAudioChan::CoreAudioChan(const String& dev)
|
||||
: CallEndpoint("coreaudio"),
|
||||
m_dev(dev)
|
||||
{
|
||||
Debug(DebugAll,"CoreAudioChan::CoreAudioChan ('%s') [%p]",dev.c_str(),this);
|
||||
s_audioChan = this;
|
||||
}
|
||||
|
||||
CoreAudioChan::~CoreAudioChan()
|
||||
{
|
||||
Debug(DebugAll,"CoreAudioChan::~CoreAudioChan() [%p]",this);
|
||||
setTarget();
|
||||
setSource();
|
||||
setConsumer();
|
||||
s_audioChan = 0;
|
||||
}
|
||||
|
||||
bool CoreAudioChan::init()
|
||||
{
|
||||
CoreAudioSource* source = new CoreAudioSource();
|
||||
if (!source->init(m_dev)) {
|
||||
source->deref();
|
||||
return false;
|
||||
}
|
||||
setSource(source);
|
||||
source->deref();
|
||||
CoreAudioConsumer* cons = new CoreAudioConsumer();
|
||||
if (!cons->init(m_dev)) {
|
||||
cons->deref();
|
||||
setSource();
|
||||
return false;
|
||||
}
|
||||
setConsumer(cons);
|
||||
cons->deref();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CoreAudioChan::disconnected(bool final, const char *reason)
|
||||
{
|
||||
Debug(DebugInfo,"CoreAudioChan::disconnected() '%s' [%p]",reason,this);
|
||||
setTarget();
|
||||
}
|
||||
|
||||
void CoreAudioChan::answer()
|
||||
{
|
||||
Message* m = new Message("call.answered");
|
||||
m->addParam("module","coreaudio");
|
||||
String tmp("coreaudio/");
|
||||
tmp += m_dev;
|
||||
m->addParam("id",tmp);
|
||||
if (m_target)
|
||||
m->addParam("targetid",m_target);
|
||||
Engine::enqueue(m);
|
||||
}
|
||||
|
||||
bool CoreAudioHandler::received(Message &msg)
|
||||
{
|
||||
Debug(DebugInfo,"CoreAudio received call.execute");
|
||||
String dest(msg.getValue("callto"));
|
||||
if (dest.null())
|
||||
return false;
|
||||
Regexp r("^coreaudio/\\(.*\\)$");
|
||||
if (!dest.matches(r))
|
||||
return false;
|
||||
if (s_audioChan) {
|
||||
msg.setParam("error","busy");
|
||||
return false;
|
||||
}
|
||||
CoreAudioChan *chan = new CoreAudioChan(dest.matchString(1).c_str());
|
||||
if (!chan->init()) {
|
||||
chan->destruct();
|
||||
return false;
|
||||
}
|
||||
CallEndpoint* ch = static_cast<CallEndpoint*>(msg.userData());
|
||||
Debug(DebugInfo,"We are routing to device '%s'",dest.matchString(1).c_str());
|
||||
if (ch && chan->connect(ch,msg.getValue("reason"))) {
|
||||
chan->setTarget(msg.getValue("id"));
|
||||
msg.setParam("peerid",dest);
|
||||
msg.setParam("targetid",dest);
|
||||
chan->answer();
|
||||
chan->deref();
|
||||
}
|
||||
else {
|
||||
const char *direct = msg.getValue("direct");
|
||||
if (direct) {
|
||||
Message m("call.execute");
|
||||
m.addParam("module","audiocore");
|
||||
m.addParam("cdrtrack",String::boolText(false));
|
||||
m.addParam("id",dest);
|
||||
m.addParam("caller",dest);
|
||||
m.addParam("callto",direct);
|
||||
m.userData(chan);
|
||||
if (Engine::dispatch(m)) {
|
||||
chan->setTarget(m.getValue("targetid"));
|
||||
msg.addParam("targetid",chan->getTarget());
|
||||
chan->deref();
|
||||
return true;
|
||||
}
|
||||
Debug(DebugInfo,"CoreAudio outgoing call not accepted!");
|
||||
chan->destruct();
|
||||
return false;
|
||||
}
|
||||
const char *targ = msg.getValue("target");
|
||||
if (!targ) {
|
||||
Debug(DebugWarn,"CoreAudio outgoing call with no target!");
|
||||
chan->destruct();
|
||||
return false;
|
||||
}
|
||||
Message m("call.route");
|
||||
m.addParam("module","audiocore");
|
||||
m.addParam("cdrtrack",String::boolText(false));
|
||||
m.addParam("id",dest);
|
||||
m.addParam("caller",dest);
|
||||
m.addParam("called",targ);
|
||||
if (Engine::dispatch(m)) {
|
||||
m = "call.execute";
|
||||
m.addParam("callto",m.retValue());
|
||||
m.retValue() = 0;
|
||||
m.userData(chan);
|
||||
if (Engine::dispatch(m)) {
|
||||
chan->setTarget(m.getValue("targetid"));
|
||||
msg.addParam("targetid",chan->getTarget());
|
||||
chan->deref();
|
||||
return true;
|
||||
}
|
||||
Debug(DebugInfo,"CoreAudio outgoing call not accepted!");
|
||||
}
|
||||
else
|
||||
Debug(DebugWarn,"CoreAudio outgoing call but no route!");
|
||||
chan->destruct();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StatusHandler::received(Message &msg)
|
||||
{
|
||||
const String* sel = msg.getParam("module");
|
||||
if (sel && (*sel != "coreaudio"))
|
||||
return false;
|
||||
msg.retValue() << "name=coreaudio,type=misc;chan=" << (s_audioChan != 0 ) << "\r\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MasqHandler::received(Message &msg)
|
||||
{
|
||||
String id(msg.getValue("id"));
|
||||
if (msg.getParam("message") && id.startsWith("coreaudio/")) {
|
||||
msg = msg.getValue("message");
|
||||
msg.clearParam("message");
|
||||
if (s_audioChan) {
|
||||
msg.addParam("targetid",s_audioChan->getTarget());
|
||||
msg.userData(s_audioChan);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DropHandler::received(Message &msg)
|
||||
{
|
||||
String id(msg.getValue("id"));
|
||||
if (id.null() || id.startsWith("coreaudio/")) {
|
||||
if (s_audioChan) {
|
||||
Debug("CoreAudio",DebugInfo,"ping call");
|
||||
s_audioChan->disconnect();
|
||||
}
|
||||
return !id.null();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AttachHandler::received(Message& msg)
|
||||
{
|
||||
int more = 2;
|
||||
String src(msg.getValue("source"));
|
||||
if (src.null())
|
||||
more--;
|
||||
else {
|
||||
if (!src.startSkip("coreaudio/",false))
|
||||
src = "";
|
||||
}
|
||||
|
||||
String cons(msg.getValue("consumer"));
|
||||
if (cons.null())
|
||||
more--;
|
||||
else {
|
||||
if (!cons.startSkip("coreaudio/",false))
|
||||
cons = "";
|
||||
}
|
||||
|
||||
if (src.null() && cons.null())
|
||||
return false;
|
||||
if (src && cons && (src != cons)) {
|
||||
Debug(DebugWarn,"CoreAudio asked to attach source '%s' and consumer '%s'",src.c_str(),cons.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPointer<DataEndpoint> dd = static_cast<DataEndpoint*>(msg.userObject("DataEndpoint"));
|
||||
if (!dd) {
|
||||
CallEndpoint *ch = static_cast<CallEndpoint*>(msg.userObject("CallEndpoint"));
|
||||
if (ch) {
|
||||
DataEndpoint::commonMutex().lock();
|
||||
dd = ch->setEndpoint();
|
||||
DataEndpoint::commonMutex().unlock();
|
||||
}
|
||||
}
|
||||
if (!dd) {
|
||||
Debug(DebugWarn,"CoreAudio attach request with no control or data channel!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (src) {
|
||||
CoreAudioSource* s = new CoreAudioSource();
|
||||
if (s->init(src))
|
||||
dd->setSource(s);
|
||||
s->deref();
|
||||
}
|
||||
|
||||
if (cons) {
|
||||
CoreAudioConsumer* c = new CoreAudioConsumer();
|
||||
if (c->init(cons))
|
||||
dd->setConsumer(c);
|
||||
c->deref();
|
||||
}
|
||||
|
||||
// Stop dispatching if we handled all requested
|
||||
return !more;
|
||||
}
|
||||
|
||||
CoreAudioPlugin::CoreAudioPlugin()
|
||||
: Plugin("coreaudio"),
|
||||
m_handler(0)
|
||||
{
|
||||
Output("Loaded module CoreAudio");
|
||||
}
|
||||
|
||||
void CoreAudioPlugin::initialize()
|
||||
{
|
||||
Output("Initializing module CoreAudio");
|
||||
if (!m_handler) {
|
||||
m_handler = new CoreAudioHandler("call.execute");
|
||||
Engine::install(m_handler);
|
||||
Engine::install(new MasqHandler(10));
|
||||
Engine::install(new DropHandler());
|
||||
Engine::install(new StatusHandler());
|
||||
Engine::install(new AttachHandler());
|
||||
}
|
||||
}
|
||||
|
||||
bool CoreAudioPlugin::isBusy() const
|
||||
{
|
||||
return (s_audioChan != 0);
|
||||
}
|
||||
|
||||
}; // anonymous namespace
|
Loading…
Reference in New Issue