From c07a6b6b9e4a75433dd17b3008d87d2ec41bcd70 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Fri, 22 Nov 2013 04:52:11 +0500 Subject: [PATCH] add polycom support to multicast paging, broadcast all formats at once to support most every popular phone at once --- src/include/switch_apr.h | 1 + src/mod/applications/mod_esf/mod_esf.c | 367 +++++++++++++++++++++---- src/switch_apr.c | 6 + src/switch_rtp.c | 35 +++ 4 files changed, 360 insertions(+), 49 deletions(-) diff --git a/src/include/switch_apr.h b/src/include/switch_apr.h index 923d60d985..3cb478a7cf 100644 --- a/src/include/switch_apr.h +++ b/src/include/switch_apr.h @@ -1304,6 +1304,7 @@ SWITCH_DECLARE(switch_status_t) switch_mcast_join(switch_socket_t *sock, switch_ SWITCH_DECLARE(switch_status_t) switch_mcast_hops(switch_socket_t *sock, uint8_t ttl); SWITCH_DECLARE(switch_status_t) switch_mcast_loopback(switch_socket_t *sock, uint8_t opt); +SWITCH_DECLARE(switch_status_t) switch_mcast_interface(switch_socket_t *sock, switch_sockaddr_t *iface); /** @} */ diff --git a/src/mod/applications/mod_esf/mod_esf.c b/src/mod/applications/mod_esf/mod_esf.c index f6a9191877..d8abbb5bcd 100644 --- a/src/mod/applications/mod_esf/mod_esf.c +++ b/src/mod/applications/mod_esf/mod_esf.c @@ -35,6 +35,45 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_esf_load); SWITCH_MODULE_DEFINITION(mod_esf, mod_esf_load, NULL, NULL); + + +#ifdef _MSC_VER +#pragma pack(push, r1, 1) +#else +#pragma pack(1) +#endif + + +struct polycom_packet { + switch_byte_t op; + switch_byte_t channel; + uint32_t serno; + uint8_t cid_len; + unsigned char cid[13]; +}; + +typedef struct polycom_packet polycom_packet_t; + +typedef struct { + switch_byte_t codec; + switch_byte_t flags; + uint32_t seq; +} polycom_audio_header_t; + + +typedef struct polycom_alert_packet { + polycom_packet_t header; + polycom_audio_header_t audio_header; + uint8_t data[]; +} polycom_alert_packet_t; + + +#ifdef _MSC_VER +#pragma pack(pop, r1) +#else +#pragma pack() +#endif + struct ls_control_packet { uint32_t unique_id; uint32_t command; @@ -52,35 +91,82 @@ typedef enum { typedef enum { SEND_TYPE_UNKNOWN = 0, SEND_TYPE_RTP = 1, - SEND_TYPE_RAW = 2, - SEND_TYPE_NOMEDIA = 3 + SEND_TYPE_NOMEDIA = 2 } ls_how_t; +static uint32_t SERNO = 0; + +switch_mutex_t *MUTEX = NULL; + +uint32_t get_serno(void) +{ + uint32_t r = 0; + + switch_mutex_lock(MUTEX); + r = SERNO; + switch_mutex_unlock(MUTEX); + + return r; +} + + +void inc_serno(void) +{ + switch_mutex_lock(MUTEX); + SERNO++; + switch_mutex_unlock(MUTEX); + +} + +void dec_serno(void) +{ + switch_mutex_lock(MUTEX); + SERNO--; + switch_mutex_unlock(MUTEX); + +} + + SWITCH_STANDARD_APP(bcast_function) { switch_channel_t *channel = switch_core_session_get_channel(session); - switch_socket_t *socket; - switch_sockaddr_t *audio_addr = NULL, *control_packet_addr; + switch_socket_t *socket, *polycom_socket = NULL; + switch_sockaddr_t *audio_addr = NULL, *control_packet_addr = NULL, *polycom_addr = NULL, *local_addr = NULL; switch_frame_t *read_frame = NULL; switch_status_t status; switch_size_t bytes; ls_control_packet_t control_packet; + unsigned char polycom_buf[1024] = { 0 }; + unsigned char last_polycom_buf[1024] = { 0 }; + uint32_t last_polycom_len = 0; + polycom_packet_t *polycom_packet = (polycom_packet_t *) polycom_buf; + polycom_alert_packet_t *alert_packet = (polycom_alert_packet_t *) polycom_buf; switch_codec_t codec = { 0 }; + switch_codec_t write_codec = { 0 }; switch_rtp_flag_t flags[SWITCH_RTP_FLAG_INVALID] = {0}; const char *err; switch_rtp_t *rtp_session = NULL; - switch_port_t rtp_port; - char guess_ip[25]; + switch_port_t rtp_port = 0; ls_how_t ready = SEND_TYPE_UNKNOWN; - //int argc; char *mydata, *argv[5]; char *mcast_ip = "224.168.168.168"; switch_port_t mcast_port = 34567; switch_port_t mcast_control_port = 6061; char *mcast_port_str = "34567"; - const char *esf_broadcast_ip = NULL, *var; + char *polycom_ip = "224.0.1.116"; + const char *source_ip = NULL; + switch_port_t polycom_port = 5001; + const char *var; switch_codec_implementation_t read_impl = { 0 }; int mcast_ttl = 1; + const char *caller_id_name = NULL; + int x = 0; + uint32_t seq = 0; + const char *codec_name = "PCMU"; + int read_rate = 8000; + int need_transcode = 0; + + inc_serno(); switch_core_session_get_read_impl(session, &read_impl); @@ -115,6 +201,9 @@ SWITCH_STANDARD_APP(bcast_function) } } + switch_channel_set_variable_printf(channel, "multicast_ttl", "%d", mcast_ttl); + + if (switch_true(switch_channel_get_variable(channel, SWITCH_BYPASS_MEDIA_VARIABLE))) { switch_core_session_message_t msg = { 0 }; @@ -132,13 +221,27 @@ SWITCH_STANDARD_APP(bcast_function) switch_channel_answer(channel); } + if (!(source_ip = switch_channel_get_variable(channel, "esf_multicast_bind_ip"))) { + if (!(source_ip = switch_channel_get_variable(channel, "local_ip_v4"))) { + source_ip = "127.0.0.1"; + } + } + + /* everyone */ + + if (switch_sockaddr_info_get(&local_addr, source_ip, SWITCH_UNSPEC, + 0, 0, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "address Error\n"); + goto fail; + } + if (switch_socket_create(&socket, AF_INET, SOCK_DGRAM, 0, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Socket Error 1\n"); goto fail; } - if (switch_mcast_hops(socket, (uint8_t) mcast_ttl) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Mutlicast TTL set failed\n"); + if (switch_socket_opt_set(socket, SWITCH_SO_REUSEADDR, 1) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Option Error\n"); goto fail; } @@ -148,29 +251,121 @@ SWITCH_STANDARD_APP(bcast_function) goto fail; } + if (switch_socket_bind(socket, local_addr) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Socket bind Error\n"); + goto fail; + } + if (switch_mcast_interface(socket, local_addr) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Socket interface Error\n"); + goto fail; + } + + if (switch_mcast_join(socket, control_packet_addr, NULL, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Multicast Error\n"); + goto fail; + } + + + if (switch_mcast_hops(socket, (uint8_t) mcast_ttl) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Mutlicast TTL set failed\n"); + goto fail; + } + + /* polycom */ + + + if (switch_sockaddr_info_get(&polycom_addr, polycom_ip, SWITCH_UNSPEC, + polycom_port, 0, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Socket Error 3\n"); + goto fail; + } + + if (switch_socket_create(&polycom_socket, AF_INET, SOCK_DGRAM, 0, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Socket Error 1\n"); + goto fail; + } + + if (switch_socket_opt_set(polycom_socket, SWITCH_SO_REUSEADDR, 1) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Socket Option Error\n"); + goto fail; + } + + if (switch_socket_bind(polycom_socket, local_addr) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Socket bind Error\n"); + goto fail; + } + + + if (switch_mcast_interface(polycom_socket, local_addr) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Socket interface Error\n"); + goto fail; + } + + if (switch_mcast_join(polycom_socket, polycom_addr, NULL, NULL) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Multicast Error\n"); + goto fail; + } + + + if (switch_mcast_hops(polycom_socket, (uint8_t) mcast_ttl) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Mutlicast TTL set failed\n"); + goto fail; + } + + while (!ready) { status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0); - if (read_frame && switch_test_flag(read_frame, SFF_CNG)) { - continue; - } if (!SWITCH_READ_ACCEPTABLE(status) || !read_frame) { goto fail; } - if (read_frame->packet && read_frame->packetlen && read_impl.ianacode == 0) { - ready = SEND_TYPE_RAW; - } else { - ready = SEND_TYPE_RTP; + + if (switch_test_flag(read_frame, SFF_CNG)) { + continue; + } + + + ready = SEND_TYPE_RTP; + } + + + alert_packet->audio_header.codec = 0x00; + alert_packet->audio_header.flags = 0; + + if ((var = switch_channel_get_variable(channel, "esf_multicast_write_codec"))) { + if (!strcasecmp(var, "PCMU")) { + codec_name = var; + } else if (!strcasecmp(var, "G722")) { + codec_name = var; + read_rate = 16000; + alert_packet->audio_header.codec = 0x09; } } + if (ready == SEND_TYPE_RTP) { - if (read_impl.ianacode != 0) { + if (strcasecmp(read_impl.iananame, codec_name)) { + need_transcode = 1; + if (switch_core_codec_init(&codec, "L16", NULL, + read_rate, + 20, + 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, + NULL, switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) { + switch_core_session_set_read_codec(session, &codec); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Codec Activation Success\n"); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Codec Activation Fail\n"); + goto fail; + } + + if (switch_core_codec_init(&write_codec, + codec_name, + NULL, 8000, 20, 1, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, @@ -182,39 +377,28 @@ SWITCH_STANDARD_APP(bcast_function) goto fail; } } + - if ((var = switch_channel_get_variable(channel, "esf_broadcast_ip"))) { - esf_broadcast_ip = switch_core_session_strdup(session, var); - } else { - switch_find_local_ip(guess_ip, sizeof(guess_ip), NULL, AF_INET); - esf_broadcast_ip = guess_ip; - } - - - if (!(rtp_port = switch_rtp_request_port(esf_broadcast_ip))) { + if (!(rtp_port = switch_rtp_request_port(source_ip))) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "RTP Port Error\n"); goto fail; } - rtp_session = switch_rtp_new(esf_broadcast_ip, + rtp_session = switch_rtp_new(source_ip, rtp_port, mcast_ip, mcast_port, - 0, + alert_packet->audio_header.codec, 160, 20000, flags, "soft", &err, switch_core_session_get_pool(session)); - + if (!switch_rtp_ready(rtp_session)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "RTP Error\n"); goto fail; } + } else if (ready == SEND_TYPE_NOMEDIA) { switch_yield(10000); - } else if (ready == SEND_TYPE_RAW) { - if (switch_sockaddr_info_get(&audio_addr, mcast_ip, SWITCH_UNSPEC, mcast_port, 0, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Socket Error 2\n"); - goto fail; - } } control_packet.unique_id = htonl((u_long) switch_epoch_time_now(NULL)); @@ -227,6 +411,38 @@ SWITCH_STANDARD_APP(bcast_function) bytes = 16; switch_socket_sendto(socket, control_packet_addr, 0, (void *) &control_packet, &bytes); + + if (!(caller_id_name = switch_channel_get_variable(channel, "caller_id_name"))) { + caller_id_name = "FreeSWITCH"; + } + + strncpy((char *)polycom_packet->cid, caller_id_name, sizeof(polycom_packet->cid)); + polycom_packet->cid_len = 13; + + polycom_packet->op = 0x0F; + polycom_packet->channel = 0x1a; + polycom_packet->serno = htonl(get_serno()); + + if ((var = switch_channel_get_variable(channel, "esf_multicast_channel"))) { + int channel_no = atoi(var); + + if (channel_no > 0 && channel_no < 255) { + polycom_packet->channel = (uint8_t) channel_no; + } + } + + for (x = 0; x < 32; x++) { + bytes = sizeof(polycom_packet_t); + switch_socket_sendto(socket, polycom_addr, 0, (void *) polycom_packet, &bytes); + //switch_yield(30000); + } + + polycom_packet->op = 0x10; + + if ((var = switch_channel_get_variable(channel, "esf_multicast_alert_sound"))) { + switch_ivr_displace_session(session, var, 0, "mr"); + } + while (switch_channel_ready(channel)) { status = switch_core_session_read_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0); @@ -234,26 +450,57 @@ SWITCH_STANDARD_APP(bcast_function) if (!SWITCH_READ_ACCEPTABLE(status)) { break; } + if (switch_test_flag(read_frame, SFF_CNG)) { continue; } - if (ready == SEND_TYPE_RTP) { - short *dbuf; - unsigned char *ebuf; - uint32_t i; - unsigned char encoded_data[4192]; - dbuf = read_frame->data; - ebuf = encoded_data; - - for (i = 0; i < read_frame->datalen / sizeof(short); i++) { - ebuf[i] = linear_to_ulaw(dbuf[i]); + if (ready == SEND_TYPE_RTP) { + unsigned char *ebuf; + unsigned char encoded_data[4192]; + uint32_t encoded_datalen = sizeof(encoded_data); + + if (need_transcode) { + uint32_t rate = codec.implementation->actual_samples_per_second; + uint32_t flag = 0; + + ebuf = encoded_data; + + switch_core_codec_encode(&write_codec, + &codec, + read_frame->data, + read_frame->datalen, + read_impl.actual_samples_per_second, + ebuf, &encoded_datalen, &rate, &flag); + + read_frame->data = encoded_data; + read_frame->datalen = encoded_datalen; + + } else { + ebuf = read_frame->data; + encoded_datalen = read_frame->datalen; } - read_frame->data = encoded_data; - read_frame->datalen = read_frame->datalen / 2; + switch_rtp_write_frame(rtp_session, read_frame); - read_frame->data = dbuf; - read_frame->datalen = read_frame->datalen * 2; + + seq += 160; + + alert_packet->audio_header.seq = htonl(seq); + + if (last_polycom_len) { + memcpy(alert_packet->data, last_polycom_buf, last_polycom_len); + memcpy(alert_packet->data + last_polycom_len, ebuf, encoded_datalen); + } else { + memcpy(alert_packet->data, ebuf, encoded_datalen); + } + + bytes = sizeof(*alert_packet) + encoded_datalen + last_polycom_len; + + switch_socket_sendto(socket, polycom_addr, 0, (void *) polycom_buf, &bytes); + + last_polycom_len = encoded_datalen; + memcpy((void *)last_polycom_buf, (void *)ebuf, last_polycom_len); + } else { bytes = read_frame->packetlen; switch_socket_sendto(socket, audio_addr, 0, read_frame->packet, &bytes); @@ -267,6 +514,16 @@ SWITCH_STANDARD_APP(bcast_function) bytes = 8; switch_socket_sendto(socket, control_packet_addr, 0, (void *) &control_packet, &bytes); + + polycom_packet->op = 0xFF; + //switch_yield(50000); + + for (x = 0; x < 12; x++) { + bytes = sizeof(*polycom_packet); + switch_socket_sendto(socket, polycom_addr, 0, (void *) polycom_packet, &bytes); + //switch_yield(30000); + } + fail: switch_core_session_set_read_codec(session, NULL); @@ -281,6 +538,16 @@ SWITCH_STANDARD_APP(bcast_function) if (socket) { switch_socket_close(socket); } + + if (polycom_socket) { + switch_socket_close(polycom_socket); + } + + if (rtp_port) { + switch_rtp_release_port(source_ip, rtp_port); + } + + dec_serno(); return; } @@ -289,6 +556,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_esf_load) { switch_application_interface_t *app_interface; + switch_mutex_init(&MUTEX, SWITCH_MUTEX_NESTED, pool); + /* connect my internal structure to the blank pointer passed to me */ *module_interface = switch_loadable_module_create_module_interface(pool, modname); SWITCH_ADD_APP(app_interface, "esf_page_group", NULL, NULL, bcast_function, NULL, SAF_NONE); diff --git a/src/switch_apr.c b/src/switch_apr.c index d7717290bd..9773e31b26 100644 --- a/src/switch_apr.c +++ b/src/switch_apr.c @@ -834,6 +834,12 @@ SWITCH_DECLARE(switch_status_t) switch_mcast_loopback(switch_socket_t *sock, uin return apr_mcast_loopback(sock, opt); } +SWITCH_DECLARE(switch_status_t) switch_mcast_interface(switch_socket_t *sock, switch_sockaddr_t *iface) +{ + return apr_mcast_interface(sock, iface); +} + + /* socket functions */ SWITCH_DECLARE(const char *) switch_get_addr(char *buf, switch_size_t len, switch_sockaddr_t *in) diff --git a/src/switch_rtp.c b/src/switch_rtp.c index fab4ddba8a..85a471ac16 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -1999,6 +1999,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_local_address(switch_rtp_t *rtp_s { switch_socket_t *new_sock = NULL, *old_sock = NULL; switch_status_t status = SWITCH_STATUS_FALSE; + int j = 0; #ifndef WIN32 char o[5] = "TEST", i[5] = ""; switch_size_t len, ilen = 0; @@ -2057,6 +2058,40 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_local_address(switch_rtp_t *rtp_s goto done; } + + if ((j = atoi(host)) && j > 223 && j < 240) { /* mcast */ + if (switch_mcast_interface(new_sock, rtp_session->local_addr) != SWITCH_STATUS_SUCCESS) { + *err = "Multicast Socket interface Error"; + goto done; + } + + if (switch_mcast_join(new_sock, rtp_session->local_addr, NULL, NULL) != SWITCH_STATUS_SUCCESS) { + *err = "Multicast Error"; + goto done; + } + + if (rtp_session->session) { + switch_channel_t *channel = switch_core_session_get_channel(rtp_session->session); + const char *var; + + if ((var = switch_channel_get_variable(channel, "multicast_ttl"))) { + int ttl = atoi(var); + + if (ttl > 0 && ttl < 256) { + if (switch_mcast_hops(new_sock, (uint8_t) ttl) != SWITCH_STATUS_SUCCESS) { + *err = "Mutlicast TTL set failed"; + goto done; + } + + } + } + + } + + } + + + #ifndef WIN32 len = sizeof(i); switch_socket_opt_set(new_sock, SWITCH_SO_NONBLOCK, TRUE);