#include <switch_curl.h>
#include <errno.h>
+typedef union {
+ int32_t intval;
+ uint32_t uintval;
+ char *charval;
+} scm_multi_t;
+
+
+static scm_type_t typemap[SCM_MAX] = {
+ /*SCM_INBOUND_CODEC_STRING*/ STYPE_CHARVAL,
+ /*SCM_OUTBOUND_CODEC_STRING*/ STYPE_CHARVAL,
+ /*SCM_TEST*/ STYPE_INTVAL
+};
+
typedef enum {
SMH_INIT = (1 << 0),
SMH_READY = (1 << 1)
typedef struct switch_rtp_engine_s {
switch_secure_settings_t ssec;
- switch_rtp_t *rtp_session;
+ switch_rtp_t *rtp_session;//tp
+ switch_frame_t read_frame;//tp
switch_media_type_t type;
} switch_rtp_engine_t;
struct switch_media_handle_s {
switch_core_session_t *session;
+ switch_channel_t *channel;
switch_core_media_NDLB_t ndlb;
+ switch_core_media_flag_t media_flags;
smh_flag_t flags;
switch_rtp_engine_t engines[SWITCH_MEDIA_TYPE_TOTAL];
+ scm_multi_t params[SCM_MAX];
+ char *codec_order[SWITCH_MAX_CODECS];//tp
+ int codec_order_last;//tp
+ const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS];//tp
+ int num_codecs;//tp
+ int payload_space;//tp
};
+SWITCH_DECLARE(void) switch_media_set_param(switch_media_handle_t *smh, scm_param_t param, ...)
+{
+ scm_multi_t *val = &smh->params[param];
+ scm_type_t type = typemap[param];
+ va_list ap;
+
+ va_start(ap, param);
+
+ switch(type) {
+ case STYPE_INTVAL:
+ val->intval = va_arg(ap, int);
+ break;
+ case STYPE_UINTVAL:
+ val->uintval = va_arg(ap, unsigned int);
+ break;
+ case STYPE_CHARVAL:
+ val->charval = switch_core_session_strdup(smh->session, va_arg(ap, char *));
+ break;
+ default:
+ abort();
+ }
+
+ va_end(ap);
+}
+
+SWITCH_DECLARE(void *) switch_media_get_param(switch_media_handle_t *smh, scm_param_t param)
+{
+ scm_multi_t *val = &smh->params[param];
+ scm_type_t type = typemap[param];
+
+ switch(type) {
+ case STYPE_INTVAL:
+ return &val->intval;
+ break;
+ case STYPE_UINTVAL:
+ return &val->uintval;
+ break;
+ case STYPE_CHARVAL:
+ return val->charval;
+ break;
+ default:
+ abort();
+ }
+
+ return NULL;
+
+}
+
+#define get_str(_o, _p) _o->params[_p].charval
+
+SWITCH_DECLARE(const char *)switch_core_media_get_codec_string(switch_core_session_t *session)
+{
+ const char *preferred = NULL, *fallback = NULL;
+ switch_media_handle_t *smh;
+
+ if (!(smh = session->media_handle)) {
+ preferred = "PCMU";
+ fallback = "PCMU";
+ } else {
+
+ if (!(preferred = switch_channel_get_variable(session->channel, "absolute_codec_string"))) {
+ preferred = switch_channel_get_variable(session->channel, "codec_string");
+ }
+
+ if (!preferred) {
+ if (switch_channel_direction(session->channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
+ preferred = get_str(smh, SCM_OUTBOUND_CODEC_STRING);
+ fallback = get_str(smh, SCM_INBOUND_CODEC_STRING);
+
+ } else {
+ preferred = get_str(smh, SCM_INBOUND_CODEC_STRING);
+ fallback = get_str(smh, SCM_OUTBOUND_CODEC_STRING);
+ }
+ }
+ }
+
+ return !zstr(preferred) ? preferred : fallback;
+}
+
SWITCH_DECLARE(const char *) switch_core_session_local_crypto_key(switch_core_session_t *session, switch_media_type_t type)
{
if (!session->media_handle) return;
engine = &session->media_handle->engines[type];
engine->rtp_session = rtp_session;
+ engine->type = type;
}
session->media_handle->session = session;
*smhp = session->media_handle;
switch_set_flag(session->media_handle, SMH_INIT);
+
+ session->media_handle->engines[SWITCH_MEDIA_TYPE_AUDIO].read_frame.buflen = SWITCH_RTP_MAX_BUF_LEN;
+ session->media_handle->engines[SWITCH_MEDIA_TYPE_VIDEO].read_frame.buflen = SWITCH_RTP_MAX_BUF_LEN;
+
status = SWITCH_STATUS_SUCCESS;
}
{
switch_assert(smh);
- smh->flags |= flag;
+ smh->ndlb |= flag;
}
{
switch_assert(smh);
- smh->flags &= ~flag;
+ smh->ndlb &= ~flag;
}
SWITCH_DECLARE(int32_t) switch_media_handle_test_ndlb(switch_media_handle_t *smh, switch_core_media_NDLB_t flag)
{
switch_assert(smh);
- return (smh->flags & flag);
+ return (smh->ndlb & flag);
+}
+
+SWITCH_DECLARE(void) switch_media_handle_set_media_flag(switch_media_handle_t *smh, switch_core_media_flag_t flag)
+{
+ switch_assert(smh);
+
+ smh->media_flags |= flag;
+
+}
+
+SWITCH_DECLARE(void) switch_media_handle_clear_media_flag(switch_media_handle_t *smh, switch_core_media_flag_t flag)
+{
+ switch_assert(smh);
+
+ smh->media_flags &= ~flag;
+}
+
+SWITCH_DECLARE(int32_t) switch_media_handle_test_media_flag(switch_media_handle_t *smh, switch_core_media_flag_t flag)
+{
+ switch_assert(smh);
+ return (smh->media_flags & flag);
}
SWITCH_DECLARE(switch_status_t) switch_core_session_media_handle_ready(switch_core_session_t *session)
+
+#if 0
+SWITCH_DECLARE(switch_status_t) switch_core_session_read_media_frame(switch_core_session_t *session, switch_frame_t **frame,
+ switch_io_flag_t flags, int stream_id, switch_media_type_t type)
+{
+ switch_channel_t *channel;
+ uint32_t sanity = 1000;
+ switch_rtcp_frame_t rtcp_frame;
+ switch_rtp_engine_t *engine;
+
+ if (!session->media_handle) return;
+
+ engine = &session->media_handle->engines[type];
+ channel = switch_core_session_get_channel(session);
+
+
+ switch_assert(tech_pvt != NULL);
+
+
+ tech_pvt->read_frame.datalen = 0;
+ sofia_set_flag_locked(tech_pvt, TFLAG_READING);
+
+ if (sofia_test_flag(tech_pvt, TFLAG_HUP) || sofia_test_flag(tech_pvt, TFLAG_BYE) || !tech_pvt->read_codec.implementation ||
+ !switch_core_codec_ready(&tech_pvt->read_codec)) {
+ return SWITCH_STATUS_FALSE;
+ }
+
+ if (sofia_test_flag(tech_pvt, TFLAG_IO)) {
+ switch_status_t status;
+
+ if (!sofia_test_flag(tech_pvt, TFLAG_RTP)) {
+ return SWITCH_STATUS_GENERR;
+ }
+
+ switch_assert(tech_pvt->rtp_session != NULL);
+ tech_pvt->read_frame.datalen = 0;
+
+
+ if (sofia_test_flag(tech_pvt, TFLAG_SIMPLIFY) && sofia_test_flag(tech_pvt, TFLAG_GOT_ACK)) {
+ if (sofia_glue_tech_simplify(tech_pvt)) {
+ sofia_clear_flag(tech_pvt, TFLAG_SIMPLIFY);
+ }
+ }
+
+ while (sofia_test_flag(tech_pvt, TFLAG_IO) && tech_pvt->read_frame.datalen == 0) {
+ tech_pvt->read_frame.flags = SFF_NONE;
+
+ status = switch_rtp_zerocopy_read_frame(tech_pvt->rtp_session, &tech_pvt->read_frame, flags);
+
+ if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) {
+ if (status == SWITCH_STATUS_TIMEOUT) {
+
+ if (sofia_test_flag(tech_pvt, TFLAG_SIP_HOLD)) {
+ sofia_glue_toggle_hold(tech_pvt, 0);
+ sofia_clear_flag_locked(tech_pvt, TFLAG_SIP_HOLD);
+ switch_channel_clear_flag(channel, CF_LEG_HOLDING);
+ }
+
+ if (switch_channel_get_variable(tech_pvt->channel, "execute_on_media_timeout")) {
+ *frame = &tech_pvt->read_frame;
+ switch_set_flag((*frame), SFF_CNG);
+ (*frame)->datalen = tech_pvt->read_impl.encoded_bytes_per_packet;
+ memset((*frame)->data, 0, (*frame)->datalen);
+ switch_channel_execute_on(tech_pvt->channel, "execute_on_media_timeout");
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+
+ switch_channel_hangup(tech_pvt->channel, SWITCH_CAUSE_MEDIA_TIMEOUT);
+ }
+ return status;
+ }
+
+ /* Try to read an RTCP frame, if successful raise an event */
+ if (switch_rtcp_zerocopy_read_frame(tech_pvt->rtp_session, &rtcp_frame) == SWITCH_STATUS_SUCCESS) {
+ switch_event_t *event;
+
+ if (switch_event_create(&event, SWITCH_EVENT_RECV_RTCP_MESSAGE) == SWITCH_STATUS_SUCCESS) {
+ char value[30];
+ char header[50];
+ int i;
+
+ char *uuid = switch_core_session_get_uuid(session);
+ if (uuid) {
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(session));
+ }
+
+ snprintf(value, sizeof(value), "%.8x", rtcp_frame.ssrc);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "SSRC", value);
+
+ snprintf(value, sizeof(value), "%u", rtcp_frame.ntp_msw);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "NTP-Most-Significant-Word", value);
+
+ snprintf(value, sizeof(value), "%u", rtcp_frame.ntp_lsw);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "NTP-Least-Significant-Word", value);
+
+ snprintf(value, sizeof(value), "%u", rtcp_frame.timestamp);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "RTP-Timestamp", value);
+
+ snprintf(value, sizeof(value), "%u", rtcp_frame.packet_count);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Sender-Packet-Count", value);
+
+ snprintf(value, sizeof(value), "%u", rtcp_frame.octect_count);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Octect-Packet-Count", value);
+
+ snprintf(value, sizeof(value), "%" SWITCH_SIZE_T_FMT, tech_pvt->read_frame.timestamp);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Last-RTP-Timestamp", value);
+
+ snprintf(value, sizeof(value), "%u", tech_pvt->read_frame.rate);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "RTP-Rate", value);
+
+ snprintf(value, sizeof(value), "%" SWITCH_TIME_T_FMT, switch_time_now());
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Capture-Time", value);
+
+ // Add sources info
+ for (i = 0; i < rtcp_frame.report_count; i++) {
+ snprintf(header, sizeof(header), "Source%u-SSRC", i);
+ snprintf(value, sizeof(value), "%.8x", rtcp_frame.reports[i].ssrc);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value);
+ snprintf(header, sizeof(header), "Source%u-Fraction", i);
+ snprintf(value, sizeof(value), "%u", rtcp_frame.reports[i].fraction);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value);
+ snprintf(header, sizeof(header), "Source%u-Lost", i);
+ snprintf(value, sizeof(value), "%u", rtcp_frame.reports[i].lost);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value);
+ snprintf(header, sizeof(header), "Source%u-Highest-Sequence-Number-Received", i);
+ snprintf(value, sizeof(value), "%u", rtcp_frame.reports[i].highest_sequence_number_received);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value);
+ snprintf(header, sizeof(header), "Source%u-Jitter", i);
+ snprintf(value, sizeof(value), "%u", rtcp_frame.reports[i].jitter);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value);
+ snprintf(header, sizeof(header), "Source%u-LSR", i);
+ snprintf(value, sizeof(value), "%u", rtcp_frame.reports[i].lsr);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value);
+ snprintf(header, sizeof(header), "Source%u-DLSR", i);
+ snprintf(value, sizeof(value), "%u", rtcp_frame.reports[i].dlsr);
+ switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value);
+ }
+
+ switch_event_fire(&event);
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG10, "Dispatched RTCP event\n");
+ }
+ }
+
+ /* Fast PASS! */
+ if (switch_test_flag((&tech_pvt->read_frame), SFF_PROXY_PACKET)) {
+ sofia_clear_flag_locked(tech_pvt, TFLAG_READING);
+ *frame = &tech_pvt->read_frame;
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ 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);
+ }
+
+ if (tech_pvt->read_frame.datalen > 0) {
+ uint32_t bytes = 0;
+ int frames = 1;
+
+ if (!switch_test_flag((&tech_pvt->read_frame), SFF_CNG)) {
+ if (!tech_pvt->read_codec.implementation || !switch_core_codec_ready(&tech_pvt->read_codec)) {
+ *frame = NULL;
+ return SWITCH_STATUS_GENERR;
+ }
+
+ if ((tech_pvt->read_frame.datalen % 10) == 0 &&
+ sofia_test_pflag(tech_pvt->profile, PFLAG_AUTOFIX_TIMING) && tech_pvt->check_frames < MAX_CODEC_CHECK_FRAMES) {
+ tech_pvt->check_frames++;
+
+ if (!tech_pvt->read_impl.encoded_bytes_per_packet) {
+ tech_pvt->check_frames = MAX_CODEC_CHECK_FRAMES;
+ goto skip;
+ }
+
+ if (tech_pvt->last_ts && tech_pvt->read_frame.datalen != tech_pvt->read_impl.encoded_bytes_per_packet) {
+ uint32_t codec_ms = (int) (tech_pvt->read_frame.timestamp -
+ tech_pvt->last_ts) / (tech_pvt->read_impl.samples_per_second / 1000);
+
+ if ((codec_ms % 10) != 0 || codec_ms > tech_pvt->read_impl.samples_per_packet * 10) {
+ tech_pvt->last_ts = 0;
+ goto skip;
+ }
+
+
+ if (tech_pvt->last_codec_ms && tech_pvt->last_codec_ms == codec_ms) {
+ tech_pvt->mismatch_count++;
+ }
+
+ tech_pvt->last_codec_ms = codec_ms;
+
+ if (tech_pvt->mismatch_count > MAX_MISMATCH_FRAMES) {
+ if (switch_rtp_ready(tech_pvt->rtp_session) && codec_ms != tech_pvt->codec_ms) {
+ const char *val;
+ int rtp_timeout_sec = 0;
+ int rtp_hold_timeout_sec = 0;
+
+ if (codec_ms > 120) { /* yeah right */
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
+ "Your phone is trying to send timestamps that suggest an increment of %dms per packet\n"
+ "That seems hard to believe so I am going to go on ahead and um ignore that, mmkay?\n",
+ (int) codec_ms);
+ tech_pvt->check_frames = MAX_CODEC_CHECK_FRAMES;
+ goto skip;
+ }
+
+ tech_pvt->read_frame.datalen = 0;
+
+ if (codec_ms != tech_pvt->codec_ms) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
+ "Asynchronous PTIME not supported, changing our end from %d to %d\n",
+ (int) tech_pvt->codec_ms,
+ (int) codec_ms
+ );
+
+ switch_channel_set_variable_printf(channel, "sip_h_X-Broken-PTIME", "Adv=%d;Sent=%d",
+ (int) tech_pvt->codec_ms, (int) codec_ms);
+
+ tech_pvt->codec_ms = codec_ms;
+ }
+
+ if (sofia_glue_tech_set_codec(tech_pvt, 2) != SWITCH_STATUS_SUCCESS) {
+ *frame = NULL;
+ return SWITCH_STATUS_GENERR;
+ }
+
+ if ((val = switch_channel_get_variable(tech_pvt->channel, "rtp_timeout_sec"))) {
+ int v = atoi(val);
+ if (v >= 0) {
+ rtp_timeout_sec = v;
+ }
+ }
+
+ if ((val = switch_channel_get_variable(tech_pvt->channel, "rtp_hold_timeout_sec"))) {
+ int v = atoi(val);
+ if (v >= 0) {
+ rtp_hold_timeout_sec = v;
+ }
+ }
+
+ if (rtp_timeout_sec) {
+ tech_pvt->max_missed_packets = (tech_pvt->read_impl.samples_per_second * rtp_timeout_sec) /
+ tech_pvt->read_impl.samples_per_packet;
+
+ switch_rtp_set_max_missed_packets(tech_pvt->rtp_session, tech_pvt->max_missed_packets);
+ if (!rtp_hold_timeout_sec) {
+ rtp_hold_timeout_sec = rtp_timeout_sec * 10;
+ }
+ }
+
+ if (rtp_hold_timeout_sec) {
+ tech_pvt->max_missed_hold_packets = (tech_pvt->read_impl.samples_per_second * rtp_hold_timeout_sec) /
+ tech_pvt->read_impl.samples_per_packet;
+ }
+
+
+ tech_pvt->check_frames = 0;
+ tech_pvt->last_ts = 0;
+
+ /* inform them of the codec they are actually sending */
+#if 0
+ if (++tech_pvt->codec_reinvites > 2) {
+ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING,
+ "Ok, some devices *cough* X-lite *cough*\n"
+ "seem to continue to lie over and over again so I guess we'll\n"
+ "leave well-enough alone and let them lie\n");
+ } else {
+ sofia_glue_do_invite(session);
+ }
+#endif
+ *frame = &tech_pvt->read_frame;
+ switch_set_flag((*frame), SFF_CNG);
+ (*frame)->datalen = tech_pvt->read_impl.encoded_bytes_per_packet;
+ memset((*frame)->data, 0, (*frame)->datalen);
+ return SWITCH_STATUS_SUCCESS;
+ }
+
+ }
+
+ } else {
+ tech_pvt->mismatch_count = 0;
+ }
+
+ tech_pvt->last_ts = tech_pvt->read_frame.timestamp;
+
+
+ } else {
+ tech_pvt->mismatch_count = 0;
+ tech_pvt->last_ts = 0;
+ }
+ skip:
+
+ if ((bytes = tech_pvt->read_impl.encoded_bytes_per_packet)) {
+ frames = (tech_pvt->read_frame.datalen / bytes);
+ }
+ tech_pvt->read_frame.samples = (int) (frames * tech_pvt->read_impl.samples_per_packet);
+
+ if (tech_pvt->read_frame.datalen == 0) {
+ continue;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ sofia_clear_flag_locked(tech_pvt, TFLAG_READING);
+
+ if (tech_pvt->read_frame.datalen == 0) {
+ *frame = NULL;
+ return SWITCH_STATUS_GENERR;
+ }
+
+ *frame = &tech_pvt->read_frame;
+
+ return SWITCH_STATUS_SUCCESS;
+}
+#endif
+
+
+
+SWITCH_DECLARE(void) switch_core_media_prepare_codecs(switch_core_session_t *session, switch_bool_t force)
+{
+ const char *abs, *codec_string = NULL;
+ const char *ocodec = NULL;
+ switch_media_handle_t *smh;
+
+ if (!(smh = session->media_handle)) {
+ return;
+ }
+
+ if (switch_channel_test_flag(session->channel, CF_PROXY_MODE) || switch_channel_test_flag(session->channel, CF_PROXY_MEDIA)) {
+ return;
+ }
+
+ if (force) {
+ smh->num_codecs = 0;
+ }
+
+ if (smh->num_codecs) {
+ return;
+ }
+
+ smh->payload_space = 0;
+
+ switch_assert(smh->session != NULL);
+
+ if ((abs = switch_channel_get_variable(session->channel, "absolute_codec_string"))) {
+ /* inherit_codec == true will implicitly clear the absolute_codec_string
+ variable if used since it was the reason it was set in the first place and is no longer needed */
+ if (switch_true(switch_channel_get_variable(session->channel, "inherit_codec"))) {
+ switch_channel_set_variable(session->channel, "absolute_codec_string", NULL);
+ }
+ codec_string = abs;
+ goto ready;
+ }
+
+ if (!(codec_string = switch_channel_get_variable(session->channel, "codec_string"))) {
+ codec_string = switch_core_media_get_codec_string(smh->session);
+ }
+
+ if (codec_string && *codec_string == '=') {
+ codec_string++;
+ goto ready;
+ }
+
+
+ if ((ocodec = switch_channel_get_variable(session->channel, SWITCH_ORIGINATOR_CODEC_VARIABLE))) {
+ if (!codec_string || (smh->media_flags & SCMF_DISABLE_TRANSCODING)) {
+ codec_string = ocodec;
+ } else {
+ if (!(codec_string = switch_core_session_sprintf(smh->session, "%s,%s", ocodec, codec_string))) {
+ codec_string = ocodec;
+ }
+ }
+ }
+
+ ready:
+
+ if (codec_string) {
+ char *tmp_codec_string = switch_core_session_strdup(smh->session, codec_string);
+ smh->codec_order_last = switch_separate_string(tmp_codec_string, ',', smh->codec_order, SWITCH_MAX_CODECS);
+ smh->num_codecs = switch_loadable_module_get_codecs_sorted(smh->codecs, SWITCH_MAX_CODECS, smh->codec_order, smh->codec_order_last);
+ } else {
+ smh->num_codecs = switch_loadable_module_get_codecs(smh->codecs, sizeof(smh->codecs) / sizeof(smh->codecs[0]));
+ }
+}
+
+
+
/* For Emacs:
* Local Variables:
* mode:c