SWITCH_DECLARE(switch_size_t) switch_buffer_slide_write(switch_buffer_t *buffer, const void *data, switch_size_t datalen) { switch_size_t w; if (!(w = switch_buffer_write(buffer, data, datalen))) { switch_buffer_toss(buffer, datalen); return switch_buffer_write(buffer, data, datalen); } return w; }
void *SWITCH_THREAD_FUNC rtmp_io_tcp_thread(switch_thread_t *thread, void *obj) { rtmp_io_tcp_t *io = (rtmp_io_tcp_t*)obj; io->base.running = 1; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s: I/O Thread starting\n", io->base.profile->name); while(io->base.running) { const switch_pollfd_t *fds; int32_t numfds; int32_t i; switch_status_t status; switch_mutex_lock(io->mutex); status = switch_pollset_poll(io->pollset, 500000, &numfds, &fds); switch_mutex_unlock(io->mutex); if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_TIMEOUT) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "pollset_poll failed\n"); continue; } else if (status == SWITCH_STATUS_TIMEOUT) { switch_cond_next(); } for (i = 0; i < numfds; i++) { if (!fds[i].client_data) { switch_socket_t *newsocket; if (switch_socket_accept(&newsocket, io->listen_socket, io->base.pool) != SWITCH_STATUS_SUCCESS) { if (io->base.running) { /* Don't spam the logs if we are shutting down */ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Error [%s]\n", strerror(errno)); } else { return NULL; } } else { rtmp_session_t *rsession; if (switch_socket_opt_set(newsocket, SWITCH_SO_NONBLOCK, TRUE)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't set socket as non-blocking\n"); } if (switch_socket_opt_set(newsocket, SWITCH_SO_TCP_NODELAY, 1)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Couldn't disable Nagle.\n"); } if (rtmp_session_request(io->base.profile, &rsession) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "RTMP session request failed\n"); switch_socket_close(newsocket); } else { switch_sockaddr_t *addr = NULL; char ipbuf[200]; /* Create out private data and attach it to the rtmp session structure */ rtmp_tcp_io_private_t *pvt = switch_core_alloc(rsession->pool, sizeof(*pvt)); rsession->io_private = pvt; pvt->socket = newsocket; switch_socket_create_pollfd(&pvt->pollfd, newsocket, SWITCH_POLLIN | SWITCH_POLLERR, rsession, rsession->pool); switch_pollset_add(io->pollset, pvt->pollfd); switch_buffer_create_dynamic(&pvt->sendq, 512, 1024, 0); /* Get the remote address/port info */ switch_socket_addr_get(&addr, SWITCH_TRUE, newsocket); switch_get_addr(ipbuf, sizeof(ipbuf), addr); rsession->remote_address = switch_core_strdup(rsession->pool, ipbuf); rsession->remote_port = switch_sockaddr_get_port(addr); switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_INFO, "Rtmp connection from %s:%i\n", rsession->remote_address, rsession->remote_port); } } } else { rtmp_session_t *rsession = (rtmp_session_t*)fds[i].client_data; rtmp_tcp_io_private_t *io_pvt = (rtmp_tcp_io_private_t*)rsession->io_private; if (fds[i].rtnevents & SWITCH_POLLOUT && switch_buffer_inuse(io_pvt->sendq) > 0) { /* Send as much remaining data as possible */ switch_size_t sendlen; const void *ptr; sendlen = switch_buffer_peek_zerocopy(io_pvt->sendq, &ptr); switch_socket_send_nonblock(io_pvt->socket, ptr, &sendlen); switch_buffer_toss(io_pvt->sendq, sendlen); if (switch_buffer_inuse(io_pvt->sendq) == 0) { /* Remove our fd from OUT polling */ rtmp_tcp_alter_pollfd(rsession, SWITCH_FALSE); } } else if (fds[i].rtnevents & SWITCH_POLLIN && rtmp_handle_data(rsession) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_UUID_LOG(rsession->uuid), SWITCH_LOG_DEBUG, "Closing socket\n"); switch_mutex_lock(io->mutex); switch_pollset_remove(io->pollset, io_pvt->pollfd); switch_mutex_unlock(io->mutex); switch_socket_close(io_pvt->socket); io_pvt->socket = NULL; rtmp_session_destroy(&rsession); } } } } io->base.running = -1; switch_socket_close(io->listen_socket); return NULL; }
SWITCH_DECLARE(switch_status_t) switch_core_media_bug_read(switch_media_bug_t *bug, switch_frame_t *frame, switch_bool_t fill) { switch_size_t bytes = 0, datalen = 0; int16_t *dp, *fp; uint32_t x; size_t rlen = 0; size_t wlen = 0; uint32_t blen; switch_codec_implementation_t read_impl = { 0 }; int16_t *tp; switch_size_t do_read = 0, do_write = 0, has_read = 0, has_write = 0, fill_read = 0, fill_write = 0; switch_core_session_get_read_impl(bug->session, &read_impl); bytes = read_impl.decoded_bytes_per_packet; if (frame->buflen < bytes) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_ERROR, "%s frame buffer too small!\n", switch_channel_get_name(bug->session->channel)); return SWITCH_STATUS_FALSE; } if ((!bug->raw_read_buffer && (!bug->raw_write_buffer || !switch_test_flag(bug, SMBF_WRITE_STREAM)))) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_ERROR, "%s Buffer Error (raw_read_buffer=%p, raw_write_buffer=%p, read=%s, write=%s)\n", switch_channel_get_name(bug->session->channel), (void *)bug->raw_read_buffer, (void *)bug->raw_write_buffer, switch_test_flag(bug, SMBF_READ_STREAM) ? "yes" : "no", switch_test_flag(bug, SMBF_WRITE_STREAM) ? "yes" : "no"); return SWITCH_STATUS_FALSE; } frame->flags = 0; frame->datalen = 0; if (switch_test_flag(bug, SMBF_READ_STREAM)) { has_read = 1; switch_mutex_lock(bug->read_mutex); do_read = switch_buffer_inuse(bug->raw_read_buffer); switch_mutex_unlock(bug->read_mutex); } if (switch_test_flag(bug, SMBF_WRITE_STREAM)) { has_write = 1; switch_mutex_lock(bug->write_mutex); do_write = switch_buffer_inuse(bug->raw_write_buffer); switch_mutex_unlock(bug->write_mutex); } if (bug->record_frame_size && bug->record_pre_buffer_max && (do_read || do_write) && bug->record_pre_buffer_count < bug->record_pre_buffer_max) { bug->record_pre_buffer_count++; return SWITCH_STATUS_FALSE; } else { uint32_t frame_size; switch_codec_implementation_t read_impl = { 0 }; switch_core_session_get_read_impl(bug->session, &read_impl); frame_size = read_impl.decoded_bytes_per_packet; bug->record_frame_size = frame_size; } if (bug->record_frame_size && do_write > do_read && do_write > (bug->record_frame_size * 2)) { switch_mutex_lock(bug->write_mutex); switch_buffer_toss(bug->raw_write_buffer, bug->record_frame_size); do_write = switch_buffer_inuse(bug->raw_write_buffer); switch_mutex_unlock(bug->write_mutex); } if ((has_read && !do_read)) { fill_read = 1; } if ((has_write && !do_write)) { fill_write = 1; } if (bug->record_frame_size) { if ((do_read && do_read < bug->record_frame_size) || (do_write && do_write < bug->record_frame_size)) { return SWITCH_STATUS_FALSE; } if (do_read && do_read > bug->record_frame_size) { do_read = bug->record_frame_size; } if (do_write && do_write > bug->record_frame_size) { do_write = bug->record_frame_size; } } if ((fill_read && fill_write) || (fill && (fill_read || fill_write))) { return SWITCH_STATUS_FALSE; } if (do_read && do_read > SWITCH_RECOMMENDED_BUFFER_SIZE) { do_read = 1280; } if (do_write && do_write > SWITCH_RECOMMENDED_BUFFER_SIZE) { do_write = 1280; } if (do_read) { switch_mutex_lock(bug->read_mutex); frame->datalen = (uint32_t) switch_buffer_read(bug->raw_read_buffer, frame->data, do_read); if (frame->datalen != do_read) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_ERROR, "Framing Error Reading!\n"); switch_core_media_bug_flush(bug); switch_mutex_unlock(bug->read_mutex); return SWITCH_STATUS_FALSE; } switch_mutex_unlock(bug->read_mutex); } else if (fill_read) { frame->datalen = (uint32_t)bytes; memset(frame->data, 255, frame->datalen); } if (do_write) { switch_assert(bug->raw_write_buffer); switch_mutex_lock(bug->write_mutex); datalen = (uint32_t) switch_buffer_read(bug->raw_write_buffer, bug->data, do_write); if (datalen != do_write) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_ERROR, "Framing Error Writing!\n"); switch_core_media_bug_flush(bug); switch_mutex_unlock(bug->write_mutex); return SWITCH_STATUS_FALSE; } switch_mutex_unlock(bug->write_mutex); } else if (fill_write) { datalen = bytes; memset(bug->data, 255, datalen); } tp = bug->tmp; dp = (int16_t *) bug->data; fp = (int16_t *) frame->data; rlen = frame->datalen / 2; wlen = datalen / 2; blen = (uint32_t)(bytes / 2); if (switch_test_flag(bug, SMBF_STEREO)) { int16_t *left, *right; size_t left_len, right_len; if (switch_test_flag(bug, SMBF_STEREO_SWAP)) { left = dp; /* write stream */ left_len = wlen; right = fp; /* read stream */ right_len = rlen; } else { left = fp; /* read stream */ left_len = rlen; right = dp; /* write stream */ right_len = wlen; } for (x = 0; x < blen; x++) { if (x < left_len) { *(tp++) = *(left + x); } else { *(tp++) = 0; } if (x < right_len) { *(tp++) = *(right + x); } else { *(tp++) = 0; } } memcpy(frame->data, bug->tmp, bytes * 2); } else { for (x = 0; x < blen; x++) { int32_t w = 0, r = 0, z = 0; if (x < rlen) { r = (int32_t) * (fp + x); } if (x < wlen) { w = (int32_t) * (dp + x); } z = w + r; if (z > SWITCH_SMAX || z < SWITCH_SMIN) { if (r) z += (r/2); if (w) z += (w/2); } switch_normalize_to_16bit(z); *(fp + x) = (int16_t) z; } } frame->datalen = (uint32_t)bytes; frame->samples = (uint32_t)(bytes / sizeof(int16_t) / read_impl.number_of_channels); frame->rate = read_impl.actual_samples_per_second; frame->codec = NULL; if (switch_test_flag(bug, SMBF_STEREO)) { frame->datalen *= 2; frame->channels = 2; } else { frame->channels = read_impl.number_of_channels; } return SWITCH_STATUS_SUCCESS; }
/* marshall frames from the call leg to the conference thread for muxing to other call legs */ void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, void *obj) { switch_event_t *event; conference_member_t *member = obj; switch_channel_t *channel; switch_status_t status; switch_frame_t *read_frame = NULL; uint32_t hangover = 40, hangunder = 5, hangover_hits = 0, hangunder_hits = 0, diff_level = 400; switch_core_session_t *session = member->session; uint32_t flush_len; switch_frame_t tmp_frame = { 0 }; if (switch_core_session_read_lock(session) != SWITCH_STATUS_SUCCESS) { goto end; } switch_assert(member != NULL); conference_utils_member_clear_flag_locked(member, MFLAG_TALKING); channel = switch_core_session_get_channel(session); switch_core_session_get_read_impl(session, &member->read_impl); switch_channel_audio_sync(channel); flush_len = switch_samples_per_packet(member->conference->rate, member->conference->interval) * 2 * member->conference->channels * (500 / member->conference->interval); /* As long as we have a valid read, feed that data into an input buffer where the conference thread will take it and mux it with any audio from other channels. */ while (conference_utils_member_test_flag(member, MFLAG_RUNNING) && switch_channel_ready(channel)) { if (switch_channel_ready(channel) && switch_channel_test_app_flag(channel, CF_APP_TAGGED)) { switch_yield(100000); continue; } /* Read a frame. */ status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0); switch_mutex_lock(member->read_mutex); /* end the loop, if appropriate */ if (!SWITCH_READ_ACCEPTABLE(status) || !conference_utils_member_test_flag(member, MFLAG_RUNNING)) { switch_mutex_unlock(member->read_mutex); break; } if (switch_channel_test_flag(channel, CF_VIDEO) && !conference_utils_member_test_flag(member, MFLAG_ACK_VIDEO)) { conference_utils_member_set_flag_locked(member, MFLAG_ACK_VIDEO); conference_video_check_avatar(member, SWITCH_FALSE); switch_core_session_video_reinit(member->session); conference_video_set_floor_holder(member->conference, member, SWITCH_FALSE); } else if (conference_utils_member_test_flag(member, MFLAG_ACK_VIDEO) && !switch_channel_test_flag(channel, CF_VIDEO)) { conference_video_check_avatar(member, SWITCH_FALSE); } /* if we have caller digits, feed them to the parser to find an action */ if (switch_channel_has_dtmf(channel)) { char dtmf[128] = ""; switch_channel_dequeue_dtmf_string(channel, dtmf, sizeof(dtmf)); if (conference_utils_member_test_flag(member, MFLAG_DIST_DTMF)) { conference_member_send_all_dtmf(member, member->conference, dtmf); } else if (member->dmachine) { char *p; char str[2] = ""; for (p = dtmf; p && *p; p++) { str[0] = *p; switch_ivr_dmachine_feed(member->dmachine, str, NULL); } } } else if (member->dmachine) { switch_ivr_dmachine_ping(member->dmachine, NULL); } if (switch_queue_size(member->dtmf_queue)) { switch_dtmf_t *dt; void *pop; if (switch_queue_trypop(member->dtmf_queue, &pop) == SWITCH_STATUS_SUCCESS) { dt = (switch_dtmf_t *) pop; switch_core_session_send_dtmf(member->session, dt); free(dt); } } if (switch_channel_test_flag(member->channel, CF_CONFERENCE_RESET_MEDIA)) { member->reset_media = 10; switch_channel_audio_sync(member->channel); switch_channel_clear_flag(member->channel, CF_CONFERENCE_RESET_MEDIA); } if (member->reset_media) { if (--member->reset_media > 0) { goto do_continue; } if (conference_member_setup_media(member, member->conference)) { switch_mutex_unlock(member->read_mutex); break; } member->loop_loop = 1; goto do_continue; } if (switch_test_flag(read_frame, SFF_CNG)) { if (member->conference->agc_level) { member->nt_tally++; } if (hangunder_hits) { hangunder_hits--; } if (conference_utils_member_test_flag(member, MFLAG_TALKING)) { if (++hangover_hits >= hangover) { hangover_hits = hangunder_hits = 0; conference_utils_member_clear_flag_locked(member, MFLAG_TALKING); conference_member_update_status_field(member); conference_member_check_agc_levels(member); conference_member_clear_avg(member); member->score_iir = 0; member->floor_packets = 0; if (test_eflag(member->conference, EFLAG_STOP_TALKING) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) { conference_member_add_event_data(member, event); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "stop-talking"); switch_event_fire(&event); } } } goto do_continue; } if (member->nt_tally > (int32_t)(member->read_impl.actual_samples_per_second / member->read_impl.samples_per_packet) * 3) { member->agc_volume_in_level = 0; conference_member_clear_avg(member); } /* Check for input volume adjustments */ if (!member->conference->agc_level) { member->conference->agc_level = 0; conference_member_clear_avg(member); } /* if the member can speak, compute the audio energy level and */ /* generate events when the level crosses the threshold */ if ((conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK) || conference_utils_member_test_flag(member, MFLAG_MUTE_DETECT))) { uint32_t energy = 0, i = 0, samples = 0, j = 0; int16_t *data; int agc_period = (member->read_impl.actual_samples_per_second / member->read_impl.samples_per_packet) / 4; data = read_frame->data; member->score = 0; if (member->volume_in_level) { switch_change_sln_volume(read_frame->data, (read_frame->datalen / 2) * member->conference->channels, member->volume_in_level); } if (member->agc_volume_in_level) { switch_change_sln_volume_granular(read_frame->data, (read_frame->datalen / 2) * member->conference->channels, member->agc_volume_in_level); } if ((samples = read_frame->datalen / sizeof(*data) / member->read_impl.number_of_channels)) { for (i = 0; i < samples; i++) { energy += abs(data[j]); j += member->read_impl.number_of_channels; } member->score = energy / samples; } if (member->vol_period) { member->vol_period--; } if (member->conference->agc_level && member->score && conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK) && conference_member_noise_gate_check(member) ) { int last_shift = abs((int)(member->last_score - member->score)); if (member->score && member->last_score && last_shift > 900) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG7, "AGC %s:%d drop anomalous shift of %d\n", member->conference->name, member->id, last_shift); } else { member->avg_tally += member->score; member->avg_itt++; if (!member->avg_itt) member->avg_itt++; member->avg_score = member->avg_tally / member->avg_itt; } switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG7, "AGC %s:%d diff:%d level:%d cur:%d avg:%d vol:%d\n", member->conference->name, member->id, member->conference->agc_level - member->avg_score, member->conference->agc_level, member->score, member->avg_score, member->agc_volume_in_level); if (++member->agc_concur >= agc_period) { if (!member->vol_period) { conference_member_check_agc_levels(member); } member->agc_concur = 0; } } else { member->nt_tally++; } member->score_iir = (int) (((1.0 - SCORE_DECAY) * (float) member->score) + (SCORE_DECAY * (float) member->score_iir)); if (member->score_iir > SCORE_MAX_IIR) { member->score_iir = SCORE_MAX_IIR; } if (conference_member_noise_gate_check(member)) { uint32_t diff = member->score - member->energy_level; if (hangover_hits) { hangover_hits--; } if (member->conference->agc_level) { member->nt_tally = 0; } if (member == member->conference->floor_holder) { member->floor_packets++; } if (diff >= diff_level || ++hangunder_hits >= hangunder) { hangover_hits = hangunder_hits = 0; member->last_talking = switch_epoch_time_now(NULL); if (!conference_utils_member_test_flag(member, MFLAG_TALKING)) { conference_utils_member_set_flag_locked(member, MFLAG_TALKING); conference_member_update_status_field(member); member->floor_packets = 0; if (test_eflag(member->conference, EFLAG_START_TALKING) && conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) { conference_member_add_event_data(member, event); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "start-talking"); switch_event_fire(&event); } if (conference_utils_member_test_flag(member, MFLAG_MUTE_DETECT) && !conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK)) { if (!zstr(member->conference->mute_detect_sound)) { conference_utils_member_set_flag(member, MFLAG_INDICATE_MUTE_DETECT); } if (test_eflag(member->conference, EFLAG_MUTE_DETECT) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) { conference_member_add_event_data(member, event); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "mute-detect"); switch_event_fire(&event); } } } } } else { if (hangunder_hits) { hangunder_hits--; } if (member->conference->agc_level) { member->nt_tally++; } if (conference_utils_member_test_flag(member, MFLAG_TALKING) && conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK)) { switch_event_t *event; if (++hangover_hits >= hangover) { hangover_hits = hangunder_hits = 0; conference_utils_member_clear_flag_locked(member, MFLAG_TALKING); conference_member_update_status_field(member); conference_member_check_agc_levels(member); conference_member_clear_avg(member); if (test_eflag(member->conference, EFLAG_STOP_TALKING) && switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT) == SWITCH_STATUS_SUCCESS) { conference_member_add_event_data(member, event); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Action", "stop-talking"); switch_event_fire(&event); } } } } member->last_score = member->score; if (member == member->conference->floor_holder) { if (member->id != member->conference->video_floor_holder && (member->floor_packets > member->conference->video_floor_packets || member->energy_level == 0)) { conference_video_set_floor_holder(member->conference, member, SWITCH_FALSE); } } } /* skip frames that are not actual media or when we are muted or silent */ if ((conference_utils_member_test_flag(member, MFLAG_TALKING) || member->energy_level == 0 || conference_utils_test_flag(member->conference, CFLAG_AUDIO_ALWAYS)) && conference_utils_member_test_flag(member, MFLAG_CAN_SPEAK) && !conference_utils_test_flag(member->conference, CFLAG_WAIT_MOD) && (member->conference->count > 1 || (member->conference->record_count && member->conference->count >= member->conference->min_recording_participants))) { switch_audio_resampler_t *read_resampler = member->read_resampler; void *data; uint32_t datalen; if (read_resampler) { int16_t *bptr = (int16_t *) read_frame->data; int len = (int) read_frame->datalen; switch_resample_process(read_resampler, bptr, len / 2 / member->read_impl.number_of_channels); memcpy(member->resample_out, read_resampler->to, read_resampler->to_len * 2 * member->read_impl.number_of_channels); len = read_resampler->to_len * 2 * member->read_impl.number_of_channels; datalen = len; data = member->resample_out; } else { data = read_frame->data; datalen = read_frame->datalen; } tmp_frame.data = data; tmp_frame.datalen = datalen; tmp_frame.rate = member->conference->rate; conference_member_check_channels(&tmp_frame, member, SWITCH_TRUE); if (datalen) { switch_size_t ok = 1; /* Write the audio into the input buffer */ switch_mutex_lock(member->audio_in_mutex); if (switch_buffer_inuse(member->audio_buffer) > flush_len) { switch_buffer_toss(member->audio_buffer, tmp_frame.datalen); } ok = switch_buffer_write(member->audio_buffer, tmp_frame.data, tmp_frame.datalen); switch_mutex_unlock(member->audio_in_mutex); if (!ok) { switch_mutex_unlock(member->read_mutex); break; } } } do_continue: switch_mutex_unlock(member->read_mutex); } if (switch_queue_size(member->dtmf_queue)) { switch_dtmf_t *dt; void *pop; while (switch_queue_trypop(member->dtmf_queue, &pop) == SWITCH_STATUS_SUCCESS) { dt = (switch_dtmf_t *) pop; free(dt); } } switch_resample_destroy(&member->read_resampler); switch_core_session_rwunlock(session); end: conference_utils_member_clear_flag_locked(member, MFLAG_ITHREAD); return NULL; }