diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 2ab0a9b9f5..bb71b3a7e7 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -107,6 +107,50 @@ struct switch_core_port_allocator; \{ */ + +///\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 ///\{ @@ -119,7 +163,10 @@ struct switch_core_port_allocator; \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 diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 6e02752572..7d0ff8f226 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -94,7 +94,24 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_collect_digits_count(switch_core_sess 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 diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 9b4dd87bfd..1b7897db3b 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -80,6 +80,12 @@ typedef enum { 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; @@ -739,9 +745,8 @@ typedef struct switch_file_interface switch_file_interface_t; 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 *); @@ -787,7 +792,7 @@ struct switch_channel; /*! \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 diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index b008c62ad2..00cd5de496 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -50,6 +50,12 @@ SWITCH_BEGIN_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 diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index 2aa596a331..6d2eba8e37 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -228,10 +228,6 @@ static uint32_t next_member_id(void) } -#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; @@ -249,7 +245,7 @@ static void switch_change_sln_volume(int16_t *data, uint32_t samples, int32_t vo 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; } } @@ -600,7 +596,7 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v 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; } diff --git a/src/mod/applications/mod_playback/mod_playback.c b/src/mod/applications/mod_playback/mod_playback.c index cc167aae3a..50e963b023 100644 --- a/src/mod/applications/mod_playback/mod_playback.c +++ b/src/mod/applications/mod_playback/mod_playback.c @@ -133,6 +133,26 @@ static void record_function(switch_core_session_t *session, char *data) } +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 @@ -145,11 +165,27 @@ static const switch_application_interface_t record_application_interface = { &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 = { diff --git a/src/switch_core.c b/src/switch_core.c index 95ff1364bd..b389393155 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -50,15 +50,15 @@ #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 { @@ -101,6 +101,8 @@ 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; @@ -149,6 +151,175 @@ static void db_pick_path(char *dbname, char *buf, switch_size_t size) } } +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; @@ -158,7 +329,10 @@ struct switch_core_port_allocator { 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; @@ -1362,7 +1536,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi { 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; @@ -1420,6 +1594,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi 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; @@ -1472,6 +1651,24 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_read_frame(switch_core_sessi 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; @@ -1587,7 +1784,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess 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); @@ -1620,6 +1817,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess 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; @@ -1680,7 +1882,23 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess 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; @@ -1819,6 +2037,11 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_write_frame(switch_core_sess } } } else { + do_write = 1; + } + + done: + if (do_write) { return perform_write(session, frame, timeout, io_flag, stream_id); } @@ -2658,6 +2881,7 @@ SWITCH_DECLARE(void) switch_core_session_destroy(switch_core_session_t **session 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); @@ -2889,6 +3113,7 @@ SWITCH_DECLARE(switch_core_session_t *) switch_core_session_request(const switch 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); diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 89ab3ccc3c..52c62c4d91 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -448,6 +448,138 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_record_file(switch_core_session_t *se 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,