forked from Mirrors/freeswitch
add MODFORM-25 to SVN, thanks Hanedi Salas
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@12178 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
parent
8c97af6e5c
commit
1c73921625
23
src/mod/formats/mod_portaudio_stream/Makefile
Normal file
23
src/mod/formats/mod_portaudio_stream/Makefile
Normal file
@ -0,0 +1,23 @@
|
||||
BASE=../../../..
|
||||
BASE=../../../..
|
||||
|
||||
PA_DIR=$(BASE)/libs/portaudio
|
||||
MODPA_DIR=$(BASE)/src/mod/endpoints/mod_portaudio
|
||||
|
||||
|
||||
PALA=$(PA_DIR)/lib/libportaudio.la
|
||||
LOCAL_CFLAGS=-I. -I$(PA_DIR)/include -I$(MODPA_DIR) -D__EXTENSION__=1
|
||||
|
||||
LOCAL_INSERT_LDFLAGS=if test $$osarch = "Darwin" ; then echo "-framework CoreAudio -framework AudioToolbox -framework AudioUnit -framework Carbon" ; fi ;
|
||||
|
||||
LOCAL_LIBADD=$(PALA)
|
||||
LOCAL_OBJS=$(MODPA_DIR)/pablio.o $(MODPA_DIR)/pa_ringbuffer.o
|
||||
LOCAL_SOURCES=$(MODPA_DIR)/pablio.c $(MODPA_DIR)/pa_ringbuffer.c
|
||||
|
||||
include $(BASE)/build/modmake.rules
|
||||
|
||||
$(PALA): $(PA_DIR) $(PA_DIR)/.update
|
||||
cd $(PA_DIR) && $(MAKE)
|
||||
$(TOUCH_TARGET)
|
||||
|
||||
$(LOCAL_OBJS): $(LOCAL_SOURCES)
|
639
src/mod/formats/mod_portaudio_stream/mod_portaudio_stream.c
Normal file
639
src/mod/formats/mod_portaudio_stream/mod_portaudio_stream.c
Normal file
@ -0,0 +1,639 @@
|
||||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
* Copyright (C) 2005-2009, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Anthony Minessale II <anthm@freeswitch.org>
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* mod_portaudio_stream.c -- Portaudio Streaming interface Audio
|
||||
*
|
||||
*/
|
||||
#include "switch.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "pablio.h"
|
||||
#include <string.h>
|
||||
|
||||
#define DEFAULT_PREBUFFER_SIZE 1024 * 64
|
||||
#define SAMPLE_TYPE paInt16
|
||||
#define PREFERRED_RATE 8000
|
||||
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_portaudio_stream_load);
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_portaudio_stream_shutdown);
|
||||
SWITCH_MODULE_DEFINITION(mod_portaudio_stream, mod_portaudio_stream_load, mod_portaudio_stream_shutdown, NULL);
|
||||
static switch_memory_pool_t *module_pool = NULL;
|
||||
|
||||
struct portaudio_stream_source;
|
||||
|
||||
static struct {
|
||||
int running;
|
||||
int threads;
|
||||
switch_mutex_t *mutex;
|
||||
switch_hash_t *source_hash;
|
||||
|
||||
} globals;
|
||||
|
||||
|
||||
struct portaudio_stream_context {
|
||||
struct portaudio_stream_source *source;
|
||||
switch_mutex_t *audio_mutex;
|
||||
switch_buffer_t *audio_buffer;
|
||||
int err;
|
||||
const char *func;
|
||||
const char *file;
|
||||
int line;
|
||||
switch_file_handle_t *handle;
|
||||
struct portaudio_stream_context *next;
|
||||
};
|
||||
|
||||
typedef struct portaudio_stream_context portaudio_stream_context_t;
|
||||
|
||||
struct portaudio_stream_source {
|
||||
char *sourcename;
|
||||
int sourcedev;
|
||||
int rate;
|
||||
int interval;
|
||||
char *timer_name;
|
||||
int total;
|
||||
int ready;
|
||||
int stopped;
|
||||
uint8_t channels;
|
||||
switch_size_t samples;
|
||||
uint32_t prebuf;
|
||||
portaudio_stream_context_t *context_list;
|
||||
switch_mutex_t *mutex;
|
||||
switch_memory_pool_t *pool;
|
||||
switch_thread_rwlock_t *rwlock;
|
||||
PABLIO_Stream *audio_stream;
|
||||
switch_frame_t read_frame;
|
||||
switch_timer_t timer;
|
||||
switch_codec_t read_codec;
|
||||
switch_codec_t write_codec;
|
||||
switch_mutex_t *device_lock;
|
||||
unsigned char databuf[SWITCH_RECOMMENDED_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
typedef struct portaudio_stream_source portaudio_stream_source_t;
|
||||
|
||||
|
||||
|
||||
static int get_dev_by_number(char *numstr, int in)
|
||||
{
|
||||
int numDevices = Pa_GetDeviceCount();
|
||||
const PaDeviceInfo *pdi;
|
||||
char *end_ptr;
|
||||
int number;
|
||||
|
||||
number = (int) strtol(numstr, &end_ptr, 10);
|
||||
|
||||
if (end_ptr == numstr || number < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (number > -1 && number < numDevices && (pdi = Pa_GetDeviceInfo(number))) {
|
||||
if (in && pdi->maxInputChannels) {
|
||||
return number;
|
||||
} else if (!in && pdi->maxOutputChannels) {
|
||||
return number;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int get_dev_by_name(char *name, int in)
|
||||
{
|
||||
int i;
|
||||
int numDevices;
|
||||
const PaDeviceInfo *pdi;
|
||||
numDevices = Pa_GetDeviceCount();
|
||||
|
||||
if (numDevices < 0) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG_CLEAN, SWITCH_LOG_ERROR, "ERROR: Pa_CountDevices returned 0x%x\n", numDevices);
|
||||
return -2;
|
||||
}
|
||||
|
||||
for (i = 0; i < numDevices; i++) {
|
||||
int match = 0;
|
||||
pdi = Pa_GetDeviceInfo(i);
|
||||
|
||||
if (switch_strlen_zero(name)) {
|
||||
match = 1;
|
||||
} else if (pdi && pdi->name && strstr(pdi->name, name)) {
|
||||
match = 1;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
if (in && pdi->maxInputChannels) {
|
||||
return i;
|
||||
} else if (!in && pdi->maxOutputChannels) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static switch_status_t engage_device(portaudio_stream_source_t *source,int restart)
|
||||
{
|
||||
PaStreamParameters inputParameters,outputParameters;
|
||||
PaError err;
|
||||
int sample_rate = source->rate;
|
||||
int codec_ms = source->interval;
|
||||
|
||||
switch_mutex_init(&source->device_lock, SWITCH_MUTEX_NESTED, module_pool);
|
||||
|
||||
if (source->timer.timer_interface) {
|
||||
switch_core_timer_sync(&source->timer);
|
||||
}
|
||||
|
||||
if (source->audio_stream) {
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (!source->read_codec.implementation) {
|
||||
if (switch_core_codec_init(&source->read_codec,
|
||||
"L16",
|
||||
NULL, sample_rate, codec_ms, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL,
|
||||
NULL) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
switch_assert(source->read_codec.implementation);
|
||||
|
||||
if (!source->write_codec.implementation) {
|
||||
if (switch_core_codec_init(&source->write_codec,
|
||||
"L16",
|
||||
NULL,
|
||||
sample_rate, codec_ms, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL,
|
||||
NULL) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n");
|
||||
switch_core_codec_destroy(&source->read_codec);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!source->timer.timer_interface) {
|
||||
if (switch_core_timer_init(&source->timer,
|
||||
source->timer_name, codec_ms, source->read_codec.implementation->samples_per_packet,
|
||||
module_pool) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "setup timer failed!\n");
|
||||
switch_core_codec_destroy(&source->read_codec);
|
||||
switch_core_codec_destroy(&source->write_codec);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
source->read_frame.rate = sample_rate;
|
||||
source->read_frame.codec = &source->read_codec;
|
||||
|
||||
switch_mutex_lock(source->device_lock);
|
||||
/* LOCKED ************************************************************************************************** */
|
||||
inputParameters.device = source->sourcedev;
|
||||
inputParameters.channelCount = 1;
|
||||
inputParameters.sampleFormat = SAMPLE_TYPE;
|
||||
inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
|
||||
inputParameters.hostApiSpecificStreamInfo = NULL;
|
||||
outputParameters.device = source->sourcedev;
|
||||
outputParameters.channelCount = 1;
|
||||
outputParameters.sampleFormat = SAMPLE_TYPE;
|
||||
outputParameters.suggestedLatency = Pa_GetDeviceInfo(outputParameters.device)->defaultLowOutputLatency;
|
||||
outputParameters.hostApiSpecificStreamInfo = NULL;
|
||||
|
||||
|
||||
err = OpenAudioStream(&source->audio_stream, &inputParameters, NULL, sample_rate, paClipOff,
|
||||
source->read_codec.implementation->samples_per_packet, 0);
|
||||
/* UNLOCKED ************************************************************************************************* */
|
||||
if (err != paNoError) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error opening audio device retrying\n");
|
||||
switch_yield(1000000);
|
||||
err = OpenAudioStream(&source->audio_stream, &inputParameters, &outputParameters, sample_rate, paClipOff,
|
||||
source->read_codec.implementation->samples_per_packet, 0);
|
||||
}
|
||||
|
||||
switch_mutex_unlock(source->device_lock);
|
||||
if (err != paNoError) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't open audio device\n");
|
||||
switch_core_codec_destroy(&source->read_codec);
|
||||
switch_core_timer_destroy(&source->timer);
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void *SWITCH_THREAD_FUNC read_stream_thread(switch_thread_t *thread, void *obj)
|
||||
{
|
||||
portaudio_stream_source_t *source = obj;
|
||||
portaudio_stream_context_t *cp;
|
||||
int samples = 0;
|
||||
int bused, bytesToWrite;
|
||||
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
globals.threads++;
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
if (!source->prebuf) {
|
||||
source->prebuf = DEFAULT_PREBUFFER_SIZE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
switch_core_hash_insert(globals.source_hash, source->sourcename, source);
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
|
||||
switch_thread_rwlock_create(&source->rwlock, source->pool);
|
||||
|
||||
if (engage_device(source,0)!=SWITCH_STATUS_SUCCESS){
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, " Dev %d cant be engaged !\n",(int) source->sourcedev);
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, " Dev %d engaged at %d rate!\n",(int) source->sourcedev, (int) source->rate);
|
||||
if (globals.running && !source->stopped) {
|
||||
source->ready = 1;
|
||||
|
||||
if (!source->audio_stream){
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Audio Stream wops!\n");
|
||||
source->stopped = 0;
|
||||
source->ready = 0;
|
||||
} else {
|
||||
while (globals.running && !source->stopped) {
|
||||
samples = 0;
|
||||
switch_mutex_lock(source->device_lock);
|
||||
samples = ReadAudioStream(source->audio_stream, source->databuf,
|
||||
source->read_codec.implementation->samples_per_packet , &source->timer);
|
||||
switch_mutex_unlock(source->device_lock);
|
||||
|
||||
|
||||
if (samples) {
|
||||
bytesToWrite = source->samples;
|
||||
if (samples < bytesToWrite) {
|
||||
bytesToWrite = samples;
|
||||
}
|
||||
bytesToWrite *= source->audio_stream->bytesPerFrame;
|
||||
|
||||
if (source->total) {
|
||||
|
||||
switch_mutex_lock(source->mutex);
|
||||
for (cp = source->context_list; cp; cp = cp->next) {
|
||||
|
||||
switch_mutex_lock(cp->audio_mutex);
|
||||
|
||||
bused = switch_buffer_inuse(cp->audio_buffer);
|
||||
if (bused > source->samples * 768 ) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Leaking stream handle! [%s() %s:%d] %d used %d max\n", cp->func, cp->file,
|
||||
cp->line,(int) bused, (int) ( source->samples * 768));
|
||||
switch_buffer_zero(cp->audio_buffer);
|
||||
} else {
|
||||
switch_buffer_write(cp->audio_buffer, source->databuf, bytesToWrite);
|
||||
}
|
||||
|
||||
switch_mutex_unlock(cp->audio_mutex);
|
||||
}
|
||||
switch_mutex_unlock(source->mutex);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
source->ready = 0;
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
switch_core_hash_delete(globals.source_hash, source->sourcename);
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
switch_thread_rwlock_wrlock(source->rwlock);
|
||||
switch_thread_rwlock_unlock(source->rwlock);
|
||||
|
||||
|
||||
switch_mutex_lock(source->device_lock);
|
||||
CloseAudioStream(source->audio_stream);
|
||||
if (source->read_codec.implementation) {
|
||||
switch_core_codec_destroy(&source->read_codec);
|
||||
switch_core_codec_destroy(&source->write_codec);
|
||||
}
|
||||
if (source->write_codec.implementation) {
|
||||
switch_core_codec_destroy(&source->write_codec);
|
||||
}
|
||||
switch_mutex_unlock(source->device_lock);
|
||||
|
||||
|
||||
switch_core_destroy_memory_pool(&source->pool);
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
globals.threads--;
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, " thread ending succesfully !\n");
|
||||
switch_thread_exit(thread,SWITCH_STATUS_SUCCESS);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static switch_status_t portaudio_stream_file_open(switch_file_handle_t *handle, const char *path)
|
||||
{
|
||||
portaudio_stream_context_t *context ;
|
||||
portaudio_stream_source_t *source;
|
||||
switch_memory_pool_t *pool;
|
||||
switch_status_t status = SWITCH_STATUS_FALSE;
|
||||
switch_thread_t *thread;
|
||||
switch_threadattr_t *thd_attr = NULL;
|
||||
uint32_t rate = PREFERRED_RATE;
|
||||
char *npath ;
|
||||
int devNumber;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
handle->pre_buffer_datalen = 0;
|
||||
|
||||
if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This format does not support writing! (yet)\n");
|
||||
return status;
|
||||
}
|
||||
|
||||
npath = switch_core_strdup(module_pool, path);
|
||||
|
||||
int tmp = handle->samplerate;
|
||||
if (tmp == 8000 || tmp == 16000 || tmp == 32000 || tmp == 48000) {
|
||||
rate = tmp;
|
||||
}
|
||||
|
||||
if (*path == '#') {
|
||||
devNumber = get_dev_by_number(npath + 1, 1);
|
||||
} else {
|
||||
devNumber = get_dev_by_name(npath , 1);
|
||||
}
|
||||
npath = switch_mprintf("device-%d at %d",devNumber,rate);
|
||||
|
||||
|
||||
|
||||
switch_mutex_lock(globals.mutex);
|
||||
source = switch_core_hash_find(globals.source_hash, npath);
|
||||
|
||||
/* dev isnt there, try to start thread */
|
||||
if (!source) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, " source isnt Created, create and start thread!\n");
|
||||
|
||||
if (switch_core_new_memory_pool(&pool) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, " :S no pool\n");
|
||||
} else {
|
||||
source = switch_core_alloc(pool, sizeof(*source));
|
||||
if (source!=NULL){
|
||||
source->pool = pool;
|
||||
source->sourcedev = devNumber;
|
||||
source->sourcename = switch_core_strdup(source->pool, npath);
|
||||
source->rate = rate;
|
||||
source->interval = 20;
|
||||
source->channels = 1;
|
||||
source->timer_name = "soft";
|
||||
source->prebuf = DEFAULT_PREBUFFER_SIZE;
|
||||
source->stopped = 0;
|
||||
source->ready = 0;
|
||||
source->samples = switch_samples_per_packet(source->rate, source->interval);
|
||||
|
||||
|
||||
|
||||
switch_mutex_init(&source->mutex, SWITCH_MUTEX_NESTED, source->pool);
|
||||
|
||||
|
||||
|
||||
switch_threadattr_create(&thd_attr, source->pool);
|
||||
switch_threadattr_detach_set(thd_attr, 1);
|
||||
|
||||
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
|
||||
switch_thread_create(&thread, thd_attr, read_stream_thread, source, source->pool);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
switch_mutex_unlock(globals.mutex);
|
||||
switch_yield(1000000);
|
||||
/* dev already engaged */
|
||||
if (source) {
|
||||
|
||||
/*wait for source to be ready*/
|
||||
|
||||
while(source->ready==0){switch_yield(100000);}
|
||||
|
||||
|
||||
if (switch_thread_rwlock_tryrdlock(source->rwlock) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, " error rwlock !\n");
|
||||
source = NULL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (source) {
|
||||
status = SWITCH_STATUS_SUCCESS;
|
||||
|
||||
if ((context = switch_core_alloc(handle->memory_pool, sizeof(*context))) == 0) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, " error allocating context!\n");
|
||||
status = SWITCH_STATUS_MEMERR;
|
||||
} else {
|
||||
/* everything goes fine at this point */
|
||||
handle->samples = 0;
|
||||
handle->samplerate = source->rate;
|
||||
handle->channels = 1;
|
||||
handle->format = 0;
|
||||
handle->sections = 0;
|
||||
handle->seekable = 0;
|
||||
handle->speed = 0;
|
||||
handle->private_info = context;
|
||||
handle->interval = source->interval;
|
||||
|
||||
|
||||
switch_mutex_init(&context->audio_mutex, SWITCH_MUTEX_NESTED, handle->memory_pool);
|
||||
if (switch_buffer_create_dynamic(&context->audio_buffer, 512, 1024, 0) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n");
|
||||
status = SWITCH_STATUS_MEMERR;
|
||||
} else {
|
||||
/* context created... then continue */
|
||||
context->source = source;
|
||||
context->file = handle->file;
|
||||
context->func = handle->func;
|
||||
context->line = handle->line;
|
||||
context->handle = handle;
|
||||
switch_mutex_lock(source->mutex);
|
||||
context->next = source->context_list;
|
||||
source->context_list = context;
|
||||
source->total++;
|
||||
switch_mutex_unlock(source->mutex);
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unknown source %s\n", path);
|
||||
status = SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static switch_status_t portaudio_stream_file_close(switch_file_handle_t *handle)
|
||||
{
|
||||
portaudio_stream_context_t *cp, *last = NULL, *context = handle->private_info;
|
||||
|
||||
|
||||
switch_mutex_lock(context->source->mutex);
|
||||
for (cp = context->source->context_list; cp; cp = cp->next) {
|
||||
if (cp == context) {
|
||||
if (last) {
|
||||
last->next = cp->next;
|
||||
} else {
|
||||
context->source->context_list = cp->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
last = cp;
|
||||
}
|
||||
context->source->total--;
|
||||
switch_mutex_unlock(context->source->mutex);
|
||||
switch_buffer_destroy(&context->audio_buffer);
|
||||
switch_thread_rwlock_unlock(context->source->rwlock);
|
||||
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
static switch_status_t portaudio_stream_file_read(switch_file_handle_t *handle, void *data, size_t *len)
|
||||
{
|
||||
portaudio_stream_context_t *context = handle->private_info;
|
||||
switch_size_t bytes = 0;
|
||||
int bytesPerSample = context->source->audio_stream->bytesPerFrame;
|
||||
size_t need = *len * bytesPerSample;
|
||||
|
||||
|
||||
|
||||
if (!context->source->ready) {
|
||||
*len = 0;
|
||||
return SWITCH_STATUS_FALSE;
|
||||
}
|
||||
|
||||
switch_mutex_lock(context->audio_mutex);
|
||||
if ((bytes = switch_buffer_read(context->audio_buffer, data, need))) {
|
||||
*len = bytes / bytesPerSample;
|
||||
} else {
|
||||
if (need > 2560) {
|
||||
need = 2560;
|
||||
}
|
||||
memset(data, 255, need);
|
||||
*len = need / bytesPerSample;
|
||||
}
|
||||
switch_mutex_unlock(context->audio_mutex);
|
||||
|
||||
|
||||
|
||||
handle->sample_count += *len;
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* Registration */
|
||||
|
||||
static char *supported_formats[SWITCH_MAX_CODECS] = { 0 };
|
||||
|
||||
static void shutdown_event_handler(switch_event_t *event)
|
||||
{
|
||||
globals.running = 0;
|
||||
}
|
||||
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_portaudio_stream_load)
|
||||
{
|
||||
switch_file_interface_t *file_interface;
|
||||
supported_formats[0] = "portaudio_stream";
|
||||
|
||||
module_pool = pool;
|
||||
|
||||
Pa_Initialize();
|
||||
|
||||
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
|
||||
file_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_FILE_INTERFACE);
|
||||
file_interface->interface_name = modname;
|
||||
file_interface->extens = supported_formats;
|
||||
file_interface->file_open = portaudio_stream_file_open;
|
||||
file_interface->file_close = portaudio_stream_file_close;
|
||||
file_interface->file_read = portaudio_stream_file_read;
|
||||
|
||||
if (switch_event_bind(modname, SWITCH_EVENT_SHUTDOWN, SWITCH_EVENT_SUBCLASS_ANY, shutdown_event_handler, NULL) != SWITCH_STATUS_SUCCESS) {
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind shutdown event handler!\n");
|
||||
}
|
||||
|
||||
memset(&globals, 0, sizeof(globals));
|
||||
globals.running = 1;
|
||||
globals.threads = 0;
|
||||
switch_mutex_init(&globals.mutex, SWITCH_MUTEX_NESTED, module_pool);
|
||||
switch_core_hash_init(&globals.source_hash, module_pool);
|
||||
|
||||
|
||||
/* indicate that the module should continue to be loaded */
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_portaudio_stream_shutdown)
|
||||
{
|
||||
globals.running = 0;
|
||||
switch_event_unbind_callback(shutdown_event_handler);
|
||||
|
||||
while(globals.threads > 0) {
|
||||
switch_yield(100000);
|
||||
}
|
||||
|
||||
Pa_Terminate();
|
||||
|
||||
switch_core_hash_destroy(&globals.source_hash);
|
||||
return SWITCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* For Emacs:
|
||||
* Local Variables:
|
||||
* mode:c
|
||||
* indent-tabs-mode:t
|
||||
* tab-width:4
|
||||
* c-basic-offset:4
|
||||
* End:
|
||||
* For VIM:
|
||||
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
|
||||
*/
|
Loading…
Reference in New Issue
Block a user