\{
*/
+
+///\defgroup pa1 Media Bugs
+///\ingroup core1
+///\{
+
+/*!
+ \brief Add a media bug to the session
+ \param session the session to add the bug to
+ \param callback a callback for events
+ \param user_data arbitrary user data
+ \param new_bug pointer to assign new bug to
+ \return SWITCH_STATUS_SUCCESS if the operation was a success
+*/
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_add(switch_core_session_t *session,
+ switch_media_bug_callback_t callback,
+ void *user_data,
+ switch_media_bug_t **new_bug);
+
+
+/*!
+ \brief Remove a media bug from the session
+ \param session the session to remove the bug from
+ \param bug bug to remove
+ \return SWITCH_STATUS_SUCCESS if the operation was a success
+*/
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_remove(switch_core_session_t *session, switch_media_bug_t **bug);
+
+/*!
+ \brief Remove all media bugs from the session
+ \param session the session to remove the bugs from
+ \return SWITCH_STATUS_SUCCESS if the operation was a success
+*/
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_remove_all(switch_core_session_t *session);
+
+/*!
+ \brief Read a frame from the bug
+ \param bug the bug to read from
+ \param frame the frame to write the data to
+ \return the amount of data
+*/
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_read(switch_media_bug_t *bug, switch_frame_t *frame);
+
+///\}
+
///\defgroup pa1 Port Allocation
///\ingroup core1
///\{
\param new pointer for the return value
\return SWITCH_STATUS_SUCCESS if the operation was a success
*/
-SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(switch_port_t start, switch_port_t end, uint8_t inc, switch_core_port_allocator_t **new_allocator);
+SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(switch_port_t start,
+ switch_port_t end,
+ uint8_t inc,
+ switch_core_port_allocator_t **new_allocator);
/*!
\brief Get a port from the port allocator
const char *terminators,
char *terminator,
unsigned int timeout);
-
+
+/*!
+ \brief Record a session to disk
+ \param session the session to record
+ \param file the path to the file
+ \param fh file handle to use (NULL for builtin one)
+ \return SWITCH_STATUS_SUCCESS if all is well
+*/
+SWITCH_DECLARE(switch_status_t) switch_ivr_record_session(switch_core_session_t *session, char *file, switch_file_handle_t *fh);
+
+/*!
+ \brief Stop Recording a session
+ \param session the session to stop recording
+ \param file the path to the file
+ \return SWITCH_STATUS_SUCCESS if all is well
+*/
+SWITCH_DECLARE(switch_status_t) switch_ivr_stop_record_session(switch_core_session_t *session, char *file);
+
/*!
\brief play a file from the disk to the session
\param session the session to play the file too
SWITCH_BITPACK_MODE_AAL2
} switch_bitpack_mode_t;
+typedef enum {
+ SWITCH_ABC_TYPE_INIT,
+ SWITCH_ABC_TYPE_READ,
+ SWITCH_ABC_TYPE_WRITE,
+ SWITCH_ABC_TYPE_CLOSE,
+} switch_abc_type_t;
typedef struct {
switch_byte_t *buf;
typedef struct switch_speech_interface switch_speech_interface_t;
typedef struct switch_directory_interface switch_directory_interface_t;
typedef struct switch_core_port_allocator switch_core_port_allocator_t;
-typedef struct switch_audio_bug switch_audio_bug_t;
-typedef void (*switch_audio_bug_read_callback_t)(switch_audio_bug_t *);
-typedef void (*switch_audio_bug_write_callback_t)(switch_audio_bug_t *);
+typedef struct switch_media_bug switch_media_bug_t;
+typedef void (*switch_media_bug_callback_t)(switch_media_bug_t *, void *, switch_abc_type_t);
typedef void (*switch_application_function_t)(switch_core_session_t *, char *);
typedef void (*switch_event_callback_t)(switch_event_t *);
typedef switch_caller_extension_t *(*switch_dialplan_hunt_function_t)(switch_core_session_t *);
/*! \brief A core session representing a call and all of it's resources */
struct switch_core_session;
/*! \brief An audio bug */
-struct switch_audio_bug;
+struct switch_media_bug;
SWITCH_END_EXTERN_C
#endif
+#define SWITCH_SMAX 32767
+#define SWITCH_SMIN -32768
+#define switch_normalize_to_16bit(n) if (n > SWITCH_SMAX) n = SWITCH_SMAX / 2; else if (n < SWITCH_SMIN) n = SWITCH_SMIN / 2;
+
+
+
/*!
\brief Evaluate the truthfullness of a string expression
\param expr a string expression
}
-#define SMAX 32767
-#define SMIN -32768
-#define normalize_to_16bit(n) if (n > SMAX) n = SMAX; else if (n < SMIN) n = SMIN;
-
static void switch_change_sln_volume(int16_t *data, uint32_t samples, int32_t vol)
{
int16_t *p = data;
for (x = 0; x < samples; x++) {
b = (int32_t)((double)p[x] * mult);
- normalize_to_16bit(b);
+ switch_normalize_to_16bit(b);
p[x] = (int16_t) b;
}
}
for (x = 0; x < imember->read / 2; x++) {
int32_t z = muxed[x] + bptr[x];
- normalize_to_16bit(z);
+ switch_normalize_to_16bit(z);
muxed[x] = (int16_t)z;
}
}
+static void record_session_function(switch_core_session_t *session, char *data)
+{
+ switch_channel_t *channel;
+ channel = switch_core_session_get_channel(session);
+ assert(channel != NULL);
+
+ switch_ivr_record_session(session, data, NULL);
+}
+
+
+static void stop_record_session_function(switch_core_session_t *session, char *data)
+{
+ switch_channel_t *channel;
+ channel = switch_core_session_get_channel(session);
+ assert(channel != NULL);
+
+ switch_ivr_stop_record_session(session, data);
+}
+
+
static const switch_application_interface_t speak_application_interface = {
/*.interface_name */ "speak",
/*.application_function */ speak_function
&speak_application_interface
};
+
+static const switch_application_interface_t record_session_application_interface = {
+ /*.interface_name */ "record_session",
+ /*.application_function */ record_session_function,
+ NULL,NULL,NULL,
+ &record_application_interface
+};
+
+
+static const switch_application_interface_t stop_record_session_application_interface = {
+ /*.interface_name */ "stop_record_session",
+ /*.application_function */ stop_record_session_function,
+ NULL,NULL,NULL,
+ &record_session_application_interface
+};
+
static const switch_application_interface_t playback_application_interface = {
/*.interface_name */ "playback",
/*.application_function */ playback_function,
NULL,NULL,NULL,
- /*.next*/ &record_application_interface
+ /*.next*/ &stop_record_session_application_interface
};
static const switch_loadable_module_interface_t mod_playback_module_interface = {
#define SWITCH_SQL_QUEUE_LEN 2000
-struct switch_audio_bug {
- switch_codec_t *read_codec;
- switch_codec_t *write_codec;
+struct switch_media_bug {
switch_buffer_t *raw_write_buffer;
switch_buffer_t *raw_read_buffer;
- uint8_t data[SWITCH_RECCOMMENDED_BUFFER_SIZE];
- switch_audio_bug_read_callback_t read_callback;
- switch_audio_bug_read_callback_t write_callback;
- struct switch_audio_bug *next;
+ switch_media_bug_callback_t callback;
+ switch_mutex_t *read_mutex;
+ switch_mutex_t *write_mutex;
+ switch_core_session_t *session;
+ void *user_data;
+ struct switch_media_bug *next;
};
struct switch_core_session {
void *private_info;
switch_queue_t *event_queue;
switch_queue_t *private_event_queue;
+ switch_thread_rwlock_t *bug_rwlock;
+ switch_media_bug_t *bugs;
};
SWITCH_DECLARE_DATA switch_directories SWITCH_GLOBAL_dirs;
}
}
+static void switch_core_media_bug_destroy(switch_media_bug_t *bug)
+{
+ switch_buffer_destroy(&bug->raw_read_buffer);
+ switch_buffer_destroy(&bug->raw_write_buffer);
+}
+
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_read(switch_media_bug_t *bug, switch_frame_t *frame)
+{
+ uint32_t bytes = 0;
+ uint8_t data[SWITCH_RECCOMMENDED_BUFFER_SIZE] = {0};
+ uint32_t datalen = 0;
+ int16_t *dp, *fp;
+ uint32_t x;
+ uint32_t rlen = switch_buffer_inuse(bug->raw_read_buffer);
+ uint32_t wlen = switch_buffer_inuse(bug->raw_write_buffer);
+ uint32_t blen;
+ uint32_t rdlen = 0;
+ uint32_t maxlen;
+
+ if (!rlen && !wlen) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ maxlen = sizeof(data) > frame->buflen ? frame->buflen : sizeof(data);
+ if ((rdlen = rlen > wlen ? wlen : rlen) > maxlen) {
+ rdlen = maxlen;
+ }
+
+ frame->datalen = 0;
+
+ if (rlen) {
+ switch_mutex_lock(bug->read_mutex);
+
+ frame->datalen = (uint32_t) switch_buffer_read(bug->raw_read_buffer,
+ frame->data,
+ rdlen);
+ switch_mutex_unlock(bug->read_mutex);
+ }
+
+ if (wlen) {
+ switch_mutex_lock(bug->write_mutex);
+ datalen = (uint32_t) switch_buffer_read(bug->raw_write_buffer,
+ data,
+ rdlen);
+ switch_mutex_unlock(bug->write_mutex);
+ }
+
+ bytes = (datalen > frame->datalen) ? datalen : frame->datalen;
+
+ if (bytes) {
+ dp = (int16_t *) data;
+ fp = (int16_t *) frame->data;
+
+ rlen = frame->datalen / 2;
+ wlen = datalen / 2;
+ blen = bytes / 2;
+
+ for(x = 0; x < blen; x++) {
+ int32_t z = 0;
+
+ if (x < rlen) {
+ z += (int32_t) *(fp+x);
+ }
+ if (x < wlen) {
+ z += (int32_t)*(dp+x);
+ }
+ switch_normalize_to_16bit(z);
+ *(fp+x) = (int16_t) z;
+ }
+
+ frame->datalen = bytes;
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ return SWITCH_STATUS_FALSE;
+}
+
+#define MAX_BUG_BUFFER 1024 * 512
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_add(switch_core_session_t *session,
+ switch_media_bug_callback_t callback,
+ void *user_data,
+ switch_media_bug_t **new_bug)
+
+{
+ switch_media_bug_t *bug;
+ switch_size_t bytes;
+
+ if (!(bug = switch_core_session_alloc(session, sizeof(*bug)))) {
+ return SWITCH_STATUS_MEMERR;
+ }
+
+ bug->callback = callback;
+ bug->user_data = user_data;
+ bug->session = session;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Attaching BUG to %s\n", switch_channel_get_name(session->channel));
+ bytes = session->read_codec->implementation->bytes_per_frame * 2;
+ switch_buffer_create_dynamic(&bug->raw_read_buffer, bytes, bytes, MAX_BUG_BUFFER);
+ bytes = session->write_codec->implementation->bytes_per_frame * 2;
+ switch_buffer_create_dynamic(&bug->raw_write_buffer, bytes, bytes, MAX_BUG_BUFFER);
+ switch_mutex_init(&bug->read_mutex, SWITCH_MUTEX_NESTED, session->pool);
+ switch_mutex_init(&bug->write_mutex, SWITCH_MUTEX_NESTED, session->pool);
+
+ switch_thread_rwlock_wrlock(session->bug_rwlock);
+ bug->next = session->bugs;
+ session->bugs = bug;
+ switch_thread_rwlock_unlock(session->bug_rwlock);
+ *new_bug = bug;
+
+ if (bug->callback) {
+ bug->callback(bug, bug->user_data, SWITCH_ABC_TYPE_INIT);
+ }
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_remove_all(switch_core_session_t *session)
+{
+ switch_media_bug_t *bp;
+
+ if (session->bugs) {
+ switch_thread_rwlock_wrlock(session->bug_rwlock);
+ for (bp = session->bugs; bp; bp = bp->next) {
+ if (bp->callback) {
+ bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_CLOSE);
+ }
+ switch_core_media_bug_destroy(bp);
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing BUG from %s\n", switch_channel_get_name(session->channel));
+ }
+ switch_thread_rwlock_unlock(session->bug_rwlock);
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ return SWITCH_STATUS_FALSE;
+}
+
+SWITCH_DECLARE(switch_status_t) switch_core_media_bug_remove(switch_core_session_t *session, switch_media_bug_t **bug)
+{
+ switch_media_bug_t *bp = NULL, *last = NULL;
+
+ if (session->bugs) {
+ switch_thread_rwlock_wrlock(session->bug_rwlock);
+ for (bp = session->bugs; bp; bp = bp->next) {
+ if (bp == *bug) {
+ if (last) {
+ last->next = bp->next;
+ } else {
+ session->bugs = bp->next;
+ }
+ break;
+ }
+ last = bp;
+ }
+ switch_thread_rwlock_unlock(session->bug_rwlock);
+
+ if (bp) {
+ if (bp->callback) {
+ bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_CLOSE);
+ }
+ switch_core_media_bug_destroy(bp);
+ *bug = NULL;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Removing BUG from %s\n", switch_channel_get_name(session->channel));
+ return SWITCH_STATUS_SUCCESS;
+ }
+ }
+
+ return SWITCH_STATUS_FALSE;
+}
+
struct switch_core_port_allocator {
switch_port_t start;
switch_port_t end;
switch_memory_pool_t *pool;
};
-SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(switch_port_t start, switch_port_t end, uint8_t inc, switch_core_port_allocator_t **new_allocator)
+SWITCH_DECLARE(switch_status_t) switch_core_port_allocator_new(switch_port_t start,
+ switch_port_t end,
+ uint8_t inc,
+ switch_core_port_allocator_t **new_allocator)
{
switch_status_t status;
switch_memory_pool_t *pool;
{
switch_io_event_hook_read_frame_t *ptr;
switch_status_t status;
- int need_codec, perfect;
+ int need_codec, perfect, do_bugs = 0;
top:
status = SWITCH_STATUS_FALSE;
need_codec = TRUE;
}
+ if (session->bugs && !need_codec) {
+ do_bugs = 1;
+ need_codec = 1;
+ }
+
if (status == SWITCH_STATUS_SUCCESS && need_codec) {
switch_frame_t *enc_frame, *read_frame = *frame;
read_frame->rate = session->read_resampler->to_rate;
}
+ if (session->bugs) {
+ switch_media_bug_t *bp;
+ switch_thread_rwlock_rdlock(session->bug_rwlock);
+ for (bp = session->bugs; bp; bp = bp->next) {
+ switch_mutex_lock(bp->read_mutex);
+ switch_buffer_write(bp->raw_read_buffer, read_frame->data, read_frame->datalen);
+ if (bp->callback) {
+ bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_READ);
+ }
+ switch_mutex_unlock(bp->read_mutex);
+ }
+ switch_thread_rwlock_unlock(session->bug_rwlock);
+ }
+
+ if (do_bugs) {
+ goto done;
+ }
+
if (session->read_codec) {
if ((*frame)->datalen == session->read_codec->implementation->bytes_per_frame) {
perfect = TRUE;
switch_status_t status = SWITCH_STATUS_FALSE;
switch_frame_t *enc_frame = NULL, *write_frame = frame;
- unsigned int flag = 0, need_codec = 0, perfect = 0;
+ unsigned int flag = 0, need_codec = 0, perfect = 0, do_bugs = 0, do_write = 0;
switch_io_flag_t io_flag = SWITCH_IO_FLAG_NOOP;
assert(session != NULL);
need_codec = TRUE;
}
+ if (session->bugs && !need_codec) {
+ do_bugs = 1;
+ need_codec = 1;
+ }
+
if (need_codec) {
if (frame->codec) {
session->raw_write_frame.datalen = session->raw_write_frame.buflen;
write_frame->rate = session->write_resampler->to_rate;
}
-
+ if (session->bugs) {
+ switch_media_bug_t *bp;
+ switch_thread_rwlock_rdlock(session->bug_rwlock);
+ for (bp = session->bugs; bp; bp = bp->next) {
+ switch_mutex_lock(bp->write_mutex);
+ switch_buffer_write(bp->raw_write_buffer, write_frame->data, write_frame->datalen);
+ switch_mutex_unlock(bp->write_mutex);
+ if (bp->callback) {
+ bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_WRITE);
+ }
+ }
+ switch_thread_rwlock_unlock(session->bug_rwlock);
+ }
+ if (do_bugs) {
+ do_write = 1;
+ goto done;
+ }
if (session->write_codec) {
if (write_frame->datalen == session->write_codec->implementation->bytes_per_frame) {
perfect = TRUE;
}
}
} else {
+ do_write = 1;
+ }
+
+ done:
+ if (do_write) {
return perform_write(session, frame, timeout, io_flag, stream_id);
}
switch_event_fire(&event);
}
+ switch_core_media_bug_remove_all(*session);
switch_buffer_destroy(&(*session)->raw_read_buffer);
switch_buffer_destroy(&(*session)->raw_write_buffer);
switch_channel_uninit((*session)->channel);
session->enc_read_frame.buflen = sizeof(session->enc_read_buf);
switch_mutex_init(&session->mutex, SWITCH_MUTEX_NESTED, session->pool);
+ switch_thread_rwlock_create(&session->bug_rwlock, session->pool);
switch_thread_cond_create(&session->cond, session->pool);
switch_thread_rwlock_create(&session->rwlock, session->pool);
return status;
}
+static void record_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
+{
+ switch_file_handle_t *fh = (switch_file_handle_t *) user_data;
+ uint8_t data[SWITCH_RECCOMMENDED_BUFFER_SIZE];
+ switch_frame_t frame = {0};
+
+ frame.data = data;
+ frame.buflen = SWITCH_RECCOMMENDED_BUFFER_SIZE;
+
+ switch(type) {
+ case SWITCH_ABC_TYPE_INIT:
+ break;
+ case SWITCH_ABC_TYPE_CLOSE:
+ switch_core_file_close(fh);
+ case SWITCH_ABC_TYPE_READ:
+ if (fh) {
+ switch_size_t len;
+
+ if (switch_core_media_bug_read(bug, &frame) == SWITCH_STATUS_SUCCESS) {
+ len = (switch_size_t) frame.datalen / 2;
+ switch_core_file_write(fh, frame.data, &len);
+ }
+ }
+ break;
+ case SWITCH_ABC_TYPE_WRITE:
+ break;
+ }
+}
+
+SWITCH_DECLARE(switch_status_t) switch_ivr_stop_record_session(switch_core_session_t *session, char *file)
+{
+ switch_media_bug_t *bug;
+ switch_channel_t *channel = switch_core_session_get_channel(session);
+
+ assert(channel != NULL);
+ if ((bug = switch_channel_get_private(channel, file))) {
+ switch_channel_set_private(channel, file, NULL);
+ switch_core_media_bug_remove(session, &bug);
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ return SWITCH_STATUS_FALSE;
+
+}
+
+SWITCH_DECLARE(switch_status_t) switch_ivr_record_session(switch_core_session_t *session, char *file, switch_file_handle_t *fh)
+{
+ switch_channel_t *channel;
+ switch_codec_t *read_codec;
+ char *p;
+ const char *vval;
+ switch_media_bug_t *bug;
+ switch_status_t status;
+
+ if (!fh) {
+ if (!(fh = switch_core_session_alloc(session, sizeof(*fh)))) {
+ return SWITCH_STATUS_MEMERR;
+ }
+ }
+
+ channel = switch_core_session_get_channel(session);
+ assert(channel != NULL);
+
+ read_codec = switch_core_session_get_read_codec(session);
+ assert(read_codec != NULL);
+
+ fh->channels = read_codec->implementation->number_of_channels;
+ fh->samplerate = read_codec->implementation->samples_per_second;
+
+
+ if (switch_core_file_open(fh,
+ file,
+ SWITCH_FILE_FLAG_WRITE | SWITCH_FILE_DATA_SHORT,
+ switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
+ switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
+ switch_core_session_reset(session);
+ return SWITCH_STATUS_GENERR;
+ }
+
+ switch_channel_answer(channel);
+
+ if ((p = switch_channel_get_variable(channel, "RECORD_TITLE"))) {
+ vval = (const char *) switch_core_session_strdup(session, p);
+ switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_TITLE, vval);
+ switch_channel_set_variable(channel, "RECORD_TITLE", NULL);
+ }
+
+ if ((p = switch_channel_get_variable(channel, "RECORD_COPYRIGHT"))) {
+ vval = (const char *) switch_core_session_strdup(session, p);
+ switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_COPYRIGHT, vval);
+ switch_channel_set_variable(channel, "RECORD_COPYRIGHT", NULL);
+ }
+
+ if ((p = switch_channel_get_variable(channel, "RECORD_SOFTWARE"))) {
+ vval = (const char *) switch_core_session_strdup(session, p);
+ switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_SOFTWARE, vval);
+ switch_channel_set_variable(channel, "RECORD_SOFTWARE", NULL);
+ }
+
+ if ((p = switch_channel_get_variable(channel, "RECORD_ARTIST"))) {
+ vval = (const char *) switch_core_session_strdup(session, p);
+ switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_ARTIST, vval);
+ switch_channel_set_variable(channel, "RECORD_ARTIST", NULL);
+ }
+
+ if ((p = switch_channel_get_variable(channel, "RECORD_COMMENT"))) {
+ vval = (const char *) switch_core_session_strdup(session, p);
+ switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_COMMENT, vval);
+ switch_channel_set_variable(channel, "RECORD_COMMENT", NULL);
+ }
+
+ if ((p = switch_channel_get_variable(channel, "RECORD_DATE"))) {
+ vval = (const char *) switch_core_session_strdup(session, p);
+ switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_DATE, vval);
+ switch_channel_set_variable(channel, "RECORD_DATE", NULL);
+ }
+
+
+
+ if ((status = switch_core_media_bug_add(session,
+ record_callback,
+ fh,
+ &bug)) != SWITCH_STATUS_SUCCESS) {
+ switch_core_file_close(fh);
+ return status;
+ }
+
+ switch_channel_set_private(channel, file, bug);
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *session,
switch_file_handle_t *fh,
char *file,