From d9e7d7b3ca18533b5c8d7dc7ec629f134c30ffa8 Mon Sep 17 00:00:00 2001 From: smorlat Date: Wed, 10 Sep 2008 21:32:22 +0000 Subject: [PATCH] add missing file. git-svn-id: svn+ssh://svn.savannah.nongnu.org/linphone/trunk@17 3f6dc0c8-ddfe-455d-9043-3cd528dc4637 --- linphone/mediastreamer2/src/aqsnd.c | 643 ++++++++++++++++++++++++++++ 1 file changed, 643 insertions(+) create mode 100644 linphone/mediastreamer2/src/aqsnd.c diff --git a/linphone/mediastreamer2/src/aqsnd.c b/linphone/mediastreamer2/src/aqsnd.c new file mode 100644 index 000000000..6d986444f --- /dev/null +++ b/linphone/mediastreamer2/src/aqsnd.c @@ -0,0 +1,643 @@ +/* +mediastreamer2 library - modular sound and video processing and streaming +Copyright (C) 2006 Simon MORLAT (simon.morlat@linphone.org) + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ + +/* this file is specifically distributed under a BSD license */ + +/** +* Copyright (C) 2008 Hiroki Mori (himori@users.sourceforge.net) +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**/ + +/* + This is MacOS X Audio Queue Service support code for mediastreamer2. + Audio Queue Support MacOS X 10.5 or later. + http://developer.apple.com/documentation/MusicAudio/Conceptual/AudioQueueProgrammingGuide/ + */ + +#include +#if !defined(__AudioHardware_h__) +#include "AudioHardware.h" +#endif + +#include "mediastreamer2/mssndcard.h" +#include "mediastreamer2/msfilter.h" + +MSFilter *ms_aq_read_new(MSSndCard *card); +MSFilter *ms_aq_write_new(MSSndCard *card); + +#define kSecondsPerBuffer 0.04 +#define kNumberAudioDataBuffers 5 + +typedef struct AQData{ + int rate; + int bits; + bool_t stereo; + + ms_mutex_t mutex; + queue_t rq; + bool_t read_started; + bool_t write_started; + + AudioQueueRef readQueue; + AudioStreamBasicDescription readAudioFormat; + UInt32 readBufferByteSize; + AudioQueueRef writeQueue; + AudioStreamBasicDescription writeAudioFormat; + UInt32 writeBufferByteSize; + AudioQueueBufferRef writeBuffers[kNumberAudioDataBuffers]; + int curWriteBuffer; + MSBufferizer *bufferizer; +} AQData; + +/* + Audio Queue recode callback + */ + +static void readCallback ( + void *aqData, + AudioQueueRef inAQ, + AudioQueueBufferRef inBuffer, + const AudioTimeStamp *inStartTime, + UInt32 inNumPackets, + const AudioStreamPacketDescription *inPacketDesc +) { + ms_debug("readCallback"); + AQData *d=(AQData*)aqData; + OSStatus err; + +// ms_debug("readCallback inNumPackets %d %d", inNumPackets, inBuffer->mAudioDataByteSize); + mblk_t *rm=NULL; + rm=allocb(inNumPackets*2,0); + memcpy(rm->b_wptr, inBuffer->mAudioData, inNumPackets*2); + rm->b_wptr += inNumPackets*2; + ms_mutex_lock(&d->mutex); + putq(&d->rq,rm); + ms_mutex_unlock(&d->mutex); + rm=NULL; + + err = AudioQueueEnqueueBuffer ( + d->readQueue, + inBuffer, + 0, + NULL + ); + if(err != noErr) { + ms_error("readCallback:AudioQueueEnqueueBuffer %d", err); + } +} + +/* + Audio Queue play callback + */ + +static void writeCallback ( + void *aqData, + AudioQueueRef inAQ, + AudioQueueBufferRef inBuffer +) { + ms_debug("writeCallback"); + AQData *d=(AQData*)aqData; + OSStatus err; + if(d->bufferizer->size >= d->writeBufferByteSize) { + ms_mutex_lock(&d->mutex); + ms_bufferizer_read(d->bufferizer, inBuffer->mAudioData, d->writeBufferByteSize); + ms_mutex_unlock(&d->mutex); + + } else { + memset(inBuffer->mAudioData, 0, d->writeBufferByteSize); + } + inBuffer->mAudioDataByteSize = d->writeBufferByteSize; + err = AudioQueueEnqueueBuffer ( + d->writeQueue, + inBuffer, + 0, + NULL + ); + if(err != noErr) { + ms_error("AudioQueueEnqueueBuffer %d", err); + } +} + +void putWriteAQ(void *aqData, + int queuenum) +{ + ms_debug("putWriteAQ"); + AQData *d=(AQData*)aqData; + OSStatus err; + err = AudioQueueEnqueueBuffer ( + d->writeQueue, + d->writeBuffers[queuenum], + 0, + NULL + ); + if(err != noErr) { + ms_error("AudioQueueEnqueueBuffer %d", err); + } +} + +/* + play buffer setup function + */ + +void setupWrite(MSSndCard *card) { + ms_debug("setupWrite"); + AQData *d=(AQData*)card->data; + OSStatus err; + + int bufferIndex; + + for (bufferIndex = 0; bufferIndex < kNumberAudioDataBuffers; ++bufferIndex) { + + err = AudioQueueAllocateBuffer ( + d->writeQueue, + d->writeBufferByteSize, + &d->writeBuffers[bufferIndex] + ); + if(err != noErr) { + ms_error("setupWrite:AudioQueueAllocateBuffer %d", err); + } + } +} + +/* + recode buffer setup function + */ + +void setupRead(MSSndCard *card) { + ms_debug("setupRead"); + AQData *d=(AQData*)card->data; + OSStatus err; + + // allocate and enqueue buffers + int bufferIndex; + + for (bufferIndex = 0; bufferIndex < kNumberAudioDataBuffers; ++bufferIndex) { + + AudioQueueBufferRef buffer; + + err = AudioQueueAllocateBuffer ( + d->readQueue, + d->readBufferByteSize, + &buffer + ); + if(err != noErr) { + ms_error("setupRead:AudioQueueAllocateBuffer %d", err); + } + + err = AudioQueueEnqueueBuffer ( + d->readQueue, + buffer, + 0, + NULL + ); + if(err != noErr) { + ms_error("AudioQueueEnqueueBuffer %d", err); + } + } +} + +/* + mediastreamer2 function + */ + +static void aq_set_level(MSSndCard *card, MSSndCardMixerElem e, int percent) +{ + AQData *d=(AQData*)card->data; +} + +static int aq_get_level(MSSndCard *card, MSSndCardMixerElem e) +{ + AQData *d=(AQData*)card->data; + return 0; +} + +static void aq_set_source(MSSndCard *card, MSSndCardCapture source) +{ + AQData *d=(AQData*)card->data; +} + +static void aq_init(MSSndCard *card){ + ms_debug("aq_init"); + AQData *d=ms_new(AQData,1); + + d->bits=16; + d->rate=8000; + d->stereo=FALSE; + + d->read_started=FALSE; + d->write_started=FALSE; + qinit(&d->rq); + d->bufferizer=ms_bufferizer_new(); + ms_mutex_init(&d->mutex,NULL); + card->data=d; +} + +static void aq_uninit(MSSndCard *card){ + AQData *d=(AQData*)card->data; + flushq(&d->rq,0); + ms_bufferizer_destroy(d->bufferizer); + ms_mutex_destroy(&d->mutex); + ms_free(d); +} + +static void aq_detect(MSSndCardManager *m); +static MSSndCard *aq_duplicate(MSSndCard *obj); + +MSSndCardDesc aq_card_desc={ + .driver_type="AQ", + .detect=aq_detect, + .init=aq_init, + .set_level=aq_set_level, + .get_level=aq_get_level, + .set_capture=aq_set_source, + .create_reader=ms_aq_read_new, + .create_writer=ms_aq_write_new, + .uninit=aq_uninit, + .duplicate=aq_duplicate +}; + +static MSSndCard *aq_duplicate(MSSndCard *obj){ + MSSndCard *card=ms_snd_card_new(&aq_card_desc); + AQData *dcard=(AQData*)card->data; + AQData *dobj=(AQData*)obj->data; + card->name=ms_strdup(obj->name); + return card; +} + +static MSSndCard *aq_card_new(){ + MSSndCard *card=ms_snd_card_new(&aq_card_desc); + card->name=ms_strdup("Audio Queue"); + return card; +} + +static void aq_detect(MSSndCardManager *m){ + ms_debug("aq_detect"); + +#if defined(__AudioHardware_h__) + OSStatus err; + UInt32 count; + AudioDeviceID inDevice, outDevice; + char name[255]; + + count = sizeof(inDevice); + err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, + &count, + &inDevice); + if (err) { + ms_error("get kAudioHardwarePropertyDefaultInputDevice error %x", err); + return; + } + + count = sizeof(char) * 255; + AudioDeviceGetProperty(inDevice, 0, false, kAudioDevicePropertyDeviceName, &count, &name); + ms_debug("InputDevice name = %s",name); + + count = sizeof(outDevice); + err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, + &count, + &outDevice); + + if (err) { + ms_error("get kAudioHardwarePropertyDefaultOutputDevice error %d", err); + return; + } + + count = sizeof(char) * 255; + AudioDeviceGetProperty(outDevice, 0, false, kAudioDevicePropertyDeviceName, &count, &name); + ms_debug("OutputDevice name = %s", name); + + UInt32 deviceBufferSize; + AudioStreamBasicDescription deviceFormat; + count = sizeof(deviceBufferSize); + err = AudioDeviceGetProperty(outDevice, + 0, + false, + kAudioDevicePropertyBufferSize, + &count, + &deviceBufferSize); + if (err != kAudioHardwareNoError) { + ms_error("get kAudioDevicePropertyBufferSize error %ld", err); + return; + } + ms_debug("deviceBufferSize = %d", deviceBufferSize); + count = sizeof(deviceFormat); + err = AudioDeviceGetProperty(outDevice, + 0, + false, + kAudioDevicePropertyStreamFormat, + &count, + &deviceFormat); + if (err != kAudioHardwareNoError) { + ms_error("get kAudioDevicePropertyStreamFormat error %ld", err); + return; + } + ms_debug("mSampleRate = %g", deviceFormat.mSampleRate); + ms_debug("mFormatFlags = %08lX", deviceFormat.mFormatFlags); + ms_debug("mBytesPerPacket = %ld", deviceFormat.mBytesPerPacket); + ms_debug("mFramesPerPacket = %ld", deviceFormat.mFramesPerPacket); + ms_debug("mChannelsPerFrame = %ld", deviceFormat.mChannelsPerFrame); + ms_debug("mBytesPerFrame = %ld", deviceFormat.mBytesPerFrame); + ms_debug("mBitsPerChannel = %ld", deviceFormat.mBitsPerChannel); + + count = sizeof(deviceBufferSize); + err = AudioDeviceGetProperty(outDevice, + 0, + false, + kAudioDevicePropertyBufferSize, + &count, + &deviceBufferSize); + if (err != kAudioHardwareNoError) { + ms_error("get kAudioDevicePropertyBufferSize error %ld", err); + return; + } + ms_debug("deviceBufferSize = %d", deviceBufferSize); +#endif + + MSSndCard *card=aq_card_new(); + ms_snd_card_manager_add_card(m,card); +} + +static void aq_start_r(MSSndCard *card){ + AQData *d=(AQData*)card->data; + ms_debug("aq_start_r"); + if(d->read_started == FALSE) { + OSStatus aqresult; + d->readBufferByteSize = kSecondsPerBuffer * d->rate * (d->bits / 8); + + d->readAudioFormat.mSampleRate = d->rate; + d->readAudioFormat.mFormatID = kAudioFormatLinearPCM; + d->readAudioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; + d->readAudioFormat.mFramesPerPacket = 1; + d->readAudioFormat.mChannelsPerFrame = 1; + d->readAudioFormat.mBitsPerChannel = d->bits; + d->readAudioFormat.mBytesPerPacket = d->bits / 8; + d->readAudioFormat.mBytesPerFrame = d->bits / 8; + + aqresult = AudioQueueNewInput ( + &d->readAudioFormat, + readCallback, + d, // userData + NULL, // run loop + NULL, // run loop mode + 0, // flags + &d->readQueue + ); + + ms_debug("AudioQueueNewInput = %d", aqresult); + + setupRead(card); + AudioQueueStart ( + d->readQueue, + NULL // start time. NULL means ASAP. + ); + d->read_started = TRUE; + } +} + +static void aq_stop_r(MSSndCard *card){ + AQData *d=(AQData*)card->data; + + if(d->read_started == TRUE) { + AudioQueueStop ( + d->readQueue, + true + ); + AudioQueueDispose(d->readQueue,true); + d->read_started=FALSE; + } +} + +static void aq_start_w(MSSndCard *card){ + ms_debug("aq_start_w"); + AQData *d=(AQData*)card->data; + int i; + if(d->write_started == FALSE) { + OSStatus aqresult; + d->writeBufferByteSize = kSecondsPerBuffer * d->rate * (d->bits / 8); + + d->writeAudioFormat.mSampleRate = d->rate; + d->writeAudioFormat.mFormatID = kAudioFormatLinearPCM; + d->writeAudioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; + d->writeAudioFormat.mFramesPerPacket = 1; + d->writeAudioFormat.mChannelsPerFrame = 1; + d->writeAudioFormat.mBitsPerChannel = d->bits; + d->writeAudioFormat.mBytesPerPacket = d->bits / 8; + d->writeAudioFormat.mBytesPerFrame = d->bits / 8; + + // create the playback audio queue object + aqresult = AudioQueueNewOutput ( + &d->writeAudioFormat, + writeCallback, + d, + CFRunLoopGetCurrent (), + kCFRunLoopCommonModes, + 0, // run loop flags + &d->writeQueue + ); + ms_debug("AudioQueueNewOutput = %d", aqresult); + + setupWrite(card); +#if 0 + AudioQueueStart ( + d->writeQueue, + NULL // start time. NULL means ASAP. + ); + d->write_started = TRUE; +#endif + d->curWriteBuffer = 0; + } +} + +static void aq_stop_w(MSSndCard *card){ + AQData *d=(AQData*)card->data; + if(d->write_started == TRUE) { + AudioQueueStop ( + d->writeQueue, + true + ); + + AudioQueueDispose(d->writeQueue,true); + d->write_started=FALSE; + } +} + +static mblk_t *aq_get(MSSndCard *card){ + AQData *d=(AQData*)card->data; + mblk_t *m; + ms_mutex_lock(&d->mutex); + m=getq(&d->rq); + ms_mutex_unlock(&d->mutex); + return m; +} + +static void aq_put(MSSndCard *card, mblk_t *m){ + ms_debug("aq_put"); + AQData *d=(AQData*)card->data; + ms_mutex_lock(&d->mutex); + ms_bufferizer_put(d->bufferizer,m); + ms_mutex_unlock(&d->mutex); + + if(d->write_started == FALSE && d->bufferizer->size >= d->writeBufferByteSize) { + AudioQueueBufferRef curbuf = d->writeBuffers[d->curWriteBuffer]; + if(ms_bufferizer_read(d->bufferizer, curbuf->mAudioData, d->writeBufferByteSize)) { + curbuf->mAudioDataByteSize = d->writeBufferByteSize; + putWriteAQ(d, d->curWriteBuffer); + ++d->curWriteBuffer; + } + } + if(d->write_started == FALSE && d->curWriteBuffer == kNumberAudioDataBuffers) { + OSStatus err; + err = AudioQueueStart ( + d->writeQueue, + NULL // start time. NULL means ASAP. + ); + ms_debug("AudioQueueStart %d", err); + d->write_started = TRUE; + } +} + +static void aq_read_preprocess(MSFilter *f){ + MSSndCard *card=(MSSndCard*)f->data; + aq_start_r(card); +} + +static void aq_read_postprocess(MSFilter *f){ + MSSndCard *card=(MSSndCard*)f->data; + aq_stop_r(card); +} + +static void aq_read_process(MSFilter *f){ + MSSndCard *card=(MSSndCard*)f->data; + mblk_t *m; + while((m=aq_get(card))!=NULL){ + ms_queue_put(f->outputs[0],m); + } +} + +static void aq_write_preprocess(MSFilter *f){ + ms_debug("aq_write_preprocess"); + MSSndCard *card=(MSSndCard*)f->data; + aq_start_w(card); +} + +static void aq_write_postprocess(MSFilter *f){ + ms_debug("aq_write_postprocess"); + MSSndCard *card=(MSSndCard*)f->data; + aq_stop_w(card); +} + +static void aq_write_process(MSFilter *f){ +// ms_debug("aq_write_process"); + MSSndCard *card=(MSSndCard*)f->data; + mblk_t *m; + while((m=ms_queue_get(f->inputs[0]))!=NULL){ + aq_put(card,m); + } +} + +static int set_rate(MSFilter *f, void *arg){ + ms_debug("set_rate %d", *((int*)arg)); + MSSndCard *card=(MSSndCard*)f->data; + AQData *d=(AQData*)card->data; + d->rate=*((int*)arg); + return 0; +} + +static int set_nchannels(MSFilter *f, void *arg){ + ms_debug("set_nchannels %d", *((int*)arg)); + MSSndCard *card=(MSSndCard*)f->data; + AQData *d=(AQData*)card->data; + d->stereo=(*((int*)arg)==2); + return 0; +} + +static MSFilterMethod aq_methods[]={ + { MS_FILTER_SET_SAMPLE_RATE , set_rate }, +/* not support yet + { MS_FILTER_SET_NCHANNELS , set_nchannels }, +*/ + { 0 , NULL } +}; + +MSFilterDesc aq_read_desc={ + .id=MS_AQ_READ_ID, + .name="MSAQRead", + .text="Sound capture filter for MacOS X Audio Queue Service", + .category=MS_FILTER_OTHER, + .ninputs=0, + .noutputs=1, + .preprocess=aq_read_preprocess, + .process=aq_read_process, + .postprocess=aq_read_postprocess, + .methods=aq_methods +}; + + +MSFilterDesc aq_write_desc={ + .id=MS_AQ_WRITE_ID, + .name="MSAQWrite", + .text="Sound playback filter for MacOS X Audio Queue Service", + .category=MS_FILTER_OTHER, + .ninputs=1, + .noutputs=0, + .preprocess=aq_write_preprocess, + .process=aq_write_process, + .postprocess=aq_write_postprocess, + .methods=aq_methods +}; + +MSFilter *ms_aq_read_new(MSSndCard *card){ + ms_debug("ms_aq_read_new"); + MSFilter *f=ms_filter_new_from_desc(&aq_read_desc); + f->data=card; + return f; +} + + +MSFilter *ms_aq_write_new(MSSndCard *card){ + ms_debug("ms_aq_write_new"); + MSFilter *f=ms_filter_new_from_desc(&aq_write_desc); + f->data=card; + return f; +} + +MS_FILTER_DESC_EXPORT(aq_read_desc) +MS_FILTER_DESC_EXPORT(aq_write_desc)