switch_sql_queue_manager
parent
a1740fb600
commit
2a4cf1f04e
@ -0,0 +1,19 @@ |
||||
include $(top_srcdir)/build/modmake.rulesam |
||||
MODNAME=mod_opusfile
|
||||
|
||||
mod_LTLIBRARIES = mod_opusfile.la
|
||||
mod_opusfile_la_SOURCES = mod_opusfile.c
|
||||
mod_opusfile_la_CFLAGS = $(AM_CFLAGS)
|
||||
mod_opusfile_la_LIBADD = $(switch_builddir)/libfreeswitch.la
|
||||
mod_opusfile_la_LDFLAGS = -avoid-version -module -no-undefined -shared
|
||||
|
||||
if HAVE_OPUSFILE_DECODE |
||||
mod_opusfile_la_CFLAGS += -I$(OPUSFILE_DECODE_CFLAGS)
|
||||
mod_opusfile_la_LIBADD += $(OPUSFILE_DECODE_LIBS)
|
||||
endif |
||||
|
||||
if HAVE_OPUSFILE_ENCODE |
||||
mod_opusfile_la_CFLAGS += -I$(OPUSFILE_ENCODE_CFLAGS) -DHAVE_OPUSFILE_ENCODE
|
||||
mod_opusfile_la_LIBADD += $(OPUSFILE_ENCODE_LIBS)
|
||||
endif |
||||
|
@ -0,0 +1,489 @@ |
||||
/*
|
||||
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application |
||||
* Copyright (C) 2005-2014, 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): |
||||
*
|
||||
* Dragos Oancea <dragos.oancea@nexmo.com> (mod_opusfile.c) |
||||
* |
||||
* |
||||
* mod_opusfile.c -- Read and Write OGG/Opus files . Some parts inspired from mod_shout.c, libopusfile, libopusenc |
||||
* |
||||
*/ |
||||
#include <switch.h> |
||||
|
||||
#include <opusfile.h> |
||||
|
||||
#ifdef HAVE_OPUSFILE_ENCODE |
||||
#include <opus/opusenc.h> |
||||
#endif |
||||
|
||||
#define OPUSFILE_MAX 32*1024 |
||||
#define TC_BUFFER_SIZE 1024 * 128 |
||||
|
||||
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_opusfile_load); |
||||
SWITCH_MODULE_DEFINITION(mod_opusfile, mod_opusfile_load, NULL, NULL); |
||||
|
||||
struct opus_file_context { |
||||
switch_file_t *fd; |
||||
OggOpusFile *of; |
||||
ogg_int64_t duration; |
||||
int output_seekable; |
||||
ogg_int64_t pcm_offset; |
||||
ogg_int64_t pcm_print_offset; |
||||
ogg_int64_t next_pcm_offset; |
||||
ogg_int64_t nsamples; |
||||
opus_int32 bitrate; |
||||
int li; |
||||
int prev_li; |
||||
switch_mutex_t *audio_mutex; |
||||
switch_buffer_t *audio_buffer; |
||||
unsigned char decode_buf[OPUSFILE_MAX]; |
||||
int eof; |
||||
switch_thread_rwlock_t *rwlock; |
||||
switch_file_handle_t *handle; |
||||
size_t samplerate; |
||||
int channels; |
||||
size_t buffer_seconds; |
||||
opus_int16 *opusbuf; |
||||
switch_size_t opusbuflen; |
||||
FILE *fp; |
||||
#ifdef HAVE_OPUSFILE_ENCODE |
||||
OggOpusEnc *enc; |
||||
OggOpusComments *comments; |
||||
#endif |
||||
switch_memory_pool_t *pool; |
||||
}; |
||||
|
||||
typedef struct opus_file_context opus_file_context; |
||||
|
||||
static struct { |
||||
int debug; |
||||
} globals; |
||||
|
||||
static switch_status_t switch_opusfile_decode(opus_file_context *context, void *data, size_t bytes, int channels) |
||||
{ |
||||
int ret; |
||||
|
||||
while (!(context->eof) && switch_buffer_inuse(context->audio_buffer) < bytes) { |
||||
|
||||
if (channels == 1) { |
||||
ret = op_read(context->of, (opus_int16 *)context->decode_buf, sizeof(context->decode_buf), NULL); |
||||
} else if (channels > 1) { |
||||
ret = op_read_stereo(context->of, (opus_int16 *)context->decode_buf, sizeof(context->decode_buf)); |
||||
|
||||
} else { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "[OGG/OPUS File] Invalid number of channels"); |
||||
return SWITCH_STATUS_FALSE; |
||||
} |
||||
|
||||
if (ret < 0) { |
||||
switch(ret) { |
||||
case OP_HOLE: /* There was a hole in the data, and some samples may have been skipped. Call this function again to continue decoding past the hole.*/ |
||||
case OP_EREAD: /*An underlying read operation failed. This may signal a truncation attack from an <https:> source.*/ |
||||
|
||||
case OP_EFAULT: /* An internal memory allocation failed. */ |
||||
|
||||
case OP_EIMPL: /*An unseekable stream encountered a new link that used a feature that is not implemented, such as an unsupported channel family.*/ |
||||
|
||||
case OP_EINVAL: /* The stream was only partially open. */ |
||||
|
||||
case OP_ENOTFORMAT: /* An unseekable stream encountered a new link that did not have any logical Opus streams in it. */ |
||||
|
||||
case OP_EBADHEADER: /*An unseekable stream encountered a new link with a required header packet that was not properly formatted, contained illegal values, or was missing altogether.*/ |
||||
|
||||
case OP_EVERSION: /*An unseekable stream encountered a new link with an ID header that contained an unrecognized version number.*/ |
||||
|
||||
case OP_EBADPACKET: /*Failed to properly decode the next packet.*/ |
||||
|
||||
case OP_EBADLINK: /*We failed to find data we had seen before.*/ |
||||
|
||||
case OP_EBADTIMESTAMP: /*An unseekable stream encountered a new link with a starting timestamp that failed basic validity checks.*/ |
||||
|
||||
default: |
||||
goto err; |
||||
break; |
||||
} |
||||
} else if (ret == 0) { |
||||
/*The number of samples returned may be 0 if the buffer was too small to store even a single sample for both channels, or if end-of-file was reached*/ |
||||
context->eof = 1;
|
||||
if (globals.debug) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "[OGG/OPUS file]: EOF reached\n"); |
||||
} |
||||
break; |
||||
} else /* (ret > 0)*/ { |
||||
/*The number of samples read per channel on success*/ |
||||
switch_buffer_write(context->audio_buffer, context->decode_buf, ret * sizeof(int16_t) * channels); |
||||
|
||||
if (globals.debug) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
|
||||
"[OGG/OPUS File]: Read samples: [%d]. Wrote bytes to audio buffer: [%d]\n", ret, (int)(ret * sizeof(int16_t) * channels)); |
||||
} |
||||
|
||||
} |
||||
} |
||||
return SWITCH_STATUS_SUCCESS; |
||||
|
||||
err: |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "[OGG/OPUS File]: error decoding file: [%d]\n", ret); |
||||
|
||||
return SWITCH_STATUS_FALSE; |
||||
} |
||||
|
||||
|
||||
static switch_status_t switch_opusfile_open(switch_file_handle_t *handle, const char *path) |
||||
{ |
||||
opus_file_context *context; |
||||
char *ext; |
||||
unsigned int flags = 0; |
||||
int ret; |
||||
switch_status_t status = SWITCH_STATUS_SUCCESS; |
||||
|
||||
if ((ext = strrchr(path, '.')) == 0) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "[OGG/OPUS File] Invalid Format\n"); |
||||
return SWITCH_STATUS_GENERR; |
||||
} |
||||
ext++; |
||||
|
||||
if ((context = switch_core_alloc(handle->memory_pool, sizeof(*context))) == 0) { |
||||
return SWITCH_STATUS_MEMERR; |
||||
} |
||||
|
||||
context->pool = handle->memory_pool; |
||||
|
||||
switch_thread_rwlock_create(&(context->rwlock), context->pool); |
||||
|
||||
switch_thread_rwlock_rdlock(context->rwlock); |
||||
|
||||
switch_mutex_init(&context->audio_mutex, SWITCH_MUTEX_NESTED, context->pool); |
||||
|
||||
if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) { |
||||
flags |= SWITCH_FOPEN_WRITE | SWITCH_FOPEN_CREATE; |
||||
if (switch_test_flag(handle, SWITCH_FILE_WRITE_APPEND) || switch_test_flag(handle, SWITCH_FILE_WRITE_OVER)) { |
||||
flags |= SWITCH_FOPEN_READ; |
||||
} else { |
||||
flags |= SWITCH_FOPEN_TRUNCATE; |
||||
} |
||||
} |
||||
|
||||
if (switch_test_flag(handle, SWITCH_FILE_FLAG_READ)) { |
||||
if (switch_buffer_create_dynamic(&context->audio_buffer, TC_BUFFER_SIZE, TC_BUFFER_SIZE * 2, 0) != SWITCH_STATUS_SUCCESS) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Memory Error!\n"); |
||||
switch_goto_status(SWITCH_STATUS_GENERR, out); |
||||
} |
||||
|
||||
flags |= SWITCH_FOPEN_READ; |
||||
} |
||||
|
||||
handle->samples = 0; |
||||
handle->samplerate = context->samplerate = 48000; |
||||
handle->format = 0; |
||||
handle->sections = 0; |
||||
handle->seekable = 1; |
||||
handle->speed = 0; |
||||
handle->pos = 0; |
||||
handle->private_info = context; |
||||
context->handle = handle; |
||||
memcpy(handle->file_path, path, strlen(path)); |
||||
|
||||
#ifdef HAVE_OPUSFILE_ENCODE |
||||
if (switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) { |
||||
int err_open; |
||||
|
||||
context->channels = handle->channels; |
||||
context->samplerate = handle->samplerate; |
||||
handle->seekable = 0; |
||||
context->comments = ope_comments_create(); |
||||
ope_comments_add(context->comments, "METADATA", "Freeswitch/mod_opusfile"); |
||||
context->enc = ope_encoder_create_file(handle->file_path, context->comments, context->samplerate, context->channels, 0, &err_open); |
||||
if (!context->enc) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "[OGG/OPUS File] Can't open file for writing\n"); |
||||
switch_goto_status(SWITCH_STATUS_FALSE, out); |
||||
} |
||||
goto out; |
||||
} |
||||
#endif |
||||
|
||||
context->of = op_open_file(path, &ret); |
||||
if (!context->of) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "[OGG/OPUS File] Error opening %s\n", path); |
||||
switch_goto_status(SWITCH_STATUS_GENERR, out); |
||||
} |
||||
|
||||
if (switch_test_flag(handle, SWITCH_FILE_WRITE_APPEND)) { |
||||
op_pcm_seek(context->of, 0); // overwrite
|
||||
handle->pos = 0; |
||||
} |
||||
|
||||
context->prev_li = -1; |
||||
context->nsamples = 0; |
||||
|
||||
handle->channels = context->channels = op_channel_count(context->of, -1); |
||||
context->pcm_offset = op_pcm_tell(context->of); |
||||
|
||||
if(context->pcm_offset!=0){ |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "[OGG/OPUS File] Non-zero starting PCM offset: [%li]\n", (long)context->pcm_offset); |
||||
} |
||||
context->pcm_print_offset = context->pcm_offset - 48000; |
||||
context->bitrate = 0; |
||||
context->buffer_seconds = 1; |
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "[OGG/OPUS File] Opening File [%s] %dhz\n", path, handle->samplerate); |
||||
|
||||
context->li = op_current_link(context->of); |
||||
|
||||
if (context->li != context->prev_li) { |
||||
const OpusHead *head; |
||||
const OpusTags *tags; |
||||
head=op_head(context->of, context->li); |
||||
if (head) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "[OGG/OPUS File] Channels: %i\n", head->channel_count); |
||||
if (head->input_sample_rate) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "[OGG/OPUS File] Original sampling rate: %lu Hz\n", (unsigned long)head->input_sample_rate); |
||||
} |
||||
} |
||||
if (op_seekable(context->of)) { |
||||
ogg_int64_t duration; |
||||
opus_int64 size; |
||||
duration = op_pcm_total(context->of, context->li); |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO , "[OGG/OPUS File] Duration (samples): %u", (unsigned int)duration); |
||||
size = op_raw_total(context->of, context->li); |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,"[OGG/OPUS File] Size (bytes): %u", (unsigned int)size); |
||||
} |
||||
tags = op_tags(context->of, context->li); |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "[OGG/OPUS File] Encoded by: %s\n", tags->vendor); |
||||
} |
||||
|
||||
out: |
||||
switch_thread_rwlock_unlock(context->rwlock); |
||||
|
||||
return status; |
||||
} |
||||
|
||||
static switch_status_t switch_opusfile_close(switch_file_handle_t *handle) |
||||
{ |
||||
opus_file_context *context = handle->private_info; |
||||
|
||||
if (context->of) { |
||||
op_free(context->of); |
||||
} |
||||
#ifdef HAVE_OPUSFILE_ENCODE |
||||
if (context->enc) { |
||||
ope_encoder_drain(context->enc); |
||||
ope_encoder_destroy(context->enc); |
||||
} |
||||
if (context->comments) { |
||||
ope_comments_destroy(context->comments); |
||||
} |
||||
#endif |
||||
if (context->audio_buffer) { |
||||
switch_buffer_destroy(&context->audio_buffer); |
||||
} |
||||
return SWITCH_STATUS_SUCCESS; |
||||
} |
||||
|
||||
static switch_status_t switch_opusfile_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence) |
||||
{ |
||||
int ret; |
||||
opus_file_context *context = handle->private_info; |
||||
|
||||
if (handle->handler || switch_test_flag(handle, SWITCH_FILE_FLAG_WRITE)) { |
||||
return SWITCH_STATUS_FALSE; |
||||
} else { |
||||
if (whence == SWITCH_SEEK_CUR) { |
||||
samples -= switch_buffer_inuse(context->audio_buffer) / sizeof(int16_t); |
||||
} |
||||
switch_buffer_zero(context->audio_buffer); |
||||
ret = op_pcm_seek(context->of, samples); |
||||
if (globals.debug) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,"[OGG/OPUS File] seek samples: [%u]", (unsigned int)samples); |
||||
} |
||||
if (ret == 0) { |
||||
handle->pos = *cur_sample = samples; |
||||
return SWITCH_STATUS_SUCCESS; |
||||
} |
||||
} |
||||
return SWITCH_STATUS_FALSE; |
||||
} |
||||
|
||||
static switch_status_t switch_opusfile_read(switch_file_handle_t *handle, void *data, size_t *len) |
||||
{ |
||||
opus_file_context *context = handle->private_info; |
||||
size_t bytes = *len * sizeof(int16_t) * handle->real_channels; |
||||
size_t rb = 0, newbytes; |
||||
|
||||
if (!context) { |
||||
return SWITCH_STATUS_FALSE; |
||||
} |
||||
|
||||
if (!handle->handler) { |
||||
if (switch_opusfile_decode(context, data, bytes , handle->real_channels) == SWITCH_STATUS_FALSE) { |
||||
context->eof = 1; |
||||
} |
||||
} |
||||
switch_mutex_lock(context->audio_mutex); |
||||
rb = switch_buffer_read(context->audio_buffer, data, bytes); |
||||
switch_mutex_unlock(context->audio_mutex); |
||||
|
||||
if (!rb && (context->eof)) { |
||||
return SWITCH_STATUS_FALSE; |
||||
} |
||||
if (rb) { |
||||
*len = rb / sizeof(int16_t) / handle->real_channels; |
||||
if (globals.debug) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "[OGG/OPUS File] rb: [%d] *len: [%d]\n", (int)rb, (int)*len); |
||||
} |
||||
} else { |
||||
newbytes = (2 * handle->samplerate * handle->real_channels) * context->buffer_seconds; |
||||
if (newbytes < bytes) { |
||||
bytes = newbytes; |
||||
} |
||||
if (globals.debug) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "[OGG/OPUS File] Padding with empty audio. seconds: [%d] bytes: [%d] newbytes: [%d] real_channels: [%d]\n", (int)context->buffer_seconds, (int)bytes, (int)newbytes, (int)handle->real_channels); |
||||
} |
||||
memset(data, 255, bytes); |
||||
*len = bytes / sizeof(int16_t) / handle->real_channels; |
||||
} |
||||
|
||||
handle->pos += *len; |
||||
handle->sample_count += *len; |
||||
|
||||
return SWITCH_STATUS_SUCCESS; |
||||
} |
||||
|
||||
static switch_status_t switch_opusfile_write(switch_file_handle_t *handle, void *data, size_t *len) |
||||
{ |
||||
#ifdef HAVE_OPUSFILE_ENCODE |
||||
size_t nsamples = *len; |
||||
int err_open; |
||||
int ret; |
||||
|
||||
opus_file_context *context = handle->private_info; |
||||
|
||||
if (!handle) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error no handle\n"); |
||||
return SWITCH_STATUS_FALSE; |
||||
} |
||||
|
||||
if (!(context = handle->private_info)) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error no context\n"); |
||||
return SWITCH_STATUS_FALSE; |
||||
} |
||||
context->comments = ope_comments_create(); |
||||
ope_comments_add(context->comments, "METADATA", "Freeswitch/mod_opusfile"); |
||||
if (!context->enc) { |
||||
context->enc = ope_encoder_create_file(handle->file_path, context->comments, handle->samplerate, handle->channels, 0, &err_open); |
||||
if (!context->enc) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "[OGG/OPUS File] Can't open file for writing\n"); |
||||
return SWITCH_STATUS_FALSE; |
||||
} |
||||
} |
||||
|
||||
if (globals.debug) { |
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,"[OGG/OPUS File] write nsamples: [%d]", (int)nsamples); |
||||
} |
||||
|
||||
ret = ope_encoder_write(context->enc, (opus_int16 *)data, nsamples); |
||||
if (ret != OPE_OK) { |
||||
return SWITCH_STATUS_FALSE; |
||||
} |
||||
|
||||
handle->sample_count += *len; |
||||
#endif |
||||
return SWITCH_STATUS_SUCCESS; |
||||
} |
||||
|
||||
static switch_status_t switch_opusfile_set_string(switch_file_handle_t *handle, switch_audio_col_t col, const char *string) |
||||
{ |
||||
return SWITCH_STATUS_FALSE; |
||||
} |
||||
|
||||
static switch_status_t switch_opusfile_get_string(switch_file_handle_t *handle, switch_audio_col_t col, const char **string) |
||||
{ |
||||
return SWITCH_STATUS_FALSE; |
||||
} |
||||
|
||||
#define OPUSFILE_DEBUG_SYNTAX "<on|off>" |
||||
SWITCH_STANDARD_API(mod_opusfile_debug) |
||||
{ |
||||
if (zstr(cmd)) { |
||||
stream->write_function(stream, "-USAGE: %s\n", OPUSFILE_DEBUG_SYNTAX); |
||||
} else { |
||||
if (!strcasecmp(cmd, "on")) { |
||||
globals.debug = 1; |
||||
stream->write_function(stream, "OPUSFILE Debug: on\n"); |
||||
} else if (!strcasecmp(cmd, "off")) { |
||||
globals.debug = 0; |
||||
stream->write_function(stream, "OPUSFILE Debug: off\n"); |
||||
} else { |
||||
stream->write_function(stream, "-USAGE: %s\n", OPUSFILE_DEBUG_SYNTAX); |
||||
} |
||||
} |
||||
return SWITCH_STATUS_SUCCESS; |
||||
} |
||||
|
||||
/* Registration */ |
||||
|
||||
static char *supported_formats[SWITCH_MAX_CODECS] = { 0 }; |
||||
|
||||
SWITCH_MODULE_LOAD_FUNCTION(mod_opusfile_load) |
||||
{ |
||||
switch_file_interface_t *file_interface; |
||||
switch_api_interface_t *commands_api_interface; |
||||
|
||||
supported_formats[0] = "opus"; |
||||
|
||||
*module_interface = switch_loadable_module_create_module_interface(pool, modname); |
||||
|
||||
SWITCH_ADD_API(commands_api_interface, "opusfile_debug", "Set OPUSFILE Debug", mod_opusfile_debug, OPUSFILE_DEBUG_SYNTAX); |
||||
|
||||
switch_console_set_complete("add opusfile_debug on"); |
||||
switch_console_set_complete("add opusfile_debug off"); |
||||
|
||||
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 = switch_opusfile_open; |
||||
file_interface->file_close = switch_opusfile_close; |
||||
file_interface->file_read = switch_opusfile_read; |
||||
file_interface->file_write = switch_opusfile_write; |
||||
file_interface->file_seek = switch_opusfile_seek; |
||||
file_interface->file_set_string = switch_opusfile_set_string; |
||||
file_interface->file_get_string = switch_opusfile_get_string; |
||||
|
||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "mod_opusfile loaded\n"); |
||||
|
||||
/* indicate that the module should continue to be loaded */ |
||||
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 noet: |
||||
*/ |
Loading…
Reference in new issue