/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005-2011, Anthony Minessale II * * 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 * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Mathieu Rene * * rtp.c -- RTP Controllable Channel Module * */ #include #include "mod_sofia.h" #define kLOCALADDR "local_addr" #define kLOCALPORT "local_port" #define kREMOTEADDR "remote_addr" #define kREMOTEPORT "remote_port" #define kCODEC "codec" #define kPTIME "ptime" #define kPT "pt" #define kRFC2833PT "rfc2833_pt" #define kMODE "mode" #define kRATE "rate" static struct { switch_memory_pool_t *pool; switch_endpoint_interface_t *endpoint_interface; } crtp; typedef struct { switch_core_session_t *session; switch_channel_t *channel; switch_codec_t read_codec, write_codec; switch_frame_t read_frame; switch_rtp_bug_flag_t rtp_bugs; switch_rtp_t *rtp_session; uint32_t timestamp_send; const char *local_address; const char *remote_address; const char *codec; int ptime; const switch_codec_implementation_t *negotiated_codecs[SWITCH_MAX_CODECS]; int num_negotiated_codecs; char *origin; switch_port_t local_port; switch_port_t remote_port; switch_payload_t agreed_pt; /*XXX*/ switch_core_media_dtmf_t dtmf_type; enum { RTP_SENDONLY, RTP_RECVONLY, RTP_SENDRECV } mode; } crtp_private_t; static switch_status_t channel_on_init(switch_core_session_t *session); static switch_status_t channel_on_destroy(switch_core_session_t *session); static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event, switch_caller_profile_t *outbound_profile, switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags, switch_call_cause_t *cancel_cause); static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id); static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id); static switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg); static switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf); static switch_status_t channel_receive_event(switch_core_session_t *session, switch_event_t *event); switch_state_handler_table_t crtp_state_handlers = { /*on_init */channel_on_init, /*on_routing */ NULL, /*on_execute */ NULL, /*on_hangup*/ NULL, /*on_exchange_media*/ NULL, /*on_soft_execute*/ NULL, /*on_consume_media*/ NULL, /*on_hibernate*/ NULL, /*on_reset*/ NULL, /*on_park*/ NULL, /*on_reporting*/ NULL, /*on_destroy*/ channel_on_destroy }; switch_io_routines_t crtp_io_routines = { /*outgoing_channel*/ channel_outgoing_channel, /*read_frame*/ channel_read_frame, /*write_frame*/ channel_write_frame, /*kill_channel*/ NULL, /*send_dtmf*/ channel_send_dtmf, /*receive_message*/ channel_receive_message, /*receive_event*/ channel_receive_event, /*state_change*/ NULL, /*read_video_frame*/ NULL, /*write_video_frame*/ NULL, /*read_text_frame*/ NULL, /*write_text_frame*/ NULL, /*state_run*/ NULL }; void crtp_init(switch_loadable_module_interface_t *module_interface) { switch_endpoint_interface_t *endpoint_interface; //switch_api_interface_t *api_interface; crtp.pool = module_interface->pool; endpoint_interface = switch_loadable_module_create_interface(module_interface, SWITCH_ENDPOINT_INTERFACE); endpoint_interface->interface_name = "rtp"; endpoint_interface->io_routines = &crtp_io_routines; endpoint_interface->state_handler = &crtp_state_handlers; crtp.endpoint_interface = endpoint_interface; //SWITCH_ADD_API(api_interface, "rtp_test", "test", test_function, ""); } static switch_call_cause_t channel_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event, switch_caller_profile_t *outbound_profile, switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags, switch_call_cause_t *cancel_cause) { switch_channel_t *channel; char name[128]; crtp_private_t *tech_pvt = NULL; switch_caller_profile_t *caller_profile; switch_rtp_flag_t rtp_flags[SWITCH_RTP_FLAG_INVALID] = {0}; const char *err; const char *local_addr = switch_event_get_header_nil(var_event, kLOCALADDR), *szlocal_port = switch_event_get_header_nil(var_event, kLOCALPORT), *remote_addr = switch_event_get_header_nil(var_event, kREMOTEADDR), *szremote_port = switch_event_get_header_nil(var_event, kREMOTEPORT), *codec = switch_event_get_header_nil(var_event, kCODEC), *szptime = switch_event_get_header_nil(var_event, kPTIME), //*mode = switch_event_get_header_nil(var_event, kMODE), //*szrfc2833_pt = switch_event_get_header_nil(var_event, kRFC2833PT), *szrate = switch_event_get_header_nil(var_event, kRATE), *szpt = switch_event_get_header_nil(var_event, kPT); switch_port_t local_port = !zstr(szlocal_port) ? (switch_port_t)atoi(szlocal_port) : 0, remote_port = !zstr(szremote_port) ? (switch_port_t)atoi(szremote_port) : 0; int ptime = !zstr(szptime) ? atoi(szptime) : 0, //rfc2833_pt = !zstr(szrfc2833_pt) ? atoi(szrfc2833_pt) : 0, rate = !zstr(szrate) ? atoi(szrate) : 8000, pt = !zstr(szpt) ? atoi(szpt) : 0; if ( ((zstr(remote_addr) || remote_port == 0) && (zstr(local_addr) || local_port == 0)) || zstr(codec) || zstr(szpt)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing required arguments\n"); goto fail; } if (!(*new_session = switch_core_session_request(crtp.endpoint_interface, SWITCH_CALL_DIRECTION_OUTBOUND, 0, pool))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't request session.\n"); goto fail; } channel = switch_core_session_get_channel(*new_session); tech_pvt = switch_core_session_alloc(*new_session, sizeof *tech_pvt); tech_pvt->session = *new_session; tech_pvt->channel = channel; tech_pvt->local_address = switch_core_session_strdup(*new_session, local_addr); tech_pvt->local_port = local_port; tech_pvt->remote_address = switch_core_session_strdup(*new_session, remote_addr); tech_pvt->remote_port = remote_port; tech_pvt->ptime = ptime; tech_pvt->agreed_pt = (switch_payload_t)pt; tech_pvt->dtmf_type = DTMF_2833; /* XXX */ if (zstr(local_addr) || local_port == 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "The local address and port must be set\n"); goto fail; } else if (zstr(remote_addr) || remote_port == 0) { tech_pvt->mode = RTP_RECVONLY; } else { tech_pvt->mode = RTP_SENDRECV; } switch_core_session_set_private(*new_session, tech_pvt); caller_profile = switch_caller_profile_clone(*new_session, outbound_profile); switch_channel_set_caller_profile(channel, caller_profile); snprintf(name, sizeof(name), "rtp/%s", outbound_profile->destination_number); switch_channel_set_name(channel, name); switch_channel_set_state(channel, CS_INIT); if (switch_core_codec_init(&tech_pvt->read_codec, codec, NULL, NULL, rate, ptime, 1, /*SWITCH_CODEC_FLAG_ENCODE |*/ SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); goto fail; } else { if (switch_core_codec_init(&tech_pvt->write_codec, codec, NULL, NULL, rate, ptime, 1, SWITCH_CODEC_FLAG_ENCODE /*| SWITCH_CODEC_FLAG_DECODE*/, NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); goto fail; } } if (switch_core_session_set_read_codec(*new_session, &tech_pvt->read_codec) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set read codec?\n"); goto fail; } if (switch_core_session_set_write_codec(*new_session, &tech_pvt->write_codec) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set write codec?\n"); goto fail; } if (!(tech_pvt->rtp_session = switch_rtp_new(local_addr, local_port, remote_addr, remote_port, tech_pvt->agreed_pt, tech_pvt->read_codec.implementation->samples_per_packet, ptime * 1000, rtp_flags, "soft", &err, switch_core_session_get_pool(*new_session)))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't setup RTP session: [%s]\n", err); goto fail; } if (switch_core_session_thread_launch(*new_session) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't start session thread.\n"); goto fail; } switch_channel_mark_answered(channel); return SWITCH_CAUSE_SUCCESS; fail: if (tech_pvt) { if (tech_pvt->read_codec.implementation) { switch_core_codec_destroy(&tech_pvt->read_codec); } if (tech_pvt->write_codec.implementation) { switch_core_codec_destroy(&tech_pvt->write_codec); } } if (*new_session) { switch_core_session_destroy(new_session); } return SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; } static switch_status_t channel_on_init(switch_core_session_t *session) { switch_channel_t *channel = switch_core_session_get_channel(session); switch_channel_set_state(channel, CS_CONSUME_MEDIA); return SWITCH_STATUS_FALSE; } static switch_status_t channel_on_destroy(switch_core_session_t *session) { crtp_private_t *tech_pvt = NULL; if ((tech_pvt = switch_core_session_get_private(session))) { if (tech_pvt->read_codec.implementation) { switch_core_codec_destroy(&tech_pvt->read_codec); } if (tech_pvt->write_codec.implementation) { switch_core_codec_destroy(&tech_pvt->write_codec); } } return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_read_frame(switch_core_session_t *session, switch_frame_t **frame, switch_io_flag_t flags, int stream_id) { crtp_private_t *tech_pvt; switch_channel_t *channel; switch_status_t status; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); if (!tech_pvt->rtp_session || tech_pvt->mode == RTP_SENDONLY) { switch_yield(20000); /* replace by local timer XXX */ goto cng; } if (switch_rtp_has_dtmf(tech_pvt->rtp_session)) { switch_dtmf_t dtmf = { 0 }; switch_rtp_dequeue_dtmf(tech_pvt->rtp_session, &dtmf); switch_channel_queue_dtmf(channel, &dtmf); } tech_pvt->read_frame.flags = SFF_NONE; tech_pvt->read_frame.codec = &tech_pvt->read_codec; status = switch_rtp_zerocopy_read_frame(tech_pvt->rtp_session, &tech_pvt->read_frame, flags); if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { goto cng; } *frame = &tech_pvt->read_frame; return SWITCH_STATUS_SUCCESS; cng: *frame = &tech_pvt->read_frame; tech_pvt->read_frame.codec = &tech_pvt->read_codec; tech_pvt->read_frame.flags |= SFF_CNG; tech_pvt->read_frame.datalen = 0; return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id) { crtp_private_t *tech_pvt; switch_channel_t *channel; //int frames = 0, bytes = 0, samples = 0; channel = switch_core_session_get_channel(session); assert(channel != NULL); tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); #if 0 if (!switch_test_flag(frame, SFF_CNG) && !switch_test_flag(frame, SFF_PROXY_PACKET)) { if (tech_pvt->read_codec.implementation->encoded_bytes_per_packet) { bytes = tech_pvt->read_codec.implementation->encoded_bytes_per_packet; frames = ((int) frame->datalen / bytes); } else frames = 1; samples = frames * tech_pvt->read_codec.implementation->samples_per_packet; } tech_pvt->timestamp_send += samples; #endif if (tech_pvt->mode == RTP_RECVONLY) { return SWITCH_STATUS_SUCCESS; } switch_rtp_write_frame(tech_pvt->rtp_session, frame); return SWITCH_STATUS_SUCCESS; } static switch_status_t channel_send_dtmf(switch_core_session_t *session, const switch_dtmf_t *dtmf) { crtp_private_t *tech_pvt = NULL; tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); switch(tech_pvt->dtmf_type) { case DTMF_2833: { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Enqueuing RFC2833 DTMF %c of length %d\n", dtmf->digit, dtmf->duration); return switch_rtp_queue_rfc2833(tech_pvt->rtp_session, dtmf); } case DTMF_NONE: default: { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Discarding DTMF %c of length %d, DTMF type is NONE\n", dtmf->digit, dtmf->duration); } } return SWITCH_STATUS_SUCCESS; } static switch_bool_t compare_var(switch_event_t *event, switch_channel_t *channel, const char *varname) { const char *chan_val = switch_channel_get_variable_dup(channel, varname, SWITCH_FALSE, -1); const char *event_val = switch_event_get_header(event, varname); if (zstr(chan_val) || zstr(event_val)) { return 1; } return strcasecmp(chan_val, event_val); } static switch_status_t channel_receive_event(switch_core_session_t *session, switch_event_t *event) { const char *command = switch_event_get_header(event, "command"); switch_channel_t *channel = switch_core_session_get_channel(session); crtp_private_t *tech_pvt = switch_core_session_get_private(session); char *codec = switch_event_get_header_nil(event, kCODEC); char *szptime = switch_event_get_header_nil(event, kPTIME); char *szrate = switch_event_get_header_nil(event, kRATE); char *szpt = switch_event_get_header_nil(event, kPT); int ptime = !zstr(szptime) ? atoi(szptime) : 0, rate = !zstr(szrate) ? atoi(szrate) : 8000, pt = !zstr(szpt) ? atoi(szpt) : 0; if (!zstr(command) && !strcasecmp(command, "media_modify")) { /* Compare parameters */ if (compare_var(event, channel, kREMOTEADDR) || compare_var(event, channel, kREMOTEPORT)) { char *remote_addr = switch_event_get_header(event, kREMOTEADDR); char *szremote_port = switch_event_get_header(event, kREMOTEPORT); switch_port_t remote_port = !zstr(szremote_port) ? (switch_port_t)atoi(szremote_port) : 0; const char *err; switch_channel_set_variable(channel, kREMOTEADDR, remote_addr); switch_channel_set_variable(channel, kREMOTEPORT, szremote_port); if (switch_rtp_set_remote_address(tech_pvt->rtp_session, remote_addr, remote_port, 0, SWITCH_TRUE, &err) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Error setting RTP remote address: %s\n", err); } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Set RTP remote: %s:%d\n", remote_addr, (int)remote_port); tech_pvt->mode = RTP_SENDRECV; } } if (compare_var(event, channel, kCODEC) || compare_var(event, channel, kPTIME) || compare_var(event, channel, kPT) || compare_var(event, channel, kRATE)) { /* Reset codec */ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_CRIT, "Switching codec updating \n"); if (switch_core_codec_init(&tech_pvt->read_codec, codec, NULL, NULL, rate, ptime, 1, /*SWITCH_CODEC_FLAG_ENCODE |*/ SWITCH_CODEC_FLAG_DECODE, NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); goto fail; } else { if (switch_core_codec_init(&tech_pvt->write_codec, codec, NULL, NULL, rate, ptime, 1, SWITCH_CODEC_FLAG_ENCODE /*| SWITCH_CODEC_FLAG_DECODE*/, NULL, switch_core_session_get_pool(tech_pvt->session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't load codec?\n"); goto fail; } } if (switch_core_session_set_read_codec(session, &tech_pvt->read_codec) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set read codec?\n"); goto fail; } if (switch_core_session_set_write_codec(session, &tech_pvt->write_codec) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Can't set write codec?\n"); goto fail; } switch_rtp_set_default_payload(tech_pvt->rtp_session, (switch_payload_t)pt); //switch_rtp_set_recv_pt(tech_pvt->rtp_session, pt); } if (compare_var(event, channel, kRFC2833PT)) { const char *szpt = switch_channel_get_variable(channel, kRFC2833PT); int pt = !zstr(szpt) ? atoi(szpt) : 0; switch_channel_set_variable(channel, kRFC2833PT, szpt); switch_rtp_set_telephony_event(tech_pvt->rtp_session, (switch_payload_t)pt); } } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Received unknown command [%s] in event.\n", !command ? "null" : command); } return SWITCH_STATUS_SUCCESS; fail: if (tech_pvt->read_codec.implementation) { switch_core_codec_destroy(&tech_pvt->read_codec); } if (tech_pvt->write_codec.implementation) { switch_core_codec_destroy(&tech_pvt->write_codec); } switch_core_session_destroy(&session); return SWITCH_STATUS_FALSE; } static switch_status_t channel_receive_message(switch_core_session_t *session, switch_core_session_message_t *msg) { crtp_private_t *tech_pvt = NULL; tech_pvt = switch_core_session_get_private(session); assert(tech_pvt != NULL); switch (msg->message_id) { case SWITCH_MESSAGE_INDICATE_DEBUG_MEDIA: { if (switch_rtp_ready(tech_pvt->rtp_session) && !zstr(msg->string_array_arg[0]) && !zstr(msg->string_array_arg[1])) { switch_rtp_flag_t flags[SWITCH_RTP_FLAG_INVALID] = {0}; int x = 0; if (!strcasecmp(msg->string_array_arg[0], "read")) { flags[SWITCH_RTP_FLAG_DEBUG_RTP_READ]++;x++; } else if (!strcasecmp(msg->string_array_arg[0], "write")) { flags[SWITCH_RTP_FLAG_DEBUG_RTP_WRITE]++;x++; } else if (!strcasecmp(msg->string_array_arg[0], "both")) { flags[SWITCH_RTP_FLAG_DEBUG_RTP_READ]++;x++; flags[SWITCH_RTP_FLAG_DEBUG_RTP_WRITE]++; } if (x) { if (switch_true(msg->string_array_arg[1])) { switch_rtp_set_flags(tech_pvt->rtp_session, flags); } else { switch_rtp_clear_flags(tech_pvt->rtp_session, flags); } } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid Options\n"); } } break; } case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC: if (switch_rtp_ready(tech_pvt->rtp_session)) { rtp_flush_read_buffer(tech_pvt->rtp_session, SWITCH_RTP_FLUSH_ONCE); } break; case SWITCH_MESSAGE_INDICATE_JITTER_BUFFER: { if (switch_rtp_ready(tech_pvt->rtp_session)) { int len = 0, maxlen = 0, qlen = 0, maxqlen = 50; if (msg->string_arg) { char *p; const char *s; if (!strcasecmp(msg->string_arg, "pause")) { switch_rtp_pause_jitter_buffer(tech_pvt->rtp_session, SWITCH_TRUE); goto end; } else if (!strcasecmp(msg->string_arg, "resume")) { switch_rtp_pause_jitter_buffer(tech_pvt->rtp_session, SWITCH_FALSE); goto end; } else if (!strncasecmp(msg->string_arg, "debug:", 6)) { s = msg->string_arg + 6; if (s && !strcmp(s, "off")) { s = NULL; } switch_rtp_debug_jitter_buffer(tech_pvt->rtp_session, s); goto end; } if ((len = atoi(msg->string_arg))) { qlen = len / (tech_pvt->read_codec.implementation->microseconds_per_packet / 1000); if (qlen < 1) { qlen = 3; } } if (qlen) { if ((p = strchr(msg->string_arg, ':'))) { p++; maxlen = atol(p); } } if (maxlen) { maxqlen = maxlen / (tech_pvt->read_codec.implementation->microseconds_per_packet / 1000); } } if (qlen) { if (maxqlen < qlen) { maxqlen = qlen * 5; } if (switch_rtp_activate_jitter_buffer(tech_pvt->rtp_session, qlen, maxqlen, tech_pvt->read_codec.implementation->samples_per_packet, tech_pvt->read_codec.implementation->samples_per_second) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_DEBUG, "Setting Jitterbuffer to %dms (%d frames) (%d max frames)\n", len, qlen, maxqlen); switch_channel_set_flag(tech_pvt->channel, CF_JITTERBUFFER); if (!switch_false(switch_channel_get_variable(tech_pvt->channel, "rtp_jitter_buffer_plc"))) { switch_channel_set_flag(tech_pvt->channel, CF_JITTERBUFFER_PLC); } } else { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(tech_pvt->session), SWITCH_LOG_WARNING, "Error Setting Jitterbuffer to %dms (%d frames)\n", len, qlen); } } else { switch_rtp_deactivate_jitter_buffer(tech_pvt->rtp_session); } } } break; default: break; } end: return SWITCH_STATUS_SUCCESS; }