diff --git a/libs/.gitignore b/libs/.gitignore index 093c5bdf10..9bf6e4bab6 100644 --- a/libs/.gitignore +++ b/libs/.gitignore @@ -558,11 +558,13 @@ opal /spandsp/src/Makefile.in /spandsp/src/make_math_fixed_tables /spandsp/src/make_modem_filter +/spandsp/src/make_t43_gray_code_tables /spandsp/src/math_fixed_tables.h /spandsp/src/msvc/All/BuildLog make_at_dictionary.htm /spandsp/src/msvc/All/BuildLog make_modem_filter.htm /spandsp/src/spandsp.h /spandsp/src/stamp-h1 +/spandsp/src/t43_gray_code_tables.h /spandsp/src/v17_v32bis_rx_fixed_rrc.h /spandsp/src/v17_v32bis_rx_floating_rrc.h /spandsp/src/v17_v32bis_tx_fixed_rrc.h diff --git a/libs/sofia-sip/.update b/libs/sofia-sip/.update index c36486c61a..17253549d1 100644 --- a/libs/sofia-sip/.update +++ b/libs/sofia-sip/.update @@ -1 +1 @@ -Wed Jul 3 11:09:02 CDT 2013 +Thu Jul 11 17:38:06 CDT 2013 diff --git a/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c b/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c index c912dd3b59..b587aa5cc1 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c +++ b/libs/sofia-sip/libsofia-sip-ua/sdp/sdp_print.c @@ -590,7 +590,7 @@ static void print_media(sdp_printer_t *p, case sdp_proto_udp: proto = "udp"; break; case sdp_proto_rtp: proto = "RTP/AVP"; break; case sdp_proto_srtp: proto = "RTP/SAVP"; break; - case sdp_proto_extended_srtp: proto = "RTP/SAVPF"; break; + //case sdp_proto_extended_srtp: proto = "RTP/SAVPF"; break; case sdp_proto_udptl: proto = "udptl"; break; case sdp_proto_msrp: proto = "TCP/MSRP"; break; case sdp_proto_msrps: proto = "TCP/TLS/MSRP"; break; diff --git a/src/include/switch_rtp.h b/src/include/switch_rtp.h index ff7079b833..f704d943d1 100644 --- a/src/include/switch_rtp.h +++ b/src/include/switch_rtp.h @@ -139,6 +139,7 @@ SWITCH_DECLARE(void) switch_rtp_shutdown(void); SWITCH_DECLARE(switch_port_t) switch_rtp_set_start_port(switch_port_t port); SWITCH_DECLARE(switch_status_t) switch_rtp_set_ssrc(switch_rtp_t *rtp_session, uint32_t ssrc); +SWITCH_DECLARE(switch_status_t) switch_rtp_set_remote_ssrc(switch_rtp_t *rtp_session, uint32_t ssrc); /*! \brief Set/Get RTP end port diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 2e469de3de..315c0b2fdf 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -1326,6 +1326,7 @@ typedef enum { CF_DTLS_OK, CF_VIDEO_PASSIVE, CF_NOVIDEO, + CF_VIDEO_ECHO, /* WARNING: DO NOT ADD ANY FLAGS BELOW THIS LINE */ /* IF YOU ADD NEW ONES CHECK IF THEY SHOULD PERSIST OR ZERO THEM IN switch_core_session.c switch_core_session_request_xml() */ CF_FLAG_MAX diff --git a/src/include/switch_utils.h b/src/include/switch_utils.h index 755ac91d27..daceb854a6 100644 --- a/src/include/switch_utils.h +++ b/src/include/switch_utils.h @@ -598,31 +598,39 @@ switch_mutex_unlock(obj->flag_mutex); #define switch_set_string(_dst, _src) switch_copy_string(_dst, _src, sizeof(_dst)) +SWITCH_DECLARE(char *) switch_url_encode(const char *url, char *buf, size_t len); +SWITCH_DECLARE(char *) switch_url_decode(char *s); static inline char *switch_sanitize_number(char *number) { - char *p = number, *q; + char *p, *q; char warp[] = "/:"; int i; + char *val; switch_assert(number); - if (!(strchr(p, '/') || strchr(p, ':') || strchr(p, '@') || strchr(p, '%'))) { + p = strdup(number); + val = p; + switch_url_decode(val); + + if (!(strchr(val, '/') || strchr(val, ':') || strchr(val, '@') || strchr(val, '%'))) { return number; } - while ((q = strrchr(p, '@'))) + while ((q = strrchr(val, '@'))) *q = '\0'; - while ((q = strrchr(p, '%'))) + while ((q = strrchr(val, '%'))) *q = '\0'; for (i = 0; i < (int) strlen(warp); i++) { - while (p && (q = strchr(p, warp[i]))) - p = q + 1; + while (val && (q = strchr(val, warp[i]))) + val = q + 1; } + free(p); - return p; + return val; } static inline switch_bool_t switch_string_var_check(char *s, switch_bool_t disable) @@ -930,8 +938,6 @@ SWITCH_DECLARE(char *) switch_util_quote_shell_arg_pool(const char *string, swit #define SWITCH_READ_ACCEPTABLE(status) (status == SWITCH_STATUS_SUCCESS || status == SWITCH_STATUS_BREAK) -SWITCH_DECLARE(char *) switch_url_encode(const char *url, char *buf, size_t len); -SWITCH_DECLARE(char *) switch_url_decode(char *s); SWITCH_DECLARE(switch_bool_t) switch_simple_email(const char *to, const char *from, const char *headers, diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index a841f2d371..bb572f4e78 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -201,7 +201,8 @@ typedef enum { CFLAG_VIDEO_BRIDGE = (1 << 14), CFLAG_AUDIO_ALWAYS = (1 << 15), CFLAG_ENDCONF_FORCED = (1 << 16), - CFLAG_RFC4579 = (1 << 17) + CFLAG_RFC4579 = (1 << 17), + CFLAG_FLOOR_CHANGE = (1 << 18) } conf_flag_t; typedef enum { @@ -394,6 +395,7 @@ typedef struct conference_relationship { struct conference_member { uint32_t id; switch_core_session_t *session; + switch_channel_t *channel; conference_obj_t *conference; switch_memory_pool_t *pool; switch_buffer_t *audio_buffer; @@ -1443,15 +1445,20 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe conference_send_presence(conference); - - channel = switch_core_session_get_channel(member->session); - switch_channel_set_flag(channel, CF_VIDEO_PASSIVE); + switch_channel_set_variable_printf(channel, "conference_member_id", "%d", member->id); switch_channel_set_variable_printf(channel, "conference_moderator", "%s", switch_test_flag(member, MFLAG_MOD) ? "true" : "false"); switch_channel_set_variable(channel, "conference_recording", conference->record_filename); switch_channel_set_variable(channel, CONFERENCE_UUID_VARIABLE, conference->uuid_str); - + + + + if (switch_channel_test_flag(channel, CF_VIDEO)) { + switch_channel_clear_flag(channel, CF_VIDEO_ECHO); + /* Tell the channel to request a fresh vid frame */ + switch_core_session_refresh_video(member->session); + } if (!switch_channel_get_variable(channel, "conference_call_key")) { char *key = switch_core_session_sprintf(member->session, "conf_%s_%s_%s", @@ -1565,6 +1572,20 @@ static switch_status_t conference_add_member(conference_obj_t *conference, confe return status; } +static void conference_set_floor_holder(conference_obj_t *conference, conference_member_t *member) +{ + + if (conference->floor_holder && conference->floor_holder != member) { + switch_channel_clear_flag(conference->floor_holder->channel, CF_VIDEO_PASSIVE); + } + + if ((conference->floor_holder = member)) { + switch_channel_set_flag(member->channel, CF_VIDEO_PASSIVE); + switch_core_session_refresh_video(conference->floor_holder->session); + switch_set_flag(conference, CFLAG_FLOOR_CHANGE); + } +} + /* Gain exclusive access and remove the member from the list */ static switch_status_t conference_del_member(conference_obj_t *conference, conference_member_t *member) { @@ -1648,7 +1669,9 @@ static switch_status_t conference_del_member(conference_obj_t *conference, confe } if (member == member->conference->floor_holder) { - member->conference->floor_holder = NULL; + //member->conference->floor_holder = NULL; + conference_set_floor_holder(member->conference, NULL); + if (test_eflag(conference, EFLAG_FLOOR_CHANGE)) { switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); @@ -1673,7 +1696,10 @@ static switch_status_t conference_del_member(conference_obj_t *conference, confe } } - switch_channel_clear_flag(channel, CF_VIDEO_PASSIVE); + if (switch_channel_test_flag(channel, CF_VIDEO)) { + switch_channel_set_flag(channel, CF_VIDEO_ECHO); + switch_channel_clear_flag(channel, CF_VIDEO_PASSIVE); + } conference_send_presence(conference); switch_channel_set_variable(channel, "conference_call_key", NULL); @@ -1839,19 +1865,15 @@ static void *SWITCH_THREAD_FUNC conference_video_thread_run(switch_thread_t *thr conference_member_t *imember; switch_frame_t *vid_frame; switch_status_t status; - int has_vid = 1, want_refresh = 0; + int want_refresh = 0; int yield = 0; switch_core_session_t *session; - switch_core_session_message_t msg = { 0 }; + char buf[65536]; conference->video_running = 1; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Video thread started for conference %s\n", conference->name); - /* Tell the channel to request a fresh vid frame */ - msg.from = __FILE__; - msg.message_id = SWITCH_MESSAGE_INDICATE_VIDEO_REFRESH_REQ; - - while (has_vid && conference->video_running == 1 && globals.running && !switch_test_flag(conference, CFLAG_DESTRUCT)) { + while (conference->video_running == 1 && globals.running && !switch_test_flag(conference, CFLAG_DESTRUCT)) { if (yield) { switch_yield(yield); yield = 0; @@ -1887,12 +1909,21 @@ static void *SWITCH_THREAD_FUNC conference_video_thread_run(switch_thread_t *thr goto do_continue; } + if (vid_frame && switch_test_flag(vid_frame, SFF_CNG)) { + yield = 10000; + goto do_continue; + } + memcpy(buf, vid_frame->packet, vid_frame->packetlen); + switch_mutex_unlock(conference->mutex); switch_mutex_lock(conference->mutex); - has_vid = 0; want_refresh = 0; + if (switch_test_flag(conference, CFLAG_FLOOR_CHANGE)) { + switch_clear_flag(conference, CFLAG_FLOOR_CHANGE); + } + for (imember = conference->members; imember; imember = imember->next) { switch_core_session_t *isession = imember->session; switch_channel_t *ichannel; @@ -1908,21 +1939,20 @@ static void *SWITCH_THREAD_FUNC conference_video_thread_run(switch_thread_t *thr switch_channel_clear_flag(ichannel, CF_VIDEO_REFRESH_REQ); } - if (imember->session && switch_channel_test_flag(ichannel, CF_VIDEO)) { - has_vid++; + if (isession && switch_channel_test_flag(ichannel, CF_VIDEO)) { + memcpy(vid_frame->packet, buf, vid_frame->packetlen); switch_core_session_write_video_frame(imember->session, vid_frame, SWITCH_IO_FLAG_NONE, 0); } switch_core_session_rwunlock(isession); } - if (want_refresh) { - switch_core_session_receive_message(session, &msg); + if (want_refresh && session) { + switch_core_session_refresh_video(session); want_refresh = 0; } do_continue: - switch_mutex_unlock(conference->mutex); } @@ -2078,7 +2108,8 @@ static void *SWITCH_THREAD_FUNC conference_thread_run(switch_thread_t *thread, v } } - conference->floor_holder = floor_holder; + //conference->floor_holder = floor_holder; + conference_set_floor_holder(conference, floor_holder); } @@ -5219,7 +5250,8 @@ static switch_status_t conf_api_sub_floor(conference_member_t *member, switch_st switch_mutex_lock(member->conference->mutex); if (member->conference->floor_holder == member) { - member->conference->floor_holder = NULL; + //member->conference->floor_holder = NULL; + conference_set_floor_holder(member->conference, NULL); if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) { switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); conference_add_event_data(member->conference, event); @@ -5232,7 +5264,8 @@ static switch_status_t conf_api_sub_floor(conference_member_t *member, switch_st } } } else if (member->conference->floor_holder == NULL) { - member->conference->floor_holder = member; + //member->conference->floor_holder = member; + conference_set_floor_holder(member->conference, member); if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) { switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); conference_add_event_data(member->conference, event); @@ -5272,7 +5305,8 @@ static switch_status_t conf_api_sub_enforce_floor(conference_member_t *member, s if (member->conference->floor_holder != member) { conference_member_t *old_member = member->conference->floor_holder; - member->conference->floor_holder = member; + //member->conference->floor_holder = member; + conference_set_floor_holder(member->conference, member); if (test_eflag(member->conference, EFLAG_FLOOR_CHANGE)) { switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, CONF_EVENT_MAINT); conference_add_event_data(member->conference, event); @@ -7781,6 +7815,7 @@ SWITCH_STANDARD_APP(conference_function) } member.session = session; + member.channel = switch_core_session_get_channel(session); member.pool = switch_core_session_get_pool(session); if (setup_media(&member, conference)) { diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.c b/src/mod/endpoints/mod_skinny/skinny_protocol.c index eb472167ee..677c703b39 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.c +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.c @@ -37,7 +37,7 @@ /*****************************************************************************/ /* SKINNY FUNCTIONS */ /*****************************************************************************/ -char* skinny_codec2string(enum skinny_codecs skinnycodec) +char* skinny_codec2string(skinny_codecs skinnycodec) { switch (skinnycodec) { case SKINNY_CODEC_ALAW_64K: @@ -155,8 +155,8 @@ switch_status_t skinny_read_packet(listener_t *listener, skinny_message_t **req) memcpy(request, mbuf, bytes); #ifdef SKINNY_MEGA_DEBUG switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, - "Got request: length=%d,reserved=%x,type=%x\n", - request->length,request->reserved,request->type); + "Got request: length=%d,version=%x,type=%x\n", + request->length,request->version,request->type); #endif if(request->length < SKINNY_MESSAGE_FIELD_SIZE) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, @@ -623,6 +623,26 @@ switch_status_t perform_send_set_speaker_mode(listener_t *listener, return skinny_send_reply_quiet(listener, message); } +switch_status_t perform_send_srvreq_response(listener_t *listener, + const char *file, const char *func, int line, + char *ip, uint32_t port) +{ + skinny_message_t *message; + + message = switch_core_alloc(listener->pool, 12+sizeof(message->data.serv_res_mess)); + message->type = SERVER_RESPONSE_MESSAGE; + message->length = 4 + sizeof(message->data.serv_res_mess); + + message->data.serv_res_mess.serverListenPort[0] = port; + switch_inet_pton(AF_INET,ip, &message->data.serv_res_mess.serverIpAddr[0]); + switch_copy_string(message->data.serv_res_mess.server[0].serverName,ip,sizeof(message->data.serv_res_mess.server[0].serverName)); + + skinny_log_l_ffl(listener, file, func, line, SWITCH_LOG_DEBUG, + "Sending Server Request Response with IP (%s) and Port (%d)\n", ip, port); + + return skinny_send_reply(listener, message); +} + switch_status_t perform_send_start_media_transmission(listener_t *listener, const char *file, const char *func, int line, uint32_t conference_id, diff --git a/src/mod/endpoints/mod_skinny/skinny_protocol.h b/src/mod/endpoints/mod_skinny/skinny_protocol.h index dcea93f891..2fe1336aa6 100644 --- a/src/mod/endpoints/mod_skinny/skinny_protocol.h +++ b/src/mod/endpoints/mod_skinny/skinny_protocol.h @@ -36,6 +36,53 @@ /* mod_skinny.h should be loaded first */ #include "mod_skinny.h" +/*****************************************************************************/ +/* SKINNY TYPES */ +/*****************************************************************************/ +typedef enum { + SKINNY_CODEC_NONE = 0, + SKINNY_CODEC_NONSTANDARD = 1, + SKINNY_CODEC_ALAW_64K = 2, + SKINNY_CODEC_ALAW_56K = 3, + SKINNY_CODEC_ULAW_64K = 4, + SKINNY_CODEC_ULAW_56K = 5, + SKINNY_CODEC_G722_64K = 6, + SKINNY_CODEC_G722_56K = 7, + SKINNY_CODEC_G722_48K = 8, + SKINNY_CODEC_G723_1 = 9, + SKINNY_CODEC_G728 = 10, + SKINNY_CODEC_G729 = 11, + SKINNY_CODEC_G729A = 12, + SKINNY_CODEC_IS11172 = 13, + SKINNY_CODEC_IS13818 = 14, + SKINNY_CODEC_G729B = 15, + SKINNY_CODEC_G729AB = 16, + SKINNY_CODEC_GSM_FULL = 18, + SKINNY_CODEC_GSM_HALF = 19, + SKINNY_CODEC_GSM_EFULL = 20, + SKINNY_CODEC_WIDEBAND_256K = 25, + SKINNY_CODEC_DATA_64K = 32, + SKINNY_CODEC_DATA_56K = 33, + SKINNY_CODEC_G722_1_32K = 40, + SKINNY_CODEC_G722_1_24K = 41, + SKINNY_CODEC_GSM = 80, + SKINNY_CODEC_ACTIVEVOICE = 81, + SKINNY_CODEC_G726_32K = 82, + SKINNY_CODEC_G726_24K = 83, + SKINNY_CODEC_G726_16K = 84, + SKINNY_CODEC_G729B_BIS = 85, + SKINNY_CODEC_G729B_LOW = 86, + SKINNY_CODEC_H261 = 100, + SKINNY_CODEC_H263 = 101, + SKINNY_CODEC_VIDEO = 102, + SKINNY_CODEC_H264 = 103, + SKINNY_CODEC_T120 = 105, + SKINNY_CODEC_H224 = 106, + SKINNY_CODEC_RFC2833_DYNPAYLOAD = 257 +} skinny_codecs; + +char* skinny_codec2string(skinny_codecs skinnycodec); + /*****************************************************************************/ /* SKINNY MESSAGE DATA */ /*****************************************************************************/ @@ -145,6 +192,8 @@ struct PACKED capabilities_res_message { struct station_capabilities caps[SWITCH_MAX_CODECS]; }; +#define SERVER_REQ_MESSAGE 0x0012 + /* AlarmMessage */ #define ALARM_MESSAGE 0x0020 struct PACKED alarm_message { @@ -207,6 +256,147 @@ struct PACKED data_message { #define DEVICE_TO_USER_DATA_RESPONSE_MESSAGE 0x002F /* See struct PACKED data_message */ +#define UPDATE_CAPABILITIES_MESSAGE 0x0030 + +#define MAX_CUSTOM_PICTURES 6 +#define MAX_LAYOUT_WITH_SAME_SERVICE 5 +#define MAX_SERVICE_TYPE 4 +#define SKINNY_MAX_CAPABILITIES 18 /*!< max capabilities allowed in Cap response message */ +#define SKINNY_MAX_VIDEO_CAPABILITIES 10 +#define SKINNY_MAX_DATA_CAPABILITIES 5 +#define MAX_LEVEL_PREFERENCE 4 + +/*! + * \brief Picture Format Structure + */ +typedef struct { + uint32_t custom_picture_format_width; /*!< Picture Width */ + uint32_t custom_picture_format_height; /*!< Picture Height */ + uint32_t custom_picture_format_pixelAspectRatio; /*!< Picture Pixel Aspect Ratio */ + uint32_t custom_picture_format_pixelclockConversionCode; /*!< Picture Pixel Conversion Code */ + uint32_t custom_picture_format_pixelclockDivisor; /*!< Picture Pixel Divisor */ +} custom_picture_format_t; + + +/*! + * \brief Video Level Preference Structure + */ +typedef struct { + uint32_t transmitPreference; /*!< Transmit Preference */ + uint32_t format; /*!< Format / Codec */ + uint32_t maxBitRate; /*!< Maximum BitRate */ + uint32_t minBitRate; /*!< Minimum BitRate */ + uint32_t MPI; /*!< */ + uint32_t serviceNumber; /*!< Service Number */ +} levelPreference_t; /*!< Level Preference Structure */ + +/*! + * \brief Layout Config Structure (Update Capabilities Message Struct) + * \since 20080111 + */ +typedef struct { + uint32_t layout; /*!< Layout \todo what is layout? */ +} layoutConfig_t; /*!< Layout Config Structure */ + + +/*! + * \brief Service Resource Structure + */ +typedef struct { + uint32_t layoutCount; /*!< Layout Count */ + layoutConfig_t layout[MAX_LAYOUT_WITH_SAME_SERVICE]; /*!< Layout */ + uint32_t serviceNum; /*!< Service Number */ + uint32_t maxStreams; /*!< Maximum number of Streams */ + uint32_t maxConferences; /*!< Maximum number of Conferences */ + uint32_t activeConferenceOnRegistration; /*!< Active Conference On Registration */ +} serviceResource_t; + + + +/*! + * \brief Audio Capabilities Structure + */ +typedef struct { + skinny_codecs payload_capability; /*!< PayLoad Capability */ + uint32_t maxFramesPerPacket; /*!< Maximum Number of Frames per IP Packet */ + uint32_t unknown[2]; /*!< this are related to G.723 */ +} audioCap_t; + +/*! + * \brief Video Capabilities Structure + */ +typedef struct { + skinny_codecs payload_capability; /*!< PayLoad Capability */ + uint32_t transmitOreceive; /*!< Transmit of Receive */ + uint32_t levelPreferenceCount; /*!< Level of Preference Count */ + + levelPreference_t levelPreference[MAX_LEVEL_PREFERENCE]; /*!< Level Preference */ + +// uint32_t codec_options[2]; /*!< Codec Options */ + + union { + struct { + uint32_t unknown1; + uint32_t unknown2; + } h263; + struct { + uint32_t profile; /*!< H264 profile */ + uint32_t level; /*!< H264 level */ + } h264; + } codec_options; + + /** + * Codec options contains data specific for every codec + * + * Here is a list of known parameters per codec + // H.261 + uint32_t temporalSpatialTradeOffCapability; + uint32_t stillImageTransmission; + + // H.263 + uint32_t h263_capability_bitfield; + uint32_t annexNandWFutureUse; + + // Video + uint32_t modelNumber; + uint32_t bandwidth; + */ +} videoCap_t; /*!< Video Capabilities Structure */ + +/*! + * \brief Data Capabilities Structure + */ +typedef struct { + uint32_t payload_capability; /*!< Payload Capability */ + uint32_t transmitOrReceive; /*!< Transmit or Receive */ + uint32_t protocolDependentData; /*!< Protocol Dependent Data */ + uint32_t maxBitRate; /*!< Maximum BitRate */ +} dataCap_t; /*!< Data Capabilities Structure */ + + +struct PACKED update_capabilities_message { + uint32_t audio_cap_count; /*!< Audio Capability Count */ + uint32_t videoCapCount; /*!< Video Capability Count */ + uint32_t dataCapCount; /*!< Data Capability Count */ + uint32_t RTPPayloadFormat; /*!< RTP Payload Format */ + uint32_t custom_picture_formatCount; /*!< Custom Picture Format Count */ + + custom_picture_format_t custom_picture_format[MAX_CUSTOM_PICTURES]; /*!< Custom Picture Format */ + + uint32_t activeStreamsOnRegistration; /*!< Active Streams on Registration */ + uint32_t maxBW; /*!< Max BW ?? */ + + uint32_t serviceResourceCount; /*!< Service Resource Count */ + serviceResource_t serviceResource[MAX_SERVICE_TYPE]; /*!< Service Resource */ + + audioCap_t audioCaps[SKINNY_MAX_CAPABILITIES]; /*!< Audio Capabilities */ + videoCap_t videoCaps[SKINNY_MAX_VIDEO_CAPABILITIES]; /*!< Video Capabilities */ + dataCap_t dataCaps[SKINNY_MAX_DATA_CAPABILITIES]; /*!< Data Capabilities */ + + uint32_t unknown; /*!< Unknown */ +}; + + /* ServiceUrlStatReqMessage */ #define SERVICE_URL_STAT_REQ_MESSAGE 0x0033 struct PACKED service_url_stat_req_message { @@ -442,6 +632,23 @@ struct PACKED register_reject_message { char error[33]; }; +#define SERVER_RESPONSE_MESSAGE 0x009E +#define ServerMaxNameSize 48 +#define StationMaxServers 5 +/*! + * \brief Station Identifier Structure + */ +typedef struct { + char serverName[ServerMaxNameSize]; /*!< Server Name */ +} ServerIdentifier; + +struct PACKED server_response_message { + ServerIdentifier server[StationMaxServers]; /*!< Server Identifier */ + uint32_t serverListenPort[StationMaxServers]; /*!< Server is Listening on Port */ + uint32_t serverIpAddr[StationMaxServers]; /*!< Server IP Port */ +}; /*!< Server Result Message Structure */ + + /* ResetMessage */ #define RESET_MESSAGE 0x009F struct PACKED reset_message { @@ -670,6 +877,7 @@ union skinny_data { /* no data for CAPABILITIES_REQ_MESSAGE */ struct register_reject_message reg_rej; struct reset_message reset; + struct server_response_message serv_res_mess; /* no data for KEEP_ALIVE_ACK_MESSAGE */ struct open_receive_channel_message open_receive_channel; struct close_receive_channel_message close_receive_channel; @@ -690,6 +898,7 @@ union skinny_data { /* see field "extended_data" for USER_TO_DEVICE_DATA_VERSION1_MESSAGE */ struct dialed_phone_book_ack_message dialed_phone_book_ack; + struct update_capabilities_message upd_cap; struct data_message data; struct extended_data_message extended_data; @@ -702,7 +911,7 @@ union skinny_data { #endif /* - * header is length+reserved + * header is length+version * body is type+data * length is length of body */ @@ -719,47 +928,7 @@ struct PACKED skinny_message { typedef struct skinny_message skinny_message_t; -/*****************************************************************************/ -/* SKINNY TYPES */ -/*****************************************************************************/ -enum skinny_codecs { - SKINNY_CODEC_ALAW_64K = 2, - SKINNY_CODEC_ALAW_56K = 3, - SKINNY_CODEC_ULAW_64K = 4, - SKINNY_CODEC_ULAW_56K = 5, - SKINNY_CODEC_G722_64K = 6, - SKINNY_CODEC_G722_56K = 7, - SKINNY_CODEC_G722_48K = 8, - SKINNY_CODEC_G723_1 = 9, - SKINNY_CODEC_G728 = 10, - SKINNY_CODEC_G729 = 11, - SKINNY_CODEC_G729A = 12, - SKINNY_CODEC_IS11172 = 13, - SKINNY_CODEC_IS13818 = 14, - SKINNY_CODEC_G729B = 15, - SKINNY_CODEC_G729AB = 16, - SKINNY_CODEC_GSM_FULL = 18, - SKINNY_CODEC_GSM_HALF = 19, - SKINNY_CODEC_GSM_EFULL = 20, - SKINNY_CODEC_WIDEBAND_256K = 25, - SKINNY_CODEC_DATA_64K = 32, - SKINNY_CODEC_DATA_56K = 33, - SKINNY_CODEC_GSM = 80, - SKINNY_CODEC_ACTIVEVOICE = 81, - SKINNY_CODEC_G726_32K = 82, - SKINNY_CODEC_G726_24K = 83, - SKINNY_CODEC_G726_16K = 84, - SKINNY_CODEC_G729B_BIS = 85, - SKINNY_CODEC_G729B_LOW = 86, - SKINNY_CODEC_H261 = 100, - SKINNY_CODEC_H263 = 101, - SKINNY_CODEC_VIDEO = 102, - SKINNY_CODEC_T120 = 105, - SKINNY_CODEC_H224 = 106, - SKINNY_CODEC_RFC2833_DYNPAYLOAD = 257 -}; -char* skinny_codec2string(enum skinny_codecs skinnycodec); /*****************************************************************************/ /* SKINNY FUNCTIONS */ @@ -851,6 +1020,11 @@ switch_status_t perform_send_set_speaker_mode(listener_t *listener, uint32_t mode); #define send_set_speaker_mode(listener, ...) perform_send_set_speaker_mode(listener, __FILE__, __SWITCH_FUNC__, __LINE__, __VA_ARGS__) +switch_status_t perform_send_srvreq_response(listener_t *listener, + const char *file, const char *func, int line, + char *ip, uint32_t port); +#define send_srvreq_response(listener, ...) perform_send_srvreq_response(listener, __FILE__, __SWITCH_FUNC__, __LINE__, __VA_ARGS__) + switch_status_t perform_send_start_media_transmission(listener_t *listener, const char *file, const char *func, int line, uint32_t conference_id, diff --git a/src/mod/endpoints/mod_skinny/skinny_server.c b/src/mod/endpoints/mod_skinny/skinny_server.c index 2b7031186d..bc06b9bdb3 100644 --- a/src/mod/endpoints/mod_skinny/skinny_server.c +++ b/src/mod/endpoints/mod_skinny/skinny_server.c @@ -2184,6 +2184,79 @@ switch_status_t skinny_handle_accessory_status_message(listener_t *listener, ski return SWITCH_STATUS_SUCCESS; } +switch_status_t skinny_handle_updatecapabilities(listener_t *listener, skinny_message_t *request) +{ + char *sql; + skinny_profile_t *profile; + + uint32_t i = 0; + uint32_t n = 0; + char *codec_order[SWITCH_MAX_CODECS]; + char *codec_string; + + size_t string_len, string_pos, pos; + + switch_assert(listener->profile); + switch_assert(listener->device_name); + + profile = listener->profile; + + skinny_check_data_length(request, sizeof(request->data.upd_cap.audio_cap_count)); + + n = request->data.upd_cap.audio_cap_count; + if (n > SWITCH_MAX_CODECS) { + n = SWITCH_MAX_CODECS; + } + string_len = -1; + + skinny_check_data_length(request, sizeof(request->data.upd_cap.audio_cap_count) + n * sizeof(request->data.upd_cap.audioCaps[0])); + + for (i = 0; i < n; i++) { + char *codec = skinny_codec2string(request->data.upd_cap.audioCaps[i].payload_capability); + codec_order[i] = codec; + string_len += strlen(codec)+1; + } + i = 0; + pos = 0; + codec_string = switch_core_alloc(listener->pool, string_len+1); + for (string_pos = 0; string_pos < string_len; string_pos++) { + char *codec = codec_order[i]; + switch_assert(i < n); + if(pos == strlen(codec)) { + codec_string[string_pos] = ','; + i++; + pos = 0; + } else { + codec_string[string_pos] = codec[pos++]; + } + } + codec_string[string_len] = '\0'; + if ((sql = switch_mprintf( + "UPDATE skinny_devices SET codec_string='%s' WHERE name='%s'", + codec_string, + listener->device_name + ))) { + skinny_execute_sql(profile, sql, profile->sql_mutex); + switch_safe_free(sql); + } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, + "Codecs %s supported.\n", codec_string); + return SWITCH_STATUS_SUCCESS; +} + + +switch_status_t skinny_handle_server_req_message(listener_t *listener, skinny_message_t *request) +{ + skinny_profile_t *profile; + + profile = listener->profile; + + skinny_log_l(listener, SWITCH_LOG_INFO, "Received Server Request Message (length=%d).\n", request->length); + + send_srvreq_response(listener, profile->ip, profile->port); + return SWITCH_STATUS_SUCCESS; +} + switch_status_t skinny_handle_xml_alarm(listener_t *listener, skinny_message_t *request) { switch_event_t *event = NULL; @@ -2209,7 +2282,7 @@ switch_status_t skinny_handle_request(listener_t *listener, skinny_message_t *re skinny_log_l(listener, SWITCH_LOG_DEBUG, "Received %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length); } - if(zstr(listener->device_name) && request->type != REGISTER_MESSAGE && request->type != ALARM_MESSAGE && request->type != XML_ALARM_MESSAGE) { + if(zstr(listener->device_name) && request->type != REGISTER_MESSAGE && request->type != ALARM_MESSAGE && request->type != XML_ALARM_MESSAGE && request->type != KEEP_ALIVE_MESSAGE) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Device should send a register message first. Received %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length); return SWITCH_STATUS_FALSE; @@ -2281,6 +2354,10 @@ switch_status_t skinny_handle_request(listener_t *listener, skinny_message_t *re return skinny_handle_accessory_status_message(listener, request); case XML_ALARM_MESSAGE: return skinny_handle_xml_alarm(listener, request); + case UPDATE_CAPABILITIES_MESSAGE: + return skinny_handle_updatecapabilities(listener, request); + case SERVER_REQ_MESSAGE: + return skinny_handle_server_req_message(listener, request); default: switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled %s (type=%x,length=%d).\n", skinny_message_type2str(request->type), request->type, request->length); diff --git a/src/mod/endpoints/mod_skinny/skinny_tables.c b/src/mod/endpoints/mod_skinny/skinny_tables.c index 62c97aacf5..9f04f8cd63 100644 --- a/src/mod/endpoints/mod_skinny/skinny_tables.c +++ b/src/mod/endpoints/mod_skinny/skinny_tables.c @@ -61,6 +61,7 @@ struct skinny_table SKINNY_MESSAGE_TYPES[] = { {REGISTER_AVAILABLE_LINES_MESSAGE, "RegisterAvailableLinesMessage"}, {DEVICE_TO_USER_DATA_MESSAGE, "DeviceToUserDataMessage"}, {DEVICE_TO_USER_DATA_RESPONSE_MESSAGE, "DeviceToUserDataResponseMessage"}, + {UPDATE_CAPABILITIES_MESSAGE, "DeviceUpdateCapabilities"}, {SERVICE_URL_STAT_REQ_MESSAGE, "ServiceUrlStatReqMessage"}, {FEATURE_STAT_REQ_MESSAGE, "FeatureStatReqMessage"}, {DEVICE_TO_USER_DATA_VERSION1_MESSAGE, "DeviceToUserDataVersion1Message"}, @@ -84,7 +85,9 @@ struct skinny_table SKINNY_MESSAGE_TYPES[] = { {BUTTON_TEMPLATE_RES_MESSAGE, "ButtonTemplateResMessage"}, {VERSION_MESSAGE, "VersionMessage"}, {CAPABILITIES_REQ_MESSAGE, "CapabilitiesReqMessage"}, + {SERVER_REQ_MESSAGE, "Server Request Message"}, {REGISTER_REJECT_MESSAGE, "RegisterRejectMessage"}, + {SERVER_RESPONSE_MESSAGE, "ServerResponseMessage"}, {RESET_MESSAGE, "ResetMessage"}, {KEEP_ALIVE_ACK_MESSAGE, "KeepAliveAckMessage"}, {OPEN_RECEIVE_CHANNEL_MESSAGE, "OpenReceiveChannelMessage"}, @@ -115,18 +118,59 @@ SKINNY_DECLARE_STR2ID(skinny_str2message_type, SKINNY_MESSAGE_TYPES, -1) {1, "Cisco 30 SP+"}, {2, "Cisco 12 SP+"}, {3, "Cisco 12 SP"}, - {4, "Cisco 12"}, + {4, "Cisco 12 S"}, {5, "Cisco 30 VIP"}, - {6, "Cisco IP Phone 7910"}, - {7, "Cisco IP Phone 7960"}, - {8, "Cisco IP Phone 7940"}, - {9, "Cisco IP Phone 7935"}, + {6, "Cisco 7910"}, + {7, "Cisco 7960"}, + {8, "Cisco 7940"}, + {9, "Cisco 7935"}, + {10, "Cisco VGC Phone"}, + {11, "Cisco VGC Virtual Phone"}, {12, "Cisco ATA 186"}, - {365, "Cisco IP Phone CP-7921G"}, - {404, "Cisco IP Phone CP-7962G"}, - {436, "Cisco IP Phone CP-7965G"}, - {30018, "Cisco IP Phone CP-7961G"}, - {30019, "Cisco IP Phone 7936"}, + {30, "Cisco Analog Access"}, + {40, "Cisco Digital Access"}, + {42, "Cisco Digital Access+"}, + {43, "Cisco Digital Access WS-X6608"}, + {47, "Cisco Analog Access WS-X6624"}, + {51, "Cisco Conference Bridge WS-X6608"}, + {61, "Cisco H.323 Phone"}, + {100, "Cisco Load Simulator"}, + {111, "Cisco Media Termination Point Hardware"}, + {115, "Cisco 7941"}, + {115, "Cisco CP-7941G"}, + {119, "Cisco 7971"}, + {120, "Cisco MGCP Station"}, + {121, "Cisco MGCP Trunk"}, + {124, "Cisco 7914 14-Button Line Expansion Module"}, + {302, "Cisco 7985"}, + {307, "Cisco 7911"}, + {308, "Cisco 7961G-GE"}, + {309, "Cisco 7941G-GE"}, + {335, "Cisco Motorola CN622"}, + {348, "Cisco 7931"}, + {358, "Cisco Unified Personal Communicator"}, + {365, "Cisco 7921"}, + {369, "Cisco 7906"}, + {375, "Cisco TelePresence"}, + {404, "Cisco 7962"}, + {412, "Cisco 3951"}, + {431, "Cisco 7937"}, + {434, "Cisco 7942"}, + {435, "Cisco 7945"}, + {436, "Cisco 7965"}, + {437, "Cisco 7975"}, + {446, "Cisco 3911"}, + {20000, "Cisco 7905"}, + {30002, "Cisco 7920"}, + {30006, "Cisco 7970"}, + {30007, "Cisco 7912"}, + {30008, "Cisco 7902"}, + {30018, "Cisco 7961"}, + {30019, "Cisco 7936"}, + {30027, "Cisco Analog Phone"}, + {30028, "Cisco ISDN BRI Phone"}, + {30032, "Cisco SCCP gateway virtual phone"}, + {30035, "Cisco IP-STE"}, {0, NULL} }; SKINNY_DECLARE_ID2STR(skinny_device_type2str, SKINNY_DEVICE_TYPES, "UnknownDeviceType") diff --git a/src/mod/endpoints/mod_skinny/skinny_tables.h b/src/mod/endpoints/mod_skinny/skinny_tables.h index a35e4be18d..1e35a7d208 100644 --- a/src/mod/endpoints/mod_skinny/skinny_tables.h +++ b/src/mod/endpoints/mod_skinny/skinny_tables.h @@ -87,12 +87,12 @@ if (my_matches) {\ } -extern struct skinny_table SKINNY_MESSAGE_TYPES[72]; +extern struct skinny_table SKINNY_MESSAGE_TYPES[75]; const char *skinny_message_type2str(uint32_t id); uint32_t skinny_str2message_type(const char *str); #define SKINNY_PUSH_MESSAGE_TYPES SKINNY_DECLARE_PUSH_MATCH(SKINNY_MESSAGE_TYPES) -extern struct skinny_table SKINNY_DEVICE_TYPES[16]; +extern struct skinny_table SKINNY_DEVICE_TYPES[75]; const char *skinny_device_type2str(uint32_t id); uint32_t skinny_str2device_type(const char *str); #define SKINNY_PUSH_DEVICE_TYPES SKINNY_DECLARE_PUSH_MATCH(SKINNY_DEVICE_TYPES) diff --git a/src/switch_core_media.c b/src/switch_core_media.c index a52c9e08e7..9e21e458eb 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -126,6 +126,7 @@ typedef struct switch_rtp_engine_s { uint32_t max_missed_packets; uint32_t max_missed_hold_packets; uint32_t ssrc; + uint32_t remote_ssrc; switch_port_t remote_rtcp_port; switch_rtp_bug_flag_t rtp_bugs; @@ -1985,6 +1986,8 @@ static void check_ice(switch_media_handle_t *smh, switch_media_type_t type, sdp_ generate_local_fingerprint(smh, type); switch_channel_set_flag(smh->session->channel, CF_DTLS); + } else if (!engine->remote_ssrc && !strcasecmp(attr->a_name, "ssrc") && attr->a_value) { + engine->remote_ssrc = (uint32_t) atol(attr->a_value); #ifdef RTCP_MUX } else if (!strcasecmp(attr->a_name, "rtcp-mux")) { engine->rtcp_mux = SWITCH_TRUE; @@ -2294,7 +2297,6 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s codec_array = smh->codecs; total_codecs = smh->mparams->num_codecs; - if (!(parser = sdp_parse(NULL, r_sdp, (int) strlen(r_sdp), 0))) { return 0; } @@ -2459,9 +2461,12 @@ SWITCH_DECLARE(uint8_t) switch_core_media_negotiate_sdp(switch_core_session_t *s got_webrtc++; switch_core_session_set_ice(session); } - + if (m->m_proto_name && !strcasecmp(m->m_proto_name, "UDP/TLS/RTP/SAVPF")) { switch_channel_set_flag(session->channel, CF_WEBRTC_MOZ); + printf("PRICK FACE 1\n"); + } else { + printf("PRICK FACE 2 [%s]\n", m->m_proto_name); } if (m->m_proto == sdp_proto_srtp || m->m_proto == sdp_proto_extended_srtp) { @@ -3790,7 +3795,8 @@ static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, voi switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s Video thread started\n", switch_channel_get_name(session->channel)); switch_core_session_refresh_video(session); - + switch_channel_set_flag(channel, CF_VIDEO_ECHO); + while (switch_channel_up_nosig(channel)) { if (switch_channel_test_flag(channel, CF_VIDEO_PASSIVE)) { @@ -3812,7 +3818,6 @@ static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, voi status = switch_core_session_read_video_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0); - if (!SWITCH_READ_ACCEPTABLE(status)) { switch_cond_next(); continue; @@ -3828,7 +3833,9 @@ static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, voi continue; } - switch_core_session_write_video_frame(session, read_frame, SWITCH_IO_FLAG_NONE, 0); + if (switch_channel_test_flag(channel, CF_VIDEO_ECHO)) { + switch_core_session_write_video_frame(session, read_frame, SWITCH_IO_FLAG_NONE, 0); + } } @@ -4092,6 +4099,9 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi switch_rtp_set_ssrc(a_engine->rtp_session, a_engine->ssrc); } + if (a_engine->remote_ssrc) { + switch_rtp_set_remote_ssrc(a_engine->rtp_session, a_engine->remote_ssrc); + } switch_channel_set_flag(session->channel, CF_FS_RTP); @@ -4552,6 +4562,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi switch_rtp_set_ssrc(v_engine->rtp_session, v_engine->ssrc); } + if (v_engine->remote_ssrc) { + switch_rtp_set_remote_ssrc(v_engine->rtp_session, v_engine->remote_ssrc); + } + if (v_engine->ice_in.cands[v_engine->ice_in.chosen[0]][0].ready) { gen_ice(session, SWITCH_MEDIA_TYPE_VIDEO, NULL, 0); @@ -5125,7 +5139,7 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess switch_rtp_engine_t *a_engine, *v_engine; switch_media_handle_t *smh; ice_t *ice_out; - + int vp8 = 0; switch_assert(session); @@ -5629,6 +5643,10 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess if (v_engine->codec_params.rm_encoding) { const char *of; + + if (!strcasecmp(v_engine->codec_params.rm_encoding, "VP8")) { + vp8 = v_engine->codec_params.pt; + } rate = v_engine->codec_params.rm_rate; switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtpmap:%d %s/%ld\n", @@ -5694,6 +5712,10 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess channels = get_channels(imp); + if (!strcasecmp(imp->iananame, "VP8")) { + vp8 = ianacode; + } + if (channels > 1) { switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtpmap:%d %s/%d/%d\n", ianacode, imp->iananame, imp->samples_per_second, channels); @@ -5733,7 +5755,7 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess } - if (smh->mparams->rtcp_audio_interval_msec) { + if (smh->mparams->rtcp_video_interval_msec) { if (v_engine->rtcp_mux > 0) { switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtcp-mux\n"); switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=rtcp:%d IN %s %s\n", v_port, family, ip); @@ -5751,7 +5773,9 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess uint32_t c2 = (2^24)*126 + (2^8)*65535 + (2^0)*(256 - 2); uint32_t c3 = (2^24)*126 + (2^8)*65534 + (2^0)*(256 - 1); uint32_t c4 = (2^24)*126 + (2^8)*65534 + (2^0)*(256 - 2); - + const char *vbw; + int bw = 256; + tmp1[10] = '\0'; tmp2[10] = '\0'; switch_stun_random_string(tmp1, 10, "0123456789"); @@ -5760,6 +5784,21 @@ SWITCH_DECLARE(void) switch_core_media_gen_local_sdp(switch_core_session_t *sess ice_out = &v_engine->ice_out; + if ((vbw = switch_channel_get_variable(smh->session->channel, "rtp_video_max_bandwidth"))) { + int v = atoi(vbw); + bw = v; + } + + if (bw > 0) { + switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "b=AS:%d\n", bw); + } + + + if (vp8) { + switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), + "a=rtcp-fb:%d ccm fir\n", vp8); + } + switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=ssrc:%u cname:%s\n", v_engine->ssrc, smh->cname); switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=ssrc:%u msid:%s v0\n", v_engine->ssrc, smh->msid); switch_snprintf(buf + strlen(buf), SDPBUFLEN - strlen(buf), "a=ssrc:%u mslabel:%s\n", v_engine->ssrc, smh->msid); diff --git a/src/switch_core_session.c b/src/switch_core_session.c index 6e97391f21..8fc40f7d9d 100644 --- a/src/switch_core_session.c +++ b/src/switch_core_session.c @@ -2567,6 +2567,10 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_execute_application_get_flag *flags = application_interface->flags; } + if (!switch_test_flag(application_interface, SAF_SUPPORT_NOMEDIA) && (switch_channel_test_flag(session->channel, CF_VIDEO))) { + switch_core_session_refresh_video(session); + } + if (switch_channel_test_flag(session->channel, CF_PROXY_MODE) && !switch_test_flag(application_interface, SAF_SUPPORT_NOMEDIA)) { switch_ivr_media(session->uuid_str, SMF_NONE); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Application %s Requires media on channel %s!\n", diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 45ab478f96..f1ceb1d5c3 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -34,6 +34,8 @@ //#define RTP_DEBUG_WRITE_DELTA //#define DEBUG_MISSED_SEQ +#define FIR_COUNTDOWN 100 + #include #ifndef _MSC_VER #include @@ -116,7 +118,6 @@ typedef struct { uint8_t r3; } rtcp_fir_t; - #ifdef _MSC_VER #pragma pack(push, r1, 1) #endif @@ -264,6 +265,13 @@ static int dtls_state_dummy(switch_rtp_t *rtp_session, switch_dtls_t *dtls); dtls_state_handler_t dtls_states[DS_INVALID] = {dtls_state_handshake, dtls_state_setup, dtls_state_ready, dtls_state_dummy}; +typedef struct ts_normalize_s { + uint32_t last_ssrc; + uint32_t last_frame; + uint32_t ts; + uint32_t delta; + uint8_t m; +} ts_normalize_t; struct switch_rtp { /* @@ -281,6 +289,8 @@ struct switch_rtp { rtcp_msg_t rtcp_send_msg; rtcp_ext_msg_t rtcp_ext_send_msg; uint8_t fir_seq; + uint16_t fir_countdown; + ts_normalize_t ts_norm; switch_sockaddr_t *remote_addr, *rtcp_remote_addr; rtp_msg_t recv_msg; rtcp_msg_t rtcp_recv_msg; @@ -307,6 +317,7 @@ struct switch_rtp { uint16_t seq; uint32_t ssrc; + uint32_t remote_ssrc; int8_t sending_dtmf; uint8_t need_mark; switch_payload_t payload; @@ -698,10 +709,10 @@ static switch_status_t ice_out(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice) elapsed = (unsigned int) ((switch_micro_time_now() - rtp_session->last_stun) / 1000); if (elapsed > 30000) { - - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "No stun for a long time (PUNT!)\n"); - status = SWITCH_STATUS_GENERR; - goto end; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "No stun for a long time!\n"); + rtp_session->last_stun = switch_micro_time_now(); + //status = SWITCH_STATUS_GENERR; + //goto end; } } @@ -743,7 +754,7 @@ static switch_status_t ice_out(switch_rtp_t *rtp_session, switch_rtp_ice_t *ice) ice->sending = 3; - end: + // end: READ_DEC(rtp_session); return status; @@ -1329,20 +1340,31 @@ static void send_fir(switch_rtp_t *rtp_session) return; } + if (rtp_session->remote_ssrc == 0) { + rtp_session->remote_ssrc = rtp_session->stats.rtcp.peer_ssrc; + } + + if (rtp_session->remote_ssrc == 0) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG, "Peer ssrc not known yet for FIR\n"); + return; + } + if (rtp_session->rtcp_sock_output && rtp_session->flags[SWITCH_RTP_FLAG_ENABLE_RTCP]) { rtcp_fir_t *fir = (rtcp_fir_t *) rtp_session->rtcp_ext_send_msg.body; switch_size_t rtcp_bytes; - + rtp_session->rtcp_ext_send_msg.header.version = 2; rtp_session->rtcp_ext_send_msg.header.p = 0; rtp_session->rtcp_ext_send_msg.header.fmt = 4; rtp_session->rtcp_ext_send_msg.header.pt = 206; rtp_session->rtcp_ext_send_msg.header.send_ssrc = htonl(rtp_session->ssrc); - rtp_session->rtcp_ext_send_msg.header.recv_ssrc = htonl(rtp_session->stats.rtcp.peer_ssrc); + rtp_session->rtcp_ext_send_msg.header.recv_ssrc = 0;//htonl(rtp_session->stats.rtcp.peer_ssrc); - fir->ssrc = htonl(rtp_session->stats.rtcp.peer_ssrc); - fir->seq = (uint8_t) htonl(rtp_session->fir_seq++); + //fir->ssrc = htonl(rtp_session->stats.rtcp.peer_ssrc); + fir->ssrc = htonl(rtp_session->remote_ssrc); + fir->seq = ++rtp_session->fir_seq; + fir->r1 = fir->r2 = fir->r3 = 0; switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG1, "Sending RTCP FIR %d\n", rtp_session->fir_seq); @@ -1409,6 +1431,92 @@ static void send_fir(switch_rtp_t *rtp_session) return; } + +#if 0 +static void send_pli(switch_rtp_t *rtp_session) +{ + + if (!rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && rtp_session->ice.ice_user) { + return; + } + + if (rtp_session->rtcp_sock_output && rtp_session->flags[SWITCH_RTP_FLAG_ENABLE_RTCP]) { + switch_size_t rtcp_bytes; + + rtp_session->rtcp_ext_send_msg.header.version = 2; + rtp_session->rtcp_ext_send_msg.header.p = 0; + rtp_session->rtcp_ext_send_msg.header.fmt = 1; + rtp_session->rtcp_ext_send_msg.header.pt = 206; + + rtp_session->rtcp_ext_send_msg.header.send_ssrc = htonl(rtp_session->ssrc); + rtp_session->rtcp_ext_send_msg.header.recv_ssrc = 0; + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG1, "Sending RTCP PLI\n"); + + rtcp_bytes = sizeof(switch_rtcp_ext_hdr_t); + rtp_session->rtcp_ext_send_msg.header.length = htons((u_short)(rtcp_bytes / 4) - 1); + + +#ifdef ENABLE_SRTP + if (rtp_session->flags[SWITCH_RTP_FLAG_SECURE_SEND]) { + int sbytes = (int) rtcp_bytes; + int stat = srtp_protect_rtcp(rtp_session->send_ctx[rtp_session->srtp_idx_rtcp], &rtp_session->rtcp_ext_send_msg.header, &sbytes); + + if (stat) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_ERROR, "Error: SRTP RTCP protection failed with code %d\n", stat); + goto end; + } else { + rtcp_bytes = sbytes; + } + + } +#endif + +#ifdef ENABLE_ZRTP + /* ZRTP Send */ + if (zrtp_on && !rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA]) { + unsigned int sbytes = (int) rtcp_bytes; + zrtp_status_t stat = zrtp_status_fail; + + stat = zrtp_process_rtcp(rtp_session->zrtp_stream, (void *) &rtp_session->rtcp_ext_send_msg, &sbytes); + + switch (stat) { + case zrtp_status_ok: + break; + case zrtp_status_drop: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error: zRTP protection drop with code %d\n", stat); + goto end; + break; + case zrtp_status_fail: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error: zRTP protection fail with code %d\n", stat); + break; + default: + break; + } + + rtcp_bytes = sbytes; + } +#endif + +#ifdef DEBUG_EXTRA + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_CRIT, "%s SEND %s RTCP %ld\n", + switch_core_session_get_name(rtp_session->session), + rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] ? "video" : "audio", rtcp_bytes); +#endif + if (switch_socket_sendto(rtp_session->rtcp_sock_output, rtp_session->rtcp_remote_addr, 0, (void *)&rtp_session->rtcp_ext_send_msg, &rtcp_bytes ) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG,"RTCP packet not written\n"); + } else { + rtp_session->stats.inbound.period_packet_count = 0; + } + } + + + end: + + return; +} +#endif + static int check_rtcp_and_ice(switch_rtp_t *rtp_session) { int ret = 0; @@ -1442,10 +1550,6 @@ static int check_rtcp_and_ice(switch_rtp_t *rtp_session) rtcp_ok = 0; } - //if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) { - // rtcp_ok = 0; - //} - if (rtp_session->rtcp_sock_output && rtcp_ok && rtp_session->flags[SWITCH_RTP_FLAG_ENABLE_RTCP] && !rtp_session->flags[SWITCH_RTP_FLAG_RTCP_PASSTHRU]) { struct switch_rtcp_senderinfo *sr = (struct switch_rtcp_senderinfo*) rtp_session->rtcp_send_msg.body; const char* str_cname=NULL; @@ -2123,6 +2227,10 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_remote_address(switch_rtp_t *rtp_ status = enable_remote_rtcp_socket(rtp_session, err); } + if (rtp_session->flags[SWITCH_RTP_FLAG_ENABLE_RTCP] && rtp_session->flags[SWITCH_RTP_FLAG_RTCP_MUX]) { + rtp_session->rtcp_remote_addr = rtp_session->remote_addr; + } + switch_mutex_unlock(rtp_session->write_mutex); return status; @@ -2224,7 +2332,7 @@ static int dtls_state_ready(switch_rtp_t *rtp_session, switch_dtls_t *dtls) if (dtls->new_state) { if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) { switch_core_session_t *other_session; - send_fir(rtp_session); + rtp_session->fir_countdown = FIR_COUNTDOWN; if (rtp_session->session && switch_core_session_get_partner(rtp_session->session, &other_session) == SWITCH_STATUS_SUCCESS) { switch_core_session_refresh_video(other_session); @@ -2682,6 +2790,13 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_ssrc(switch_rtp_t *rtp_session, u return SWITCH_STATUS_SUCCESS; } +SWITCH_DECLARE(switch_status_t) switch_rtp_set_remote_ssrc(switch_rtp_t *rtp_session, uint32_t ssrc) +{ + rtp_session->remote_ssrc = ssrc; + + return SWITCH_STATUS_SUCCESS; +} + SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session, switch_payload_t payload, uint32_t samples_per_interval, @@ -2781,6 +2896,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_NOBLOCK); } + if (channel) { switch_channel_set_private(channel, "__rtcp_audio_rtp_session", rtp_session); } @@ -3221,7 +3337,10 @@ SWITCH_DECLARE(void) switch_rtp_flush(switch_rtp_t *rtp_session) SWITCH_DECLARE(void) switch_rtp_video_refresh(switch_rtp_t *rtp_session) { if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && rtp_session->ice.ice_user) { - send_fir(rtp_session); + if (!rtp_session->fir_countdown) { + //send_fir(rtp_session); + rtp_session->fir_countdown = FIR_COUNTDOWN; + } } } @@ -3827,6 +3946,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t uint32_t ts = 0; unsigned char *b = NULL; int sync = 0; + switch_time_t now; switch_assert(bytes); more: @@ -3896,15 +4016,14 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t } } - if (status == SWITCH_STATUS_SUCCESS && *bytes) { if (rtp_session->flags[SWITCH_RTP_FLAG_RTCP_MUX]) { *flags &= ~SFF_RTCP; - if (rtp_session->recv_msg.header.pt != rtp_session->rpayload && (!rtp_session->recv_te || - rtp_session->recv_msg.header.pt != rtp_session->recv_te) && + if (rtp_session->recv_msg.header.pt != rtp_session->rpayload && + (!rtp_session->recv_te || rtp_session->recv_msg.header.pt != rtp_session->recv_te) && (!rtp_session->cng_pt || rtp_session->recv_msg.header.pt != rtp_session->cng_pt) && - rtp_session->rtcp_recv_msg_p->header.version == 2 && rtp_session->rtcp_recv_msg_p->header.type > 199 && - rtp_session->rtcp_recv_msg_p->header.type < 208) { //rtcp muxed + rtp_session->rtcp_recv_msg_p->header.version == 2 && + rtp_session->rtcp_recv_msg_p->header.type > 199 && rtp_session->rtcp_recv_msg_p->header.type < 208) { //rtcp muxed *flags |= SFF_RTCP; return SWITCH_STATUS_SUCCESS; } @@ -3952,11 +4071,13 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t udptl: - ts = ntohl(rtp_session->recv_msg.header.ts); + ts = 0; rtp_session->recv_msg.ebody = NULL; + now = switch_micro_time_now(); if (*bytes) { uint16_t seq = ntohs((uint16_t) rtp_session->recv_msg.header.seq); + ts = ntohl(rtp_session->recv_msg.header.ts); if (!rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] && !rtp_session->flags[SWITCH_RTP_FLAG_UDPTL] && rtp_session->recv_msg.header.version == 2 && rtp_session->recv_msg.header.x) { /* header extensions */ @@ -3981,40 +4102,47 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t if (num_missed == 1) { /* We missed one packet */ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missed one RTP frame with sequence [%d]%s. Time since last read [%ld]\n", rtp_session->last_seq+1, (flushed_packets_diff == 1) ? " (flushed by FS)" : " (missed)", - rtp_session->last_read_time ? switch_micro_time_now()-rtp_session->last_read_time : 0); + rtp_session->last_read_time ? now-rtp_session->last_read_time : 0); } else { /* We missed multiple packets */ if (flushed_packets_diff == 0) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missed %ld RTP frames from sequence [%d] to [%d] (missed). Time since last read [%ld]\n", num_missed, rtp_session->last_seq+1, seq-1, - rtp_session->last_read_time ? switch_micro_time_now()-rtp_session->last_read_time : 0); + rtp_session->last_read_time ? now-rtp_session->last_read_time : 0); } else if (flushed_packets_diff == num_missed) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missed %ld RTP frames from sequence [%d] to [%d] (flushed by FS). Time since last read [%ld]\n", num_missed, rtp_session->last_seq+1, seq-1, - rtp_session->last_read_time ? switch_micro_time_now()-rtp_session->last_read_time : 0); + rtp_session->last_read_time ? now-rtp_session->last_read_time : 0); } else if (num_missed > flushed_packets_diff) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missed %ld RTP frames from sequence [%d] to [%d] (%ld packets flushed by FS, %ld packets missed)." " Time since last read [%ld]\n", num_missed, rtp_session->last_seq+1, seq-1, flushed_packets_diff, num_missed-flushed_packets_diff, - rtp_session->last_read_time ? switch_micro_time_now()-rtp_session->last_read_time : 0); + rtp_session->last_read_time ? now-rtp_session->last_read_time : 0); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missed %ld RTP frames from sequence [%d] to [%d] (%ld packets flushed by FS). Time since last read [%ld]\n", num_missed, rtp_session->last_seq+1, seq-1, - flushed_packets_diff, rtp_session->last_read_time ? switch_micro_time_now()-rtp_session->last_read_time : 0); + flushed_packets_diff, rtp_session->last_read_time ? now-rtp_session->last_read_time : 0); } } } #endif rtp_session->last_seq = seq; - } + - rtp_session->last_flush_packet_count = rtp_session->stats.inbound.flush_packet_count; - rtp_session->last_read_time = switch_micro_time_now(); + rtp_session->last_flush_packet_count = rtp_session->stats.inbound.flush_packet_count; + + + if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && now - rtp_session->last_read_time > 500000) { + switch_rtp_video_refresh(rtp_session); + } + + rtp_session->last_read_time = now; + } if (!rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] && !rtp_session->flags[SWITCH_RTP_FLAG_UDPTL] && !rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && *bytes && (!rtp_session->recv_te || rtp_session->recv_msg.header.pt != rtp_session->recv_te) && @@ -4121,7 +4249,17 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t } - rtp_session->last_read_ts = ts; + if (*bytes && rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) { + unsigned int diff = ts - rtp_session->last_read_ts; + + if (abs(diff) > 10000) { + switch_rtp_video_refresh(rtp_session); + } + } + + if (ts) { + rtp_session->last_read_ts = ts; + } if (rtp_session->flags[SWITCH_RTP_FLAG_BYTESWAP] && rtp_session->recv_msg.header.pt == rtp_session->rpayload) { switch_swap_linear((int16_t *)RTP_BODY(rtp_session), (int) *bytes - rtp_header_len); @@ -4251,16 +4389,13 @@ static switch_status_t read_rtcp_packet(switch_rtp_t *rtp_session, switch_size_t if (rtp_session->rtcp_dtls) { char *b = (char *) &rtp_session->rtcp_recv_msg; - //printf("RECV2 %d %ld\n", *b, *bytes); - if (*b == 0 || *b == 1) { if (rtp_session->rtcp_ice.ice_user) { handle_ice(rtp_session, &rtp_session->rtcp_ice, (void *) &rtp_session->rtcp_recv_msg, *bytes); } *bytes = 0; } - - + if (*bytes && (*b >= 20) && (*b <= 64)) { rtp_session->rtcp_dtls->bytes = *bytes; rtp_session->rtcp_dtls->data = (void *) &rtp_session->rtcp_recv_msg; @@ -4474,14 +4609,15 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ pt = 20000; } - if ((rtp_session->ice.ice_user && rtp_session->flags[SWITCH_RTP_FLAG_VIDEO])) { - pt = 10000; - } if ((io_flags & SWITCH_IO_FLAG_NOBLOCK)) { pt = 0; } + if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) { + pt = 100000; + } + poll_status = switch_poll(rtp_session->read_pollfd, 1, &fdr, pt); if (rtp_session->dtmf_data.out_digit_dur > 0) { @@ -4499,6 +4635,22 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ ret = -1; goto end; } + + + if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO]) { + //switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_CRIT, "Read bytes (%i) %ld\n", status, bytes); + + if (bytes == 0) { + if (check_rtcp_and_ice(rtp_session) == -1) { + ret = -1; + goto end; + } + // This is dumb + switch_rtp_video_refresh(rtp_session); + goto rtcp; + } + } + if ((*flags & SFF_PROXY_PACKET)) { ret = (int) bytes; goto end; @@ -4509,8 +4661,8 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ has_rtcp = 1; goto rtcp; } - - //switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG, "Read bytes (%i) %ld\n", status, bytes); + + } poll_loop = 0; } else { @@ -4539,9 +4691,11 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ ret = -1; goto end; } - - } else if ((!(io_flags & SWITCH_IO_FLAG_NOBLOCK)) && - (rtp_session->dtmf_data.out_digit_dur == 0)) { + goto recvfrom; + } + + if ((!(io_flags & SWITCH_IO_FLAG_NOBLOCK)) && + (rtp_session->dtmf_data.out_digit_dur == 0)) { return_cng_frame(); } } @@ -4572,7 +4726,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ if (rtcp_status == SWITCH_STATUS_SUCCESS) { switch_rtp_reset_media_timer(rtp_session); - + if (rtp_session->flags[SWITCH_RTP_FLAG_RTCP_PASSTHRU] || rtp_session->rtcp_recv_msg_p->header.type == 206) { switch_channel_t *channel = switch_core_session_get_channel(rtp_session->session); const char *uuid = switch_channel_get_partner_uuid(channel); @@ -5118,6 +5272,13 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_zerocopy_read_frame(switch_rtp_t *rtp return SWITCH_STATUS_FALSE; } + if (rtp_session->fir_countdown) { + if (--rtp_session->fir_countdown == 0) { + send_fir(rtp_session); + //send_pli(rtp_session); + } + } + bytes = rtp_common_read(rtp_session, &frame->payload, &frame->flags, io_flags); frame->data = RTP_BODY(rtp_session); @@ -5311,6 +5472,45 @@ static int rtp_common_write(switch_rtp_t *rtp_session, } } + + if (switch_rtp_test_flag(rtp_session, SWITCH_RTP_FLAG_VIDEO)) { + /* Normalize the timestamps to our own base by generating a made up starting point then adding the measured deltas to that base + so if the timestamps and ssrc of the source change, it will not break the other end's jitter bufffer / decoder etc *cough* CHROME *cough* + */ + + if (!rtp_session->ts_norm.ts) { + rtp_session->ts_norm.ts = (uint32_t) rand() % 1000000 + 1; + } + + if (!rtp_session->ts_norm.last_ssrc || send_msg->header.ssrc != rtp_session->ts_norm.last_ssrc) { + if (rtp_session->ts_norm.last_ssrc) { + rtp_session->ts_norm.m = 1; + if (rtp_session->ts_norm.delta) { + rtp_session->ts_norm.ts += rtp_session->ts_norm.delta; + } + } + rtp_session->ts_norm.last_ssrc = send_msg->header.ssrc; + rtp_session->ts_norm.last_frame = ntohl(send_msg->header.ts); + } + + + if (ntohl(send_msg->header.ts) != rtp_session->ts_norm.last_frame) { + rtp_session->ts_norm.delta = ntohl(send_msg->header.ts) - rtp_session->ts_norm.last_frame; + rtp_session->ts_norm.ts += rtp_session->ts_norm.delta; + } + + rtp_session->ts_norm.last_frame = ntohl(send_msg->header.ts); + send_msg->header.ts = htonl(rtp_session->ts_norm.ts); + + if (rtp_session->ts_norm.m) { + if (send_msg->header.m) { + rtp_session->ts_norm.m = 0; + } else { + send_msg->header.m = 1; + } + } + } + send_msg->header.ssrc = htonl(rtp_session->ssrc); if (rtp_session->flags[SWITCH_RTP_FLAG_GOOGLEHACK] && rtp_session->send_msg.header.pt == 97) { @@ -5447,6 +5647,8 @@ static int rtp_common_write(switch_rtp_t *rtp_session, send = 0; } + + if (send) { send_msg->header.seq = htons(++rtp_session->seq); @@ -5549,7 +5751,6 @@ static int rtp_common_write(switch_rtp_t *rtp_session, } } - if (switch_socket_sendto(rtp_session->sock_output, rtp_session->remote_addr, 0, (void *) send_msg, &bytes) != SWITCH_STATUS_SUCCESS) { rtp_session->seq--; ret = -1;