diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 72c3416bd23f0af3b1d8f8165264935e08c9c053..b3ee6c395bc8c56ad50938e0137dff4f9a838498 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -890,6 +890,8 @@ struct ast_sip_endpoint { AST_STRING_FIELD(messages_waiting); /*! Dialtone to be used as audiable indication when there is a message waiting for the endpoint */ AST_STRING_FIELD(mwi_dialtone_state); + /*Direction for the P-Early-Media */ + AST_STRING_FIELD(direction); ); /*! Configuration for extensions */ struct ast_sip_endpoint_extensions extensions; @@ -963,6 +965,9 @@ struct ast_sip_endpoint { unsigned int mediasec; /*! Call waiting enabled flag */ unsigned int call_waiting_enabled; + /*! Support earlyMedia on endpoint */ + unsigned int earlymedia; + /*! Security mechanisms supported by peer */ AST_LIST_HEAD_NOLOCK(, security_mechanism) security_mechanisms; }; diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index c8ff6b4f61528f53416eec3de757a1a77d0da05a..5cd960068a539905d34fa1a814c541018ad21d3a 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -2117,6 +2117,8 @@ int ast_res_pjsip_initialize_configuration(void) ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "timers", "yes", timers_handler, timers_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "timers_min_se", "90", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, extensions.timer.min_se)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "timers_sess_expires", "1800", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, extensions.timer.sess_expires)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "earlymedia", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, earlymedia)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "direction", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint , direction)); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "auth", "", inbound_auth_handler, inbound_auths_to_str, NULL, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "outbound_auth", "", outbound_auth_handler, outbound_auths_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "aors", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, aors)); diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 6af6b8a8dcb2d9af807fa559d538c3618c974634..3e68ae87edc3c09bb0b6ec95c5453451af8769c6 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -66,7 +66,7 @@ static int max_sessions_per_line = DEFAULT_MAX_SESSION_PER_LINE; static int max_sessions = DEFAULT_MAX_SESSION; static int current_session_count = 0; // current number of active sessions - +#define MAX_BUFFER_LEN 50 /* Some forward declarations */ static void handle_session_begin(struct ast_sip_session *session); static void handle_session_end(struct ast_sip_session *session); @@ -2540,6 +2540,7 @@ static int sip_session_refresh(struct ast_sip_session *session, } } } + ast_sip_session_send_request_with_cb(session, tdata, on_response); ast_sip_session_media_state_free(active_media_state); @@ -2609,6 +2610,8 @@ void ast_sip_session_send_response(struct ast_sip_session *session, pjsip_tx_dat static pj_bool_t session_on_rx_request(pjsip_rx_data *rdata); static pj_bool_t session_on_rx_response(pjsip_rx_data *rdata); +static pj_status_t add_earlymedia_request_headers(pjsip_tx_data *tdata); +static pj_status_t add_earlymedia_response_headers(pjsip_tx_data *tdata); static void session_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e); static pjsip_module session_module = { @@ -2617,6 +2620,8 @@ static pjsip_module session_module = { .on_rx_request = session_on_rx_request, .on_rx_response = session_on_rx_response, .on_tsx_state = session_on_tsx_state, + .on_tx_request = add_earlymedia_request_headers, + .on_tx_response = add_earlymedia_response_headers, }; /*! \brief Determine whether the SDP provided requires deferral of negotiating or not @@ -2873,6 +2878,8 @@ void ast_sip_session_send_request(struct ast_sip_session *session, pjsip_tx_data int ast_sip_session_create_invite(struct ast_sip_session *session, pjsip_tx_data **tdata) { pjmedia_sdp_session *offer; + pjsip_generic_string_hdr *content_header = NULL; + static const pj_str_t headerName = { "Content-Disposition", 19 }; SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session)); if (!(offer = create_local_sdp(session->inv_session, session, NULL))) { @@ -2913,6 +2920,19 @@ int ast_sip_session_create_invite(struct ast_sip_session *session, pjsip_tx_data } } + content_header = pjsip_msg_find_hdr_by_name((*tdata)->msg, &headerName, NULL); + /*If content dispostion is present P-Early-Media header field does not apply*/ + if (content_header) + { + char content_header_value[MAX_BUFFER_LEN] = {0}; + ast_copy_pj_str(&content_header_value, &content_header->hvalue, pj_strlen(&content_header->hvalue) + 1); + if(!strcmp(content_header_value, "early-session")) + session->endpoint->earlymedia = 0; + } + + if(session->endpoint->earlymedia) + ast_sip_add_header(*tdata,"P-Early-Media","supported"); + SCOPE_EXIT_RTN_VALUE(0); } @@ -4277,7 +4297,88 @@ static pj_bool_t has_supplement(const struct ast_sip_session *session, const pjs } return PJ_FALSE; } +/*Parse the direction and remove redundant values*/ +static void parse_earlymedia_direction(char *direction, char *buffer) +{ + char directionbuffer[MAX_BUFFER_LEN]; + strcpy(directionbuffer, direction); + char *token = strtok(directionbuffer, ","); + int flag =0; + + while( token != NULL ) + { + if(flag) + { + sprintf(buffer+strlen(buffer), "%s", ","); + flag= 0; + } + if(!strcmp(token ,"sendrecv") || !strcmp(token ,"sendonly") || !strcmp(token ,"recvonly") + || !strcmp(token ,"inactive")) + { + sprintf(buffer+strlen(buffer), "%s", token); + flag = 1; + } + token = strtok(NULL, ","); + } + if(buffer[strlen(buffer)-1] == ',') + buffer[strlen(buffer)-1]= '\0'; +} + +static pj_status_t add_earlymedia_request_headers(pjsip_tx_data *tdata) +{ + pjsip_dialog *dlg = pjsip_tdata_get_dlg(tdata); + if(tdata->msg->line.status.code == 6) //Prack + { + if(dlg) + { + struct ast_sip_session *session = ast_sip_dialog_get_session(dlg); + if(session && session->endpoint->earlymedia) + { + char buffer[MAX_BUFFER_LEN] = {0}; + parse_earlymedia_direction(session->endpoint->direction, &buffer); + if(strlen(buffer) > 0) + ast_sip_add_header(tdata,"P-Early-Media", buffer); + } + } + } + return PJ_SUCCESS; +} + +static pj_status_t add_earlymedia_response_headers(pjsip_tx_data *tdata) +{ + pjsip_dialog *dlg = pjsip_tdata_get_dlg(tdata); + struct pjsip_request_line req = tdata->msg->line.req; + pjsip_generic_string_hdr *request_headers = NULL; + static const pj_str_t headerName = { "P-Early-Media", 13 }; + request_headers = pjsip_msg_find_hdr_by_name(tdata->msg, &headerName, NULL); + + /*response is constructed by library,Hence remove the P-Early-Media header + from 200 Ok for invite and error cases*/ + if (request_headers) + { + if(tdata->msg->line.status.code != 180 && tdata->msg->line.status.code != 183) + pj_list_erase(request_headers); + } + + pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); + if((tdata->msg->line.status.code == 200 && strcmp(pj_strbuf(&cseq->method.name),"PRACK") == 0) || + (tdata->msg->line.status.code == 200 && strcmp(pj_strbuf(&cseq->method.name),"UPDATE") == 0)) + { + if(dlg) + { + struct ast_sip_session *session = ast_sip_dialog_get_session(dlg); + if(session && session->endpoint->earlymedia) + { + char buffer[MAX_BUFFER_LEN] = {0}; + parse_earlymedia_direction(session->endpoint->direction, &buffer); + if(strlen(buffer) > 0) + ast_sip_add_header(tdata,"P-Early-Media", buffer); + } + } + } + return PJ_SUCCESS; +} /*! * \internal * Added for debugging purposes @@ -4636,12 +4737,25 @@ static void handle_outgoing_response(struct ast_sip_session *session, pjsip_tx_d { struct ast_sip_session_supplement *supplement; struct pjsip_status_line status = tdata->msg->line.status; + pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); SCOPE_ENTER(3, "%s: Method is %.*s, Response is %d %.*s\n", ast_sip_session_get_name(session), (int) pj_strlen(&cseq->method.name), pj_strbuf(&cseq->method.name), status.code, (int) pj_strlen(&status.reason), pj_strbuf(&status.reason)); + //If earlymedia is enabled add header to 18X responses + if(status.code == 180 || status.code == 183) + { + if(session->endpoint->earlymedia) + { + ast_log(LOG_NOTICE, "Add the header P-Early-Media to 18X!!"); + char buffer[MAX_BUFFER_LEN]={0}; + parse_earlymedia_direction(session->endpoint->direction, &buffer); + if(strlen(buffer) > 0) + ast_sip_add_header(tdata, "P-Early-Media", buffer); + } + } if (!cseq) { SCOPE_EXIT_LOG_RTN(LOG_ERROR, "%s: Cannot send response due to missing sequence header",