Загрузка данных


/* Apply media update. */
static pj_status_t apply_med_update(pjsua_call_media *call_med,
                                    const pjmedia_sdp_session *local_sdp,
                                    const pjmedia_sdp_session *remote_sdp,
                                    pj_bool_t *need_renego_sdp)
{
    const char *STR_SENDRECV = "sendrecv";
    const char *STR_SENDONLY = "sendonly";
    const char *STR_RECVONLY = "recvonly";
    const char *STR_INACTIVE = "inactive";

    pjsua_call *call = call_med->call;
    pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
    pjsua_call_id call_id = call->index;
    unsigned mi = call_med->idx;
    pj_bool_t media_changed = PJ_FALSE;
    pj_pool_t *tmp_pool = call->inv->pool_prov;
    pj_status_t status = PJ_SUCCESS;

    /* Initialize the following variables to avoid warnings from compiler
     * and code analysis. Actually the function will always init the vars
     * when the media type is valid/recognized.
     */
    pjmedia_stream_info asi = {0};
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
    pjmedia_vid_stream_info vsi = {0};
#endif
    pjmedia_txt_stream_info tsi;
    pjmedia_stream_info_common *si = NULL;
    pjsua_stream_info stream_info = {0};
    pj_str_t *enc_name = NULL;

    if (call_med->type == PJMEDIA_TYPE_AUDIO) {
        si = (pjmedia_stream_info_common *)&asi;
        status = pjmedia_stream_info_from_sdp(
                                    &asi, tmp_pool, pjsua_var.med_endpt,
                                    local_sdp, remote_sdp, mi);
        stream_info.info.aud = asi;
        enc_name = &asi.fmt.encoding_name;

#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
    } else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
        si = (pjmedia_stream_info_common *)&vsi;
        status = pjmedia_vid_stream_info_from_sdp(
                                    &vsi, tmp_pool, pjsua_var.med_endpt,
                                    local_sdp, remote_sdp, mi);
        stream_info.info.vid = vsi;
        enc_name = &vsi.codec_info.encoding_name;
#endif
    } else if (call_med->type == PJMEDIA_TYPE_TEXT) {
        si = (pjmedia_stream_info_common *)&tsi;
        status = pjmedia_txt_stream_info_from_sdp(
                                    &tsi, tmp_pool, pjsua_var.med_endpt,
                                    local_sdp, remote_sdp, mi);
        stream_info.info.txt = tsi;
        enc_name = &tsi.fmt.encoding_name;
    }
    else {
        status = PJMEDIA_EUNSUPMEDIATYPE;
    }

    if (status != PJ_SUCCESS) {
        PJ_PERROR(1,(THIS_FILE, status,
                     "pjmedia_stream_info_from_sdp() failed "
                         "for call_id %d media %d",
                     call_id, mi));
        return status;
    }

#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
    /* Enable/disable RTCP XR based on account setting. */
    si->rtcp_xr_enabled = acc->cfg.enable_rtcp_xr;
#endif

    /* Check if remote wants RTP and RTCP multiplexing,
     * but we don't enable it.
     */
    if (si->rtcp_mux && !call_med->enable_rtcp_mux) {
        si->rtcp_mux = PJ_FALSE;
    }

    if (call_med->type == PJMEDIA_TYPE_AUDIO) {
        /* Codec parameter of stream info (si->param) can be NULL if
         * the stream is rejected or disabled.
         */
        /* Override ptime, if this option is specified. */
        if (pjsua_var.media_cfg.ptime != 0 && asi.param) {
            asi.param->setting.frm_per_pkt = (pj_uint8_t)
                (pjsua_var.media_cfg.ptime / asi.param->info.frm_ptime);
            if (asi.param->setting.frm_per_pkt == 0)
                asi.param->setting.frm_per_pkt = 1;
        }

        /* Disable VAD, if this option is specified. */
        if (pjsua_var.media_cfg.no_vad && asi.param) {
            asi.param->setting.vad = 0;
        }

        if (call->audio_idx==-1 && status==PJ_SUCCESS &&
            call_med->tp && local_sdp->media[mi]->desc.port != 0)
        {
            call->audio_idx = mi;
        }
    }

    if (!pjmedia_sdp_neg_was_answer_remote(call->inv->neg) &&
        si->dir != PJMEDIA_DIR_NONE)
    {
        pjmedia_dir dir = si->dir;

        if (call->opt.flag & PJSUA_CALL_SET_MEDIA_DIR) {
            call_med->def_dir = call->opt.media_dir[mi];
            PJ_LOG(4,(THIS_FILE, "Call %d: setting %s media "
                                 "direction #%d to %d.",
                                 call_id, pjmedia_type_name(call_med->type), mi,
                                 call_med->def_dir));
        }

        /* If the default direction specifies we do not wish
         * encoding/decoding, clear that direction.
         */
        if ((call_med->def_dir & PJMEDIA_DIR_ENCODING) == 0) {
            dir &= ~PJMEDIA_DIR_ENCODING;
        }
        if ((call_med->def_dir & PJMEDIA_DIR_DECODING) == 0) {
            dir &= ~PJMEDIA_DIR_DECODING;
        }

        if (dir != si->dir) {
            const char *str_attr = NULL;
            pjmedia_sdp_attr *attr;
            pjmedia_sdp_media *m;

            if (!*need_renego_sdp) {
                pjmedia_sdp_session *local_sdp_renego;
                local_sdp_renego =
                    pjmedia_sdp_session_clone(tmp_pool, local_sdp);
                local_sdp = local_sdp_renego;
                *need_renego_sdp = PJ_TRUE;
            }

            si->dir = dir;
            m = local_sdp->media[mi];

            /* Remove existing directions attributes */
            pjmedia_sdp_media_remove_all_attr(m, STR_SENDRECV);
            pjmedia_sdp_media_remove_all_attr(m, STR_SENDONLY);
            pjmedia_sdp_media_remove_all_attr(m, STR_RECVONLY);

            if (si->dir == PJMEDIA_DIR_ENCODING_DECODING) {
                str_attr = STR_SENDRECV;
            } else if (si->dir == PJMEDIA_DIR_ENCODING) {
                str_attr = STR_SENDONLY;
            } else if (si->dir == PJMEDIA_DIR_DECODING) {
                str_attr = STR_RECVONLY;
            } else {
                str_attr = STR_INACTIVE;
            }
            attr = pjmedia_sdp_attr_create(tmp_pool, str_attr, NULL);
            pjmedia_sdp_media_add_attr(m, attr);
        }
    }

    stream_info.type = call_med->type;

#if PJSUA_MEDIA_HAS_PJMEDIA || PJSUA_THIRD_PARTY_STREAM_HAS_GET_INFO
#if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
    /* Check if we need to reset or maintain SRTP ROC */
    check_srtp_roc(call, mi, &stream_info, local_sdp, remote_sdp);
#endif
#endif

    /* Check if this media is changed */
    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 (%s) unchanged.",
                  call_id, mi, pjmedia_type_name(call_med->type)));
    }

    /* Check if no media is active */
    if (local_sdp->media[mi]->desc.port == 0) {

        /* Update call media state and direction */
        call_med->state = PJSUA_CALL_MEDIA_NONE;
        call_med->dir = PJMEDIA_DIR_NONE;

    } else if (call_med->tp) {
        pjmedia_transport_info tp_info;
        pjmedia_srtp_info *srtp_info;

        /* Call media direction */
        call_med->dir = si->dir;

        /* Call media state */
        if (call->local_hold ||
            ((call_med->dir & PJMEDIA_DIR_DECODING) == 0 &&
             (call_med->def_dir & PJMEDIA_DIR_DECODING) == 0))
        {
            /* Local hold: Either the user holds the call, or sets
             * the media direction that requests the remote party to
             * stop sending media (i.e. sendonly or inactive).
             */
            call_med->state = PJSUA_CALL_MEDIA_LOCAL_HOLD;
        } else if ((call_med->dir & PJMEDIA_DIR_ENCODING) == 0 &&
                   (call_med->def_dir & PJMEDIA_DIR_DECODING) != 0)
        {
            /* Remote hold: Remote doesn't want us to send media
             * (recvonly or inactive) and we don't set media dir that
             * locally holds the media.
             */
            call_med->state = PJSUA_CALL_MEDIA_REMOTE_HOLD;
        } else {
            call_med->state = PJSUA_CALL_MEDIA_ACTIVE;
        }

        if (call->inv->following_fork) {
            unsigned options = (call_med->enable_rtcp_mux?
                                PJMEDIA_TPMED_RTCP_MUX: 0);
            /* Normally media transport will automatically restart
             * itself (if needed, based on info from the SDP) in
             * pjmedia_transport_media_start(), however in "following
             * forked media" case (see #1644), we need to explicitly
             * restart it as it cannot detect fork scenario from
             * the SDP only.
             */
            status = pjmedia_transport_media_stop(call_med->tp);
            if (status != PJ_SUCCESS) {
                PJ_PERROR(1,(THIS_FILE, status,
                             "pjmedia_transport_media_stop() failed "
                             "for call_id %d media %d",
                             call_id, mi));
                return status;
            }
            status = pjmedia_transport_media_create(call_med->tp,
                                                    tmp_pool,
                                                    options, NULL, mi);
            if (status != PJ_SUCCESS) {
                PJ_PERROR(1,(THIS_FILE, status,
                             "pjmedia_transport_media_create() failed "
                             "for call_id %d media %d",
                             call_id, mi));
                return status;
            }
        }

        /* Start/restart media transport based on info in SDP */
        status = pjmedia_transport_media_start(call_med->tp,
                                               tmp_pool, local_sdp,
                                               remote_sdp, mi);
        if (status != PJ_SUCCESS) {
            PJ_PERROR(1,(THIS_FILE, status,
                         "pjmedia_transport_media_start() failed "
                         "for call_id %d media %d",
                         call_id, mi));
            return status;
        }

        pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);

        /* Get remote SRTP usage policy */
        pjmedia_transport_info_init(&tp_info);
        pjmedia_transport_get_info(call_med->tp, &tp_info);
        srtp_info = (pjmedia_srtp_info*)
                    pjmedia_transport_info_get_spc_info(
                            &tp_info, PJMEDIA_TRANSPORT_TYPE_SRTP);
        if (srtp_info) {
            call_med->rem_srtp_use = srtp_info->peer_use;
        }

        /* Update audio channel */
        if (media_changed) {
            if (call_med->type == PJMEDIA_TYPE_AUDIO) {
                status = pjsua_aud_channel_update(call_med,
                                                  call->inv->pool, &asi,
                                                  local_sdp, remote_sdp);
            } else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
                status = pjsua_vid_channel_update(call_med,
                                                  call->inv->pool, &vsi,
                                                  local_sdp, remote_sdp);
#endif
            } else if (call_med->type == PJMEDIA_TYPE_TEXT) {
                status = pjsua_txt_channel_update(call_med,
                                                  call->inv->pool, &tsi,
                                                  local_sdp, remote_sdp);
            }
            if (status != PJ_SUCCESS) {
                PJ_PERROR(1,(THIS_FILE, status,
                             "%s channel_update failed "
                             "for call_id %d media %d",
                             pjmedia_type_name(call_med->type),
                             call_id, mi));
                return status;
            }

            if (pjmedia_transport_info_get_spc_info(
                            &tp_info, PJMEDIA_TRANSPORT_TYPE_LOOP))
            {
                pjmedia_transport_loop_disable_rx(
                        call_med->tp, call_med->strm.a.stream,
                        !acc->cfg.enable_loopback);
            }
        }
    }

    /* Print info. */
    if (status == PJ_SUCCESS) {
        char info[80];
        int info_len = 0;
        int len;
        const char *dir;

        switch (si->dir) {
        case PJMEDIA_DIR_NONE:
            dir = "inactive";
            break;
        case PJMEDIA_DIR_ENCODING:
            dir = "sendonly";
            break;
        case PJMEDIA_DIR_DECODING:
            dir = "recvonly";
            break;
        case PJMEDIA_DIR_ENCODING_DECODING:
            dir = "sendrecv";
            break;
        default:
            dir = "unknown";
            break;
        }
        len = pj_ansi_snprintf( info+info_len, sizeof(info)-info_len,
                               ", stream #%d: %.*s (%s)", mi,
                               (int)enc_name->slen, enc_name->ptr,
                               dir);
        if (len > 0)
            info_len += len;
        PJ_LOG(4,(THIS_FILE,"%s updated%s",
                  pjmedia_type_name(call_med->type), info));
    }
    return status;
}