From 69848a09610618abe49a4b5211af0a0d784a54a4 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Fri, 14 Sep 2012 04:06:29 +0000 Subject: Close #1568: - Added media change detection based on SDP negotiation result and local codec param settings, the detection result will decide whether the media should be re-initialized after the SDP negotiation. - Fixed stream to keep the duplicate of codec param for the stream info (was only copying the pointer). - Introduced macro PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO & PJSUA_THIRD_PARTY_STREAM_HAS_GET_STAT. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@4254 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/src/pjsua-lib/pjsua_aud.c | 9 +- pjsip/src/pjsua-lib/pjsua_dump.c | 22 +++ pjsip/src/pjsua-lib/pjsua_media.c | 355 +++++++++++++++++++++++++++++++------- pjsip/src/pjsua-lib/pjsua_vid.c | 7 +- 4 files changed, 323 insertions(+), 70 deletions(-) (limited to 'pjsip/src') diff --git a/pjsip/src/pjsua-lib/pjsua_aud.c b/pjsip/src/pjsua-lib/pjsua_aud.c index daf98c2a..480aea41 100644 --- a/pjsip/src/pjsua-lib/pjsua_aud.c +++ b/pjsip/src/pjsua-lib/pjsua_aud.c @@ -560,7 +560,12 @@ static void dtmf_callback(pjmedia_stream *strm, void *user_data, pj_log_pop_indent(); } - +/* Internal function: update audio channel after SDP negotiation. + * Warning: do not use temporary/flip-flop pool, e.g: inv->pool_prov, + * for creating stream, etc, as after SDP negotiation and when + * the SDP media is not changed, the stream should remain running + * while the temporary/flip-flop pool may be released. + */ pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med, pj_pool_t *tmp_pool, pjmedia_stream_info *si, @@ -679,7 +684,7 @@ pj_status_t pjsua_aud_channel_update(pjsua_call_media *call_med, port_name = pj_str("call"); } status = pjmedia_conf_add_port( pjsua_var.mconf, - call->inv->pool_prov, + call->inv->pool, media_port, &port_name, (unsigned*) diff --git a/pjsip/src/pjsua-lib/pjsua_dump.c b/pjsip/src/pjsua-lib/pjsua_dump.c index ed18475b..87201cc8 100644 --- a/pjsip/src/pjsua-lib/pjsua_dump.c +++ b/pjsip/src/pjsua-lib/pjsua_dump.c @@ -212,6 +212,10 @@ static unsigned dump_media_stat(const char *indent, /* Dump media session */ +#if PJSUA_MEDIA_HAS_PJMEDIA || \ + (PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO && \ + PJSUA_THIRD_PARTY_STREAM_HAS_GET_STAT) + static void dump_media_session(const char *indent, char *buf, unsigned maxlen, pjsua_call *call) @@ -858,6 +862,24 @@ static void dump_media_session(const char *indent, } } +#else /* PJSUA_MEDIA_HAS_PJMEDIA || + (PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO && + PJSUA_THIRD_PARTY_STREAM_HAS_GET_STAT) */ + +static void dump_media_session(const char *indent, + char *buf, unsigned maxlen, + pjsua_call *call) +{ + PJ_UNUSED_ARG(indent); + PJ_UNUSED_ARG(buf); + PJ_UNUSED_ARG(maxlen); + PJ_UNUSED_ARG(call); +} + +#endif /* PJSUA_MEDIA_HAS_PJMEDIA || + (PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO && + PJSUA_THIRD_PARTY_STREAM_HAS_GET_STAT) */ + /* Print call info */ void print_call(const char *title, diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 40bd2df7..483df783 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -2084,63 +2084,73 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, } -static void stop_media_session(pjsua_call_id call_id) +static void stop_media_stream(pjsua_call *call, unsigned med_idx) { - pjsua_call *call = &pjsua_var.calls[call_id]; - unsigned mi; + pjsua_call_media *call_med = &call->media[med_idx]; - pj_log_push_indent(); + /* Check if stream does not exist */ + if (med_idx >= call->med_cnt) + return; - for (mi=0; mimed_cnt; ++mi) { - pjsua_call_media *call_med = &call->media[mi]; + pj_log_push_indent(); - if (call_med->type == PJMEDIA_TYPE_AUDIO) { - pjsua_aud_stop_stream(call_med); - } + if (call_med->type == PJMEDIA_TYPE_AUDIO) { + pjsua_aud_stop_stream(call_med); + } #if PJMEDIA_HAS_VIDEO - else if (call_med->type == PJMEDIA_TYPE_VIDEO) { - pjsua_vid_stop_stream(call_med); - } + else if (call_med->type == PJMEDIA_TYPE_VIDEO) { + pjsua_vid_stop_stream(call_med); + } #endif - PJ_LOG(4,(THIS_FILE, "Media session call%02d:%d is destroyed", - call_id, mi)); - call_med->prev_state = call_med->state; - call_med->state = PJSUA_CALL_MEDIA_NONE; + PJ_LOG(4,(THIS_FILE, "Media stream call%02d:%d is destroyed", + call->index, med_idx)); + call_med->prev_state = call_med->state; + call_med->state = PJSUA_CALL_MEDIA_NONE; - /* Try to sync recent changes to provisional media */ - if (mimed_prov_cnt && call->media_prov[mi].tp==call_med->tp) - { - pjsua_call_media *prov_med = &call->media_prov[mi]; + /* Try to sync recent changes to provisional media */ + if (med_idx < call->med_prov_cnt && + call->media_prov[med_idx].tp == call_med->tp) + { + pjsua_call_media *prov_med = &call->media_prov[med_idx]; - /* Media state */ - prov_med->prev_state = call_med->prev_state; - prov_med->state = call_med->state; + /* Media state */ + prov_med->prev_state = call_med->prev_state; + prov_med->state = call_med->state; - /* RTP seq/ts */ - prov_med->rtp_tx_seq_ts_set = call_med->rtp_tx_seq_ts_set; - prov_med->rtp_tx_seq = call_med->rtp_tx_seq; - prov_med->rtp_tx_ts = call_med->rtp_tx_ts; + /* RTP seq/ts */ + prov_med->rtp_tx_seq_ts_set = call_med->rtp_tx_seq_ts_set; + prov_med->rtp_tx_seq = call_med->rtp_tx_seq; + prov_med->rtp_tx_ts = call_med->rtp_tx_ts; - /* Stream */ - if (call_med->type == PJMEDIA_TYPE_AUDIO) { - prov_med->strm.a.conf_slot = call_med->strm.a.conf_slot; - prov_med->strm.a.stream = call_med->strm.a.stream; - } + /* Stream */ + if (call_med->type == PJMEDIA_TYPE_AUDIO) { + prov_med->strm.a.conf_slot = call_med->strm.a.conf_slot; + prov_med->strm.a.stream = call_med->strm.a.stream; + } #if PJMEDIA_HAS_VIDEO - else if (call_med->type == PJMEDIA_TYPE_VIDEO) { - prov_med->strm.v.cap_win_id = call_med->strm.v.cap_win_id; - prov_med->strm.v.rdr_win_id = call_med->strm.v.rdr_win_id; - prov_med->strm.v.stream = call_med->strm.v.stream; - } -#endif + else if (call_med->type == PJMEDIA_TYPE_VIDEO) { + prov_med->strm.v.cap_win_id = call_med->strm.v.cap_win_id; + prov_med->strm.v.rdr_win_id = call_med->strm.v.rdr_win_id; + prov_med->strm.v.stream = call_med->strm.v.stream; } +#endif } pj_log_pop_indent(); } +static void stop_media_session(pjsua_call_id call_id) +{ + pjsua_call *call = &pjsua_var.calls[call_id]; + unsigned mi; + + for (mi=0; mimed_cnt; ++mi) { + stop_media_stream(call, mi); + } +} + pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id) { pjsua_call *call = &pjsua_var.calls[call_id]; @@ -2188,6 +2198,171 @@ pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id) } +/* Match codec fmtp. This will compare the values and the order. */ +static pj_bool_t match_codec_fmtp(const pjmedia_codec_fmtp *fmtp1, + const pjmedia_codec_fmtp *fmtp2) +{ + unsigned i; + + if (fmtp1->cnt != fmtp2->cnt) + return PJ_FALSE; + + for (i = 0; i < fmtp1->cnt; ++i) { + if (pj_stricmp(&fmtp1->param[i].name, &fmtp2->param[i].name)) + return PJ_FALSE; + if (pj_stricmp(&fmtp1->param[i].val, &fmtp2->param[i].val)) + return PJ_FALSE; + } + + return PJ_TRUE; +} + +#if PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO + +static pj_bool_t is_media_changed(const pjsua_call *call, + unsigned med_idx, + const pjsua_stream_info *new_si_) +{ + const pjsua_call_media *call_med = &call->media[med_idx]; + + /* Check for newly added media */ + if (med_idx >= call->med_cnt) + return PJ_TRUE; + + /* Compare media type */ + if (call_med->type != new_si_->type) + return PJ_TRUE; + + /* Audio update checks */ + if (call_med->type == PJMEDIA_TYPE_AUDIO) { + pjmedia_stream_info the_old_si; + const pjmedia_stream_info *old_si = NULL; + const pjmedia_stream_info *new_si = &new_si_->info.aud; + const pjmedia_codec_info *old_ci = NULL; + const pjmedia_codec_info *new_ci = &new_si->fmt; + const pjmedia_codec_param *old_cp = NULL; + const pjmedia_codec_param *new_cp = new_si->param; + + /* Compare media direction */ + if (call_med->dir != new_si->dir) + return PJ_TRUE; + + /* Get current active stream info */ + if (call_med->strm.a.stream) { + pjmedia_stream_get_info(call_med->strm.a.stream, &the_old_si); + old_si = &the_old_si; + old_ci = &old_si->fmt; + old_cp = old_si->param; + } else { + /* The stream is inactive. */ + return (new_si->dir != PJMEDIA_DIR_NONE); + } + + /* Compare remote RTP address */ + if (pj_sockaddr_cmp(&old_si->rem_addr, &new_si->rem_addr)) + return PJ_TRUE; + + /* Compare codec info */ + if (pj_stricmp(&old_ci->encoding_name, &new_ci->encoding_name) || + old_ci->clock_rate != new_ci->clock_rate || + old_ci->channel_cnt != new_ci->channel_cnt || + old_si->rx_pt != new_si->rx_pt || + old_si->tx_pt != new_si->tx_pt || + old_si->rx_event_pt != new_si->tx_event_pt || + old_si->tx_event_pt != new_si->tx_event_pt) + { + return PJ_TRUE; + } + + /* Compare codec param */ + if (old_cp->setting.frm_per_pkt != new_cp->setting.frm_per_pkt || + old_cp->setting.vad != new_cp->setting.vad || + old_cp->setting.cng != new_cp->setting.cng || + old_cp->setting.plc != new_cp->setting.plc || + old_cp->setting.penh != new_cp->setting.penh || + !match_codec_fmtp(&old_cp->setting.dec_fmtp, + &new_cp->setting.dec_fmtp) || + !match_codec_fmtp(&old_cp->setting.enc_fmtp, + &new_cp->setting.enc_fmtp)) + { + return PJ_TRUE; + } + } + +#if PJMEDIA_HAS_VIDEO + else if (call_med->type == PJMEDIA_TYPE_VIDEO) { + pjmedia_vid_stream_info the_old_si; + const pjmedia_vid_stream_info *old_si = NULL; + const pjmedia_vid_stream_info *new_si = &new_si_->info.vid; + const pjmedia_vid_codec_info *old_ci = NULL; + const pjmedia_vid_codec_info *new_ci = &new_si->codec_info; + const pjmedia_vid_codec_param *old_cp = NULL; + const pjmedia_vid_codec_param *new_cp = new_si->codec_param; + + /* Compare media direction */ + if (call_med->dir != new_si->dir) + return PJ_TRUE; + + /* Get current active stream info */ + if (call_med->strm.v.stream) { + pjmedia_vid_stream_get_info(call_med->strm.v.stream, &the_old_si); + old_si = &the_old_si; + old_ci = &old_si->codec_info; + old_cp = old_si->codec_param; + } else { + /* The stream is inactive. */ + return (new_si->dir != PJMEDIA_DIR_NONE); + } + + /* Compare remote RTP address */ + if (pj_sockaddr_cmp(&old_si->rem_addr, &new_si->rem_addr)) + return PJ_TRUE; + + /* Compare codec info */ + if (pj_stricmp(&old_ci->encoding_name, &new_ci->encoding_name) || + old_si->rx_pt != new_si->rx_pt || + old_si->tx_pt != new_si->tx_pt) + { + return PJ_TRUE; + } + + /* Compare codec param */ + if (/* old_cp->enc_mtu != new_cp->enc_mtu || */ + pj_memcmp(&old_cp->enc_fmt.det, &new_cp->enc_fmt.det, + sizeof(pjmedia_video_format_detail)) || + !match_codec_fmtp(&old_cp->dec_fmtp, &new_cp->dec_fmtp) || + !match_codec_fmtp(&old_cp->enc_fmtp, &new_cp->enc_fmtp)) + { + return PJ_TRUE; + } + } + +#endif + + else { + /* Just return PJ_TRUE for other media type */ + return PJ_TRUE; + } + + return PJ_FALSE; +} + +#else /* PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO */ + +static pj_bool_t is_media_changed(const pjsua_call *call, + unsigned med_idx, + const pjsua_stream_info *new_si_) +{ + PJ_UNUSED_ARG(call); + PJ_UNUSED_ARG(med_idx); + PJ_UNUSED_ARG(new_si_); + /* Always assume that media has been changed */ + return PJ_TRUE; +} + +#endif /* PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO */ + + pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, const pjmedia_sdp_session *local_sdp, const pjmedia_sdp_session *remote_sdp) @@ -2216,7 +2391,7 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, pj_log_push_indent(); /* Destroy existing media session, if any. */ - stop_media_session(call->index); + //stop_media_session(call->index); /* Call media count must be at least equal to SDP media. Note that * it may not be equal when remote removed any SDP media line. @@ -2270,6 +2445,7 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, /* Process each media stream */ for (mi=0; mi < call->med_prov_cnt; ++mi) { pjsua_call_media *call_med = &call->media_prov[mi]; + pj_bool_t media_changed = PJ_FALSE; if (mi >= local_sdp->media_count || mi >= remote_sdp->media_count) @@ -2277,8 +2453,12 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, /* This may happen when remote removed any SDP media lines in * its re-offer. */ + + /* Stop stream */ + stop_media_stream(call, mi); + + /* Close the media transport */ if (call_med->tp) { - /* Close the media transport */ pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_NULL); pjmedia_transport_close(call_med->tp); call_med->tp = call_med->tp_orig = NULL; @@ -2293,11 +2473,14 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, #endif } + /* Apply media update action */ if (call_med->type==PJMEDIA_TYPE_AUDIO) { pjmedia_stream_info the_si, *si = &the_si; + pjsua_stream_info stream_info; - status = pjmedia_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt, - local_sdp, remote_sdp, mi); + status = pjmedia_stream_info_from_sdp( + si, tmp_pool, pjsua_var.med_endpt, + local_sdp, remote_sdp, mi); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "pjmedia_stream_info_from_sdp() failed " @@ -2306,8 +2489,23 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, continue; } + /* Check if this media is changed */ + stream_info.type = PJMEDIA_TYPE_AUDIO; + stream_info.info.aud = the_si; + if (pjsua_var.media_cfg.no_smart_media_update || + is_media_changed(call, mi, &stream_info)) + { + media_changed = PJ_TRUE; + /* Stop the media */ + stop_media_stream(call, mi); + } else { + PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (audio) unchanged.", + call_id, mi)); + } + /* Check if no media is active */ if (si->dir == PJMEDIA_DIR_NONE) { + /* Update call media state and direction */ call_med->state = PJSUA_CALL_MEDIA_NONE; call_med->dir = PJMEDIA_DIR_NONE; @@ -2335,10 +2533,11 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, if (tp_info.specific_info_cnt > 0) { unsigned i; for (i = 0; i < tp_info.specific_info_cnt; ++i) { - if (tp_info.spc_info[i].type == PJMEDIA_TRANSPORT_TYPE_SRTP) + if (tp_info.spc_info[i].type == + PJMEDIA_TRANSPORT_TYPE_SRTP) { pjmedia_srtp_info *srtp_info = - (pjmedia_srtp_info*) tp_info.spc_info[i].buffer; + (pjmedia_srtp_info*)tp_info.spc_info[i].buffer; call_med->rem_srtp_use = srtp_info->peer_use; break; @@ -2346,6 +2545,20 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, } } + /* Update audio channel */ + if (media_changed) { + status = pjsua_aud_channel_update(call_med, + call->inv->pool, si, + local_sdp, remote_sdp); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "pjsua_aud_channel_update() failed " + "for call_id %d media %d", + call_id, mi)); + continue; + } + } + /* Call media direction */ call_med->dir = si->dir; @@ -2358,17 +2571,6 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, call_med->state = PJSUA_CALL_MEDIA_ACTIVE; } - /* Call implementation */ - status = pjsua_aud_channel_update(call_med, tmp_pool, si, - local_sdp, remote_sdp); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjsua_aud_channel_update() failed " - "for call_id %d media %d", - call_id, mi)); - continue; - } - /* Print info. */ if (status == PJ_SUCCESS) { char info[80]; @@ -2413,9 +2615,11 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) } else if (call_med->type==PJMEDIA_TYPE_VIDEO) { pjmedia_vid_stream_info the_si, *si = &the_si; + pjsua_stream_info stream_info; - status = pjmedia_vid_stream_info_from_sdp(si, tmp_pool, pjsua_var.med_endpt, - local_sdp, remote_sdp, mi); + status = pjmedia_vid_stream_info_from_sdp( + si, tmp_pool, pjsua_var.med_endpt, + local_sdp, remote_sdp, mi); if (status != PJ_SUCCESS) { PJ_PERROR(1,(THIS_FILE, status, "pjmedia_vid_stream_info_from_sdp() failed " @@ -2424,8 +2628,21 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, continue; } + /* Check if this media is changed */ + stream_info.type = PJMEDIA_TYPE_VIDEO; + stream_info.info.vid = the_si; + if (is_media_changed(call, mi, &stream_info)) { + media_changed = PJ_TRUE; + /* Stop the media */ + stop_media_stream(call, mi); + } else { + PJ_LOG(4,(THIS_FILE, "Call %d: stream #%d (video) unchanged.", + call_id, mi)); + } + /* Check if no media is active */ if (si->dir == PJMEDIA_DIR_NONE) { + /* Update call media state and direction */ call_med->state = PJSUA_CALL_MEDIA_NONE; call_med->dir = PJMEDIA_DIR_NONE; @@ -2464,6 +2681,20 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, } } + /* Update audio channel */ + if (media_changed) { + status = pjsua_vid_channel_update(call_med, + call->inv->pool, si, + local_sdp, remote_sdp); + if (status != PJ_SUCCESS) { + PJ_PERROR(1,(THIS_FILE, status, + "pjsua_vid_channel_update() failed " + "for call_id %d media %d", + call_id, mi)); + continue; + } + } + /* Call media direction */ call_med->dir = si->dir; @@ -2476,16 +2707,6 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, call_med->state = PJSUA_CALL_MEDIA_ACTIVE; } - status = pjsua_vid_channel_update(call_med, tmp_pool, si, - local_sdp, remote_sdp); - if (status != PJ_SUCCESS) { - PJ_PERROR(1,(THIS_FILE, status, - "pjsua_vid_channel_update() failed " - "for call_id %d media %d", - call_id, mi)); - continue; - } - /* Print info. */ { char info[80]; diff --git a/pjsip/src/pjsua-lib/pjsua_vid.c b/pjsip/src/pjsua-lib/pjsua_vid.c index b818e5f5..6ab6e18a 100644 --- a/pjsip/src/pjsua-lib/pjsua_vid.c +++ b/pjsip/src/pjsua-lib/pjsua_vid.c @@ -705,7 +705,12 @@ pj_status_t pjsua_vid_channel_init(pjsua_call_media *call_med) return PJ_SUCCESS; } -/* Internal function: update video channel after SDP negotiation */ +/* Internal function: update video channel after SDP negotiation. + * Warning: do not use temporary/flip-flop pool, e.g: inv->pool_prov, + * for creating stream, etc, as after SDP negotiation and when + * the SDP media is not changed, the stream should remain running + * while the temporary/flip-flop pool may be released. + */ pj_status_t pjsua_vid_channel_update(pjsua_call_media *call_med, pj_pool_t *tmp_pool, pjmedia_vid_stream_info *si, -- cgit v1.2.3