struct record_helper {
+ switch_media_bug_t *bug;
+ switch_memory_pool_t *helper_pool;
+ switch_core_session_t *recording_session;
+ switch_core_session_t *transfer_from_session;
+ uint8_t transfer_complete;
char *file;
switch_file_handle_t *fh;
switch_file_handle_t in_fh;
switch_thread_t *thread;
switch_mutex_t *buffer_mutex;
int thread_ready;
+ uint8_t thread_needs_transfer;
uint32_t writes;
uint32_t vwrites;
const char *completion_cause;
switch_event_t *variables;
};
+static switch_status_t record_helper_destroy(struct record_helper **rh, switch_core_session_t *session);
+
/**
* Set the recording completion cause. The cause can only be set once, to minimize the logic in the record_callback.
* [The completion_cause strings are essentially those of an MRCP Recorder resource.]
rh->thread_ready = 1;
channels = switch_core_media_bug_test_flag(bug, SMBF_STEREO) ? 2 : rh->read_impl.number_of_channels;
- data = switch_core_session_alloc(session, SWITCH_RECOMMENDED_BUFFER_SIZE);
+ data = switch_core_alloc(rh->helper_pool, SWITCH_RECOMMENDED_BUFFER_SIZE);
while(switch_test_flag(rh->fh, SWITCH_FILE_OPEN)) {
+ if (rh->thread_needs_transfer) {
+ assert(session != rh->recording_session);
+
+ if (switch_core_session_read_lock(rh->recording_session) != SWITCH_STATUS_SUCCESS) {
+ continue;
+ }
+
+ switch_core_session_rwunlock(session);
+ session = rh->recording_session;
+ channel = switch_core_session_get_channel(session);
+ bug = rh->bug;
+ channels = switch_core_media_bug_test_flag(bug, SMBF_STEREO) ? 2 : rh->read_impl.number_of_channels;
+
+ /* Tell record_callback that we transferred */
+ rh->thread_needs_transfer = 0;
+
+ /* Erasing the obj variable for safety because we transferred (we are under another bug) */
+ obj = NULL;
+ }
+
if (switch_core_file_has_video(rh->fh, SWITCH_TRUE)) {
switch_core_session_get_read_impl(session, &read_impl);
if (read_impl.decoded_bytes_per_packet > 0 && read_impl.decoded_bytes_per_packet <= SWITCH_RECOMMENDED_BUFFER_SIZE) {
int mask = switch_core_media_bug_test_flag(bug, SMBF_MASK);
unsigned char null_data[SWITCH_RECOMMENDED_BUFFER_SIZE] = {0};
+ /* Check if the recording was transferred (see recording_follow_transfer) */
+ if (rh->recording_session != session) {
+ return SWITCH_FALSE;
+ }
+
switch (type) {
case SWITCH_ABC_TYPE_INIT:
{
const char *var = switch_channel_get_variable(channel, "RECORD_USE_THREAD");
+ switch_core_session_get_read_impl(session, &rh->read_impl);
+
+ /* Check if recording is transferred from another session */
+ if (rh->transfer_from_session && rh->transfer_from_session != rh->recording_session) {
+
+ /* If there is a thread, we need to re-initiate it */
+ if (rh->thread_ready) {
+ switch_media_bug_t *oldbug = rh->bug;
+ int sanity = 200;
+
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Re-initiating recording thread for file %s\n", rh->file);
+
+ rh->bug = bug;
+ rh->thread_needs_transfer = 1;
+
+ while (--sanity > 0 && rh->thread_needs_transfer) {
+ switch_yield(10000);
+ }
+
+ /* Check if the thread reacted on the transfer */
+ if (rh->thread_needs_transfer) {
+ /* Thread did not react, assuming it is cond_wait'ing */
+ rh->bug = oldbug;
+ switch_core_session_get_read_impl(rh->transfer_from_session, &rh->read_impl);
+ rh->thread_needs_transfer = 0;
+
+ return SWITCH_FALSE;
+ }
+ }
+
+ if (rh->fh) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Record session sample rate: %d -> %d\n", rh->fh->native_rate, rh->fh->samplerate);
+ rh->fh->native_rate = rh->read_impl.actual_samples_per_second;
+ if (switch_core_file_has_video(rh->fh, SWITCH_TRUE)) {
+ switch_core_media_bug_set_media_params(bug, &rh->fh->mm);
+ }
+ }
+
+ rh->transfer_from_session = NULL;
+ rh->transfer_complete = 1;
+
+ break;
+ }
+
+ /* Required for potential record_transfer */
+ rh->bug = bug;
+
if (!rh->native && rh->fh && (zstr(var) || switch_true(var))) {
switch_threadattr_t *thd_attr = NULL;
- switch_memory_pool_t *pool = switch_core_session_get_pool(session);
int sanity = 200;
-
- switch_core_session_get_read_impl(session, &rh->read_impl);
- switch_mutex_init(&rh->buffer_mutex, SWITCH_MUTEX_NESTED, pool);
- switch_threadattr_create(&thd_attr, pool);
+ switch_mutex_init(&rh->buffer_mutex, SWITCH_MUTEX_NESTED, rh->helper_pool);
+ switch_threadattr_create(&thd_attr, rh->helper_pool);
switch_threadattr_stacksize_set(thd_attr, SWITCH_THREAD_STACKSIZE);
- switch_thread_create(&rh->thread, thd_attr, recording_thread, bug, pool);
+ switch_thread_create(&rh->thread, thd_attr, recording_thread, bug, rh->helper_pool);
while(--sanity > 0 && !rh->thread_ready) {
switch_yield(10000);
rh->speech_detected = SWITCH_FALSE;
rh->completion_cause = NULL;
- switch_core_session_get_read_impl(session, &rh->read_impl);
if (rh->fh) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Record session sample rate: %d -> %d\n", rh->fh->native_rate, rh->fh->samplerate);
rh->fh->native_rate = rh->read_impl.actual_samples_per_second;
switch_core_media_bug_set_media_params(bug, &rh->fh->mm);
}
}
-
}
break;
case SWITCH_ABC_TYPE_TAP_NATIVE_READ:
switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE);
}
send_record_stop_event(channel, &read_impl, rh);
+ record_helper_destroy(&rh, session);
+
return SWITCH_FALSE;
}
}
}
-
+ record_helper_destroy(&rh, session);
}
break;
static void* switch_ivr_record_user_data_dup(switch_core_session_t *session, void *user_data)
{
- struct record_helper *rh = (struct record_helper *) user_data, *dup = NULL;
-
- dup = switch_core_session_alloc(session, sizeof(*dup));
- memcpy(dup, rh, sizeof(*rh));
- dup->file = switch_core_session_strdup(session, rh->file);
- dup->fh = switch_core_session_alloc(session, sizeof(switch_file_handle_t));
- memcpy(dup->fh, rh->fh, sizeof(switch_file_handle_t));
- dup->variables = NULL;
- if (rh->variables) {
- switch_event_dup(&dup->variables, rh->variables);
- switch_event_safe_destroy(rh->variables);
+ struct record_helper *rh = (struct record_helper *) user_data;
+
+ if (!rh->transfer_complete && session == rh->transfer_from_session) {
+ /* Transfer failed and now we are called to put the original session back */
+ rh->recording_session = rh->transfer_from_session;
+ rh->transfer_from_session = NULL;
+ } else {
+ rh->transfer_from_session = rh->recording_session;
+ rh->recording_session = session;
+ rh->transfer_complete = 0;
}
- return dup;
+ return rh;
}
SWITCH_DECLARE(switch_status_t) switch_ivr_transfer_recordings(switch_core_session_t *orig_session, switch_core_session_t *new_session)
return status;
}
+static switch_status_t record_helper_create(struct record_helper **rh, switch_core_session_t *session)
+{
+ switch_status_t status;
+ switch_memory_pool_t *pool;
+ struct record_helper *newrh;
+
+ assert(rh);
+ assert(session);
+
+ if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) {
+ return status;
+ }
+
+ if (!(newrh = switch_core_alloc(pool, sizeof(*newrh)))) {
+ switch_core_destroy_memory_pool(&pool);
+ return SWITCH_STATUS_MEMERR;
+ }
+
+ newrh->helper_pool = pool;
+ newrh->recording_session = session;
+
+ *rh = newrh;
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
+static switch_status_t record_helper_destroy(struct record_helper **rh, switch_core_session_t *session)
+{
+ switch_memory_pool_t *pool;
+
+ assert(rh);
+ assert(*rh);
+ assert(session);
+
+ if ((*rh)->recording_session != session) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Destroying a record helper of another session!\n");
+ }
+
+ if ((*rh)->native) {
+ switch_core_file_close(&(*rh)->in_fh);
+ switch_core_file_close(&(*rh)->out_fh);
+ } else if((*rh)->fh) {
+ switch_core_file_close((*rh)->fh);
+ }
+
+ pool = (*rh)->helper_pool;
+ switch_core_destroy_memory_pool(&pool);
+ *rh = NULL;
+
+ return SWITCH_STATUS_SUCCESS;
+}
+
SWITCH_DECLARE(switch_status_t) switch_ivr_record_session_event(switch_core_session_t *session, const char *file, uint32_t limit, switch_file_handle_t *fh, switch_event_t *vars)
{
switch_channel_t *channel = switch_core_session_get_channel(session);
channels = read_impl.number_of_channels;
if ((bug = switch_channel_get_private(channel, file))) {
- if (switch_true(switch_channel_get_variable(channel, "RECORD_TOGGLE_ON_REPEAT"))) {
+ if (switch_channel_var_true(channel, "RECORD_TOGGLE_ON_REPEAT")) {
return switch_ivr_stop_record_session(session, file);
}
}
- if ((p = switch_channel_get_variable(channel, "RECORD_CHECK_BRIDGE")) && switch_true(p)) {
+ if (switch_channel_var_true(channel, "RECORD_CHECK_BRIDGE")) {
switch_core_session_t *other_session;
int exist = 0;
switch_status_t rstatus = SWITCH_STATUS_SUCCESS;
}
}
- if (!fh) {
- if (!(fh = switch_core_session_alloc(session, sizeof(*fh)))) {
- return SWITCH_STATUS_MEMERR;
+ if ((status = record_helper_create(&rh, session)) != SWITCH_STATUS_SUCCESS) {
+ return status;
+ }
+
+ if (fh) {
+ switch_file_handle_t *newfh;
+
+ if ((status = switch_core_file_handle_dup(fh, &newfh, rh->helper_pool)) != SWITCH_STATUS_SUCCESS) {
+ switch_goto_status(status, err);
+ }
+
+ fh = newfh;
+ } else {
+ if (!(fh = switch_core_alloc(rh->helper_pool, sizeof(*fh)))) {
+ switch_goto_status(SWITCH_STATUS_MEMERR, err);
}
}
- if ((p = switch_channel_get_variable(channel, "RECORD_WRITE_ONLY")) && switch_true(p)) {
+ if (switch_channel_var_true(channel, "RECORD_WRITE_ONLY")) {
flags &= ~SMBF_READ_STREAM;
flags |= SMBF_WRITE_STREAM;
}
- if ((p = switch_channel_get_variable(channel, "RECORD_READ_ONLY")) && switch_true(p)) {
+ if (switch_channel_var_true(channel, "RECORD_READ_ONLY")) {
flags &= ~SMBF_WRITE_STREAM;
flags |= SMBF_READ_STREAM;
}
if (channels == 1) { /* if leg is already stereo this feature is not available */
- if ((p = switch_channel_get_variable(channel, "RECORD_STEREO")) && switch_true(p)) {
+ if (switch_channel_var_true(channel, "RECORD_STEREO")) {
flags |= SMBF_STEREO;
flags &= ~SMBF_STEREO_SWAP;
channels = 2;
}
- if ((p = switch_channel_get_variable(channel, "RECORD_STEREO_SWAP")) && switch_true(p)) {
+ if (switch_channel_var_true(channel, "RECORD_STEREO_SWAP")) {
flags |= SMBF_STEREO;
flags |= SMBF_STEREO_SWAP;
channels = 2;
}
}
- if ((p = switch_channel_get_variable(channel, "RECORD_ANSWER_REQ")) && switch_true(p)) {
+ if (switch_channel_var_true(channel, "RECORD_ANSWER_REQ")) {
flags |= SMBF_ANSWER_REQ;
}
- if ((p = switch_channel_get_variable(channel, "RECORD_BRIDGE_REQ")) && switch_true(p)) {
+ if (switch_channel_var_true(channel, "RECORD_BRIDGE_REQ")) {
flags |= SMBF_BRIDGE_REQ;
}
- if ((p = switch_channel_get_variable(channel, "RECORD_APPEND")) && switch_true(p)) {
+ if (switch_channel_var_true(channel, "RECORD_APPEND")) {
file_flags |= SWITCH_FILE_WRITE_APPEND;
}
fh->channels = channels;
-
-
if ((vval = switch_channel_get_variable(channel, "enable_file_write_buffering"))) {
int tmp = atoi(vval);
fh->pre_buffer_datalen = SWITCH_DEFAULT_FILE_BUFFER_LEN;
}
-
if (!switch_is_file_path(file)) {
char *tfile = NULL;
char *e;
}
if (*file == '[') {
- tfile = switch_core_session_strdup(session, file);
+ tfile = switch_core_strdup(rh->helper_pool, file);
if ((e = switch_find_end_paren(tfile, '[', ']'))) {
*e = '\0';
file = e + 1;
tfile = NULL;
}
} else {
- file_path = switch_core_session_sprintf(session, "%s%s%s", prefix, SWITCH_PATH_SEPARATOR, file);
+ file_path = switch_core_sprintf(rh->helper_pool, "%s%s%s", prefix, SWITCH_PATH_SEPARATOR, file);
}
- file = switch_core_session_sprintf(session, "%s%s%s%s%s", switch_str_nil(tfile), tfile ? "]" : "", prefix, SWITCH_PATH_SEPARATOR, file);
+ file = switch_core_sprintf(rh->helper_pool, "%s%s%s%s%s", switch_str_nil(tfile), tfile ? "]" : "", prefix, SWITCH_PATH_SEPARATOR, file);
} else {
- file_path = switch_core_session_strdup(session, file);
+ file_path = switch_core_strdup(rh->helper_pool, file);
}
if (file_path && !strstr(file_path, SWITCH_URL_SEPARATOR)) {
char *p;
- char *path = switch_core_session_strdup(session, file_path);
+ char *path = switch_core_strdup(rh->helper_pool, file_path);
if ((p = strrchr(path, *SWITCH_PATH_SEPARATOR))) {
*p = '\0';
if (switch_dir_make_recursive(path, SWITCH_DEFAULT_DIR_PERMS, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error creating %s\n", path);
- return SWITCH_STATUS_GENERR;
+ switch_goto_status(SWITCH_STATUS_GENERR, err);
}
} else {
}
}
- rh = switch_core_session_alloc(session, sizeof(*rh));
-
if ((ext = strrchr(file, '.'))) {
ext++;
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE);
}
- return SWITCH_STATUS_GENERR;
+ switch_goto_status(SWITCH_STATUS_GENERR, err);
}
if (fh->params) {
ext = read_impl.iananame;
- in_file = switch_core_session_sprintf(session, "%s-in.%s", file, ext);
- out_file = switch_core_session_sprintf(session, "%s-out.%s", file, ext);
+ in_file = switch_core_sprintf(rh->helper_pool, "%s-in.%s", file, ext);
+ out_file = switch_core_sprintf(rh->helper_pool, "%s-out.%s", file, ext);
rh->in_fh.pre_buffer_datalen = rh->out_fh.pre_buffer_datalen = fh->pre_buffer_datalen;
channels = 1;
switch_set_flag(&rh->in_fh, SWITCH_FILE_NATIVE);
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE);
}
- return SWITCH_STATUS_GENERR;
+ switch_goto_status(SWITCH_STATUS_GENERR, err);
}
if (switch_core_file_open(&rh->out_fh, out_file, channels, read_impl.actual_samples_per_second, file_flags, NULL) != SWITCH_STATUS_SUCCESS) {
switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);
switch_core_session_reset(session, SWITCH_TRUE, SWITCH_TRUE);
}
- return SWITCH_STATUS_GENERR;
+ switch_goto_status(SWITCH_STATUS_GENERR, err);
}
rh->native = 1;
flags = tflags;
}
-
-
if ((p = switch_channel_get_variable(channel, "RECORD_TITLE"))) {
- vval = (const char *) switch_core_session_strdup(session, p);
+ vval = (const char *) switch_core_strdup(rh->helper_pool, p);
if (fh) 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);
+ vval = (const char *) switch_core_strdup(rh->helper_pool, p);
if (fh) 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);
+ vval = (const char *) switch_core_strdup(rh->helper_pool, p);
if (fh) 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);
+ vval = (const char *) switch_core_strdup(rh->helper_pool, p);
if (fh) 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);
+ vval = (const char *) switch_core_strdup(rh->helper_pool, p);
if (fh) 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);
+ vval = (const char *) switch_core_strdup(rh->helper_pool, p);
if (fh) switch_core_file_set_string(fh, SWITCH_AUDIO_COL_STR_DATE, vval);
switch_channel_set_variable(channel, "RECORD_DATE", NULL);
}
}
rh->fh = fh;
- rh->file = switch_core_session_strdup(session, file);
+ rh->file = switch_core_strdup(rh->helper_pool, file);
rh->packet_len = read_impl.decoded_bytes_per_packet;
if (file_flags & SWITCH_FILE_WRITE_APPEND) {
}
}
- if(vars) {
+ if (vars) {
switch_event_dup(&rh->variables, vars);
}
if ((status = switch_core_media_bug_add(session, "session_record", file,
record_callback, rh, to, flags, &bug)) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error adding media bug for file %s\n", file);
- if (rh->native) {
- switch_core_file_close(&rh->in_fh);
- switch_core_file_close(&rh->out_fh);
- } else {
- switch_core_file_close(fh);
- }
- return status;
+ switch_goto_status(status, err);
}
if ((p = switch_channel_get_variable(channel, "RECORD_PRE_BUFFER_FRAMES"))) {
}
return SWITCH_STATUS_SUCCESS;
+
+err:
+ record_helper_destroy(&rh, session);
+
+ return status;
}
SWITCH_DECLARE(switch_status_t) switch_ivr_record_session(switch_core_session_t *session, const char *file, uint32_t limit, switch_file_handle_t *fh)