#include "SpBase.h" #ifdef RVC_OS_WIN #include "stdafx.h" #include #else #endif #include "endpoint.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "refcnt.h" #include "list.h" #include "sockutil.h" #include "hash.h" #include "video_session.h" #include "audio_session.h" #include "rvc_media_common.h" #include "Event.h" bool g_IsExternalTerminalted = false; // default: ptime = 20ms #define CONDITION_PARAMS \ int status, \ char const *phrase, \ nua_t *nua, \ struct endpoint_t *ep, \ nua_handle_t *nh, \ struct endpoint_call_t *call, \ sip_t const *sip, \ tagi_t tags[] // the same with rtp payload type, the same as huawei open-eye enum e_audio_rtp_pt { PCMU = 0, // 64kbps, 4.17 PCMA = 8, // 64kbps, 4.11 G729 = 18, // 8kbps, 3.9 }; enum e_video_rtp_pt { H263 = 34, // h263 }; enum e_media_dir { DIR_NONE = 0, DIR_TX = 1, DIR_RX = 2, DIR_BOTH = 3, }; typedef struct sdpvideo_desc_t { unsigned long local_rtp_ip; int local_rtp_port; unsigned long remote_rtp_ip; int remote_rtp_port; int local_pt; int remote_pt; }; typedef struct media_desc_t { unsigned long remote_ip; int remote_port; unsigned long local_ip; int local_port; int media_dir; int local_pt; int local_ptime; int remote_pt; int remote_ptime; int local_telephone_event_pt; int remote_telephone_event_pt; int param[16]; }media_desc_t; // 1. how to exit struct endpoint_call_t { struct list_head entry; endpoint_call_callback_t cb; endpoint_t *ep; audio_session_t *audio; video_session_t *video; int id; su_home_t *home; nua_handle_t *nh; char *to_uri; char *from_uri; char *call_id; sdp_session_t *last_sdp; int connected; int local_media_port; int local_video_port; unsigned int last_media_desc_hash; char local_ip[RVC_MAX_IP_LEN]; DeviceTypeEnum eDeviceType; CallingTypeEnum nCallType; sdpvideo_desc_t sdpvieo_desc; DECLARE_REF_COUNT_MEMBER(ref_cnt); }; DECLARE_REF_COUNT_STATIC(endpoint_call, endpoint_call_t) struct endpoint_t { su_home_t home[1]; su_root_t *root; nua_t *nua; endpoint_call_t *active_call; #ifdef RVC_OS_WIN HANDLE event_thread; #else pthread_t ievent_threadid; #endif int call_seq; int media_port_seq; CEntityBase *entity; struct list_head call_list; endpoint_conf_t conf; int curr_audio_dev_type; // handfree or pickup }; static const char *state_desc[] = { "STATE::INIT", "STATE::CALLING", "STATE::PROCEEDING", "STATE::COMPLETING", "STATE::READY", "STATE::TERMINATING", "STATE::TERMINATED", }; #ifndef VIDEOPLAYER_FLAG_DOUBLESIZE #define VIDEOPLAYER_FLAG_DOUBLESIZE 0x01 #endif #ifndef VIDEOPLAYER_FLAG_NOTIMER #define VIDEOPLAYER_FLAG_NOTIMER 0x02 #endif #ifndef VIDEOPLAYER_FLAG_PUSH #define VIDEOPLAYER_FLAG_PUSH 0x04 #endif #ifndef VIDEOPLAYER_FLAG_PULL #define VIDEOPLAYER_FLAG_PULL 0x08 #endif #ifndef VIDEOPLAYER_FLAG_CHECKTOP #define VIDEOPLAYER_FLAG_CHECKTOP 0x10 #endif #ifndef VIDEOPLAYER_FLAG_ZOOMOUTSIZE #define VIDEOPLAYER_FLAG_ZOOMOUTSIZE 0x100 #endif static void endpoint_media_change_audio_dev(endpoint_call_t *call, e_dev_type current_dev_type) { if (call->audio) { DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("change audio device to %s mode.", DEV_PICKUP == current_dev_type ? "pickup" : "hand free"); audio_session_change_dev(call->audio, current_dev_type); } } static __inline struct in_addr __lton(unsigned long ip) { struct in_addr addr; addr.s_addr = ip; return addr; } #ifdef RVC_OS_WIN static void endpoint_media_update_video(endpoint_call_t *call, media_desc_t *video_desc, video_session_callback_t* cb) { if (video_desc->media_dir == DIR_NONE) { if (call->video) { video_session_stop(call->video); video_session_destroy(call->video); call->video = NULL; } else{ if (DOUBLERECORD_CALLTYPE == call->nCallType){ double_record_broadcast_video_session_stop(); } } } else { int rc; endpoint_conf_t *ep_conf = &call->ep->conf; if (!call->audio){ audio_session_conf_t conf = {0}; strcpy(&conf.in_dev[DEV_PICKUP][0], ep_conf->audio_pickup_in_dev); strcpy(&conf.in_dev[DEV_HANDFREE][0], ep_conf->audio_handfree_in_dev); strcpy(&conf.out_dev[DEV_PICKUP][0], ep_conf->audio_pickup_out_dev); strcpy(&conf.out_dev[DEV_HANDFREE][0], ep_conf->audio_handfree_out_dev); conf.agc_in[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_in_agc; conf.agc_in[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_in_agc; conf.agc_out[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_out_agc; conf.agc_out[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_out_agc; conf.ns_in[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_in_ns; conf.ns_in[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_in_ns; conf.ns_out[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_out_ns; conf.ns_out[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_out_ns; conf.aec[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_aec; conf.aec[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_aec; rc = audio_session_create(&conf, &call->audio); if (rc != 0) { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("create audio session failed! rc = %d", rc); DbgWithLink(LOG_LEVEL_WARN, LOG_TYPE_USER).setLogCode("QLR0402301A4").setResultCode("RTA3109")("视频连线创建音频通道失败"); return; } else { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_USER).setLogCode("QLR0402301A4")("视频连线创建音频通道成功"); } } if (call->video) { video_session_destroy(call->video); call->video = NULL; } int i = 0; int local_view_x = video_desc->param[i++]; int local_view_y = video_desc->param[i++]; int local_view_cx = video_desc->param[i++]; int local_view_cy = video_desc->param[i++]; int remote_view_x = video_desc->param[i++]; int remote_view_y = video_desc->param[i++]; int remote_view_cx = video_desc->param[i++]; int remote_view_cy = video_desc->param[i++]; int remote_width = video_desc->param[i++]; int remote_height = video_desc->param[i++]; int local_hwd_move = video_desc->param[i++]; int remote_hwd_move = video_desc->param[i++]; video_session_conf_t video_conf = {0}; video_conf.bit_rate = 256 * 1024; video_conf.local_rtp_ip = video_desc->local_ip; video_conf.local_rtp_port = video_desc->local_port; video_conf.local_video_view_x = local_view_x; video_conf.local_video_view_y = local_view_y; video_conf.local_video_view_cx = local_view_cx; video_conf.local_video_view_cy = local_view_cy; video_conf.mtu = ep_conf->mtu; video_conf.video_quant = ep_conf->quant; video_conf.remote_rtp_ip = video_desc->remote_ip; video_conf.remote_rtp_port = video_desc->remote_port; video_conf.remote_video_view_x = remote_view_x; video_conf.remote_video_view_y = remote_view_y; video_conf.remote_video_view_cx = remote_view_cx; video_conf.remote_video_view_cy = remote_view_cy; video_conf.remote_video_width = remote_width; video_conf.remote_video_height = remote_height; video_conf.ref_active_camera = call->ep->conf.ref_active_camera; video_conf.ref_camera_switch = call->ep->conf.ref_camera_switch; video_conf.ref_camera_state = call->ep->conf.ref_camera_state; video_conf.ref_window_state = call->ep->conf.ref_window_state; video_conf.ref_Is_showPersonArea = call->ep->conf.ref_Is_showPersonArea; video_conf.ref_active_img = NULL; video_conf.ref_Is_ActiveInspect = NULL; video_conf.camera_count = call->ep->conf.camera_count; video_conf.screen_count = call->ep->conf.screen_count; video_conf.ref_Up_Fps = call->ep->conf.ref_Up_Fps; video_conf.eDeviceType = call->eDeviceType; video_conf.nCallType = call->nCallType; video_conf.local_move = local_hwd_move; video_conf.remote_move = remote_hwd_move; video_conf.video_echo_cb = cb; video_conf.ilocal_wind_flags = VIDEOPLAYER_FLAG_PULL|VIDEOPLAYER_FLAG_CHECKTOP; video_conf.iremote_wind_flags = VIDEOPLAYER_FLAG_PUSH|VIDEOPLAYER_FLAG_CHECKTOP; video_conf.eType = (eVideoRenderType)call->ep->conf.irendertype; video_conf.local_pt = REC_COMMON_VIDEO_H264_PT; video_conf.remote_pt = REC_COMMON_VIDEO_H264_PT; rc = video_session_create(&video_conf, &call->video); if (rc != 0) { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("create video session failed! rc = %d", rc); return; } rc = video_session_start(call->video); if (rc != 0) { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("start video session failed! rc = %d", rc); video_session_destroy(call->video); call->video = NULL; return; } } } #else static void endpoint_media_update_video(endpoint_call_t *call, media_desc_t *video_desc, rvc_video_render_callback_t* cb) { if (video_desc->media_dir == DIR_NONE) { if (call->video) { video_session_stop(call->video); video_session_destroy(call->video); call->video = NULL; } } else { int rc; endpoint_conf_t *ep_conf = &call->ep->conf; if (!call->audio){ audio_session_conf_t conf = {0}; strcpy(&conf.in_dev[DEV_PICKUP][0], ep_conf->audio_pickup_in_dev); strcpy(&conf.in_dev[DEV_HANDFREE][0], ep_conf->audio_handfree_in_dev); strcpy(&conf.out_dev[DEV_PICKUP][0], ep_conf->audio_pickup_out_dev); strcpy(&conf.out_dev[DEV_HANDFREE][0], ep_conf->audio_handfree_out_dev); conf.agc_in[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_in_agc; conf.agc_in[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_in_agc; conf.agc_out[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_out_agc; conf.agc_out[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_out_agc; conf.ns_in[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_in_ns; conf.ns_in[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_in_ns; conf.ns_out[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_out_ns; conf.ns_out[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_out_ns; conf.aec[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_aec; conf.aec[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_aec; rc = audio_session_create(&conf, &call->audio); if (rc != 0) { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("create audio session failed! rc = %d", rc); DbgWithLink(LOG_LEVEL_WARN, LOG_TYPE_USER).setLogCode("QLR0402301A4").setResultCode("RTA3109")("视频连线创建音频通道失败"); return; } else { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_USER).setLogCode("QLR0402301A4")("视频连线创建音频通道成功"); } } if (call->video) { video_session_destroy(call->video); call->video = NULL; } int i = 0; int remote_width = video_desc->param[i++]; int remote_height = video_desc->param[i++]; video_session_conf_t video_conf = {0}; video_conf.bit_rate = 256 * 1024; video_conf.local_rtp_ip = video_desc->local_ip; video_conf.local_rtp_port = video_desc->local_port; video_conf.mtu = ep_conf->mtu; video_conf.video_quant = ep_conf->quant; video_conf.remote_rtp_ip = video_desc->remote_ip; video_conf.remote_rtp_port = video_desc->remote_port; video_conf.remote_video_width = remote_width; video_conf.remote_video_height = remote_height; video_conf.ref_active_camera = call->ep->conf.ref_active_camera; video_conf.ref_camera_switch = call->ep->conf.ref_camera_switch; video_conf.ref_camera_state = call->ep->conf.ref_camera_state; video_conf.ref_window_state = call->ep->conf.ref_window_state; video_conf.ref_Is_showPersonArea = call->ep->conf.ref_Is_showPersonArea; video_conf.ref_active_img = NULL; video_conf.ref_Is_ActiveInspect = NULL; video_conf.camera_count = call->ep->conf.camera_count; video_conf.screen_count = call->ep->conf.screen_count; video_conf.ref_Up_Fps = call->ep->conf.ref_Up_Fps; video_conf.eDeviceType = call->eDeviceType; video_conf.nCallType = call->nCallType; memcpy(&video_conf.video_render_cb, cb, sizeof(rvc_video_render_callback_t)); video_conf.ilocal_wind_flags = VIDEOPLAYER_FLAG_PULL|VIDEOPLAYER_FLAG_CHECKTOP; video_conf.iremote_wind_flags = VIDEOPLAYER_FLAG_PUSH|VIDEOPLAYER_FLAG_CHECKTOP; video_conf.local_pt = REC_COMMON_VIDEO_H264_PT; video_conf.remote_pt = REC_COMMON_VIDEO_H264_PT; rc = video_session_create(&video_conf, &call->video); if (rc != 0) { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("create video session failed! rc = %d", rc); return; } rc = video_session_start(call->video); if (rc != 0) { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("start video session failed! rc = %d", rc); video_session_destroy(call->video); call->video = NULL; return; } } } #endif static void endpoint_media_update_audio(endpoint_call_t *call, media_desc_t *audio_desc, int dev_type) { if (audio_desc->media_dir == DIR_NONE) { if (call->audio) { audio_session_stop(call->audio); DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_USER).setLogCode("QLR0402301A6")("停止音频通道成功"); audio_session_destroy(call->audio); DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_USER).setLogCode("QLR0402301A7")("释放音频通道成功"); call->audio = NULL; } if (call->video) { // close video also video_session_stop(call->video); video_session_destroy(call->video); call->video = NULL; } else{ #ifdef RVC_OS_WIN if (DOUBLERECORD_CALLTYPE == call->nCallType){ double_record_broadcast_video_session_stop(); } #endif } } else { int rc; if (call->audio) { audio_session_stop(call->audio); DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_USER).setLogCode("QLR0402301A6")("停止音频通道成功"); audio_session_destroy(call->audio); DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_USER).setLogCode("QLR0402301A7")("释放音频通道成功"); call->audio = NULL; } if (!call->audio) { endpoint_conf_t *ep_conf = &call->ep->conf; audio_session_conf_t conf = {0}; strcpy(&conf.in_dev[DEV_PICKUP][0], ep_conf->audio_pickup_in_dev); strcpy(&conf.in_dev[DEV_HANDFREE][0], ep_conf->audio_handfree_in_dev); strcpy(&conf.out_dev[DEV_PICKUP][0], ep_conf->audio_pickup_out_dev); strcpy(&conf.out_dev[DEV_HANDFREE][0], ep_conf->audio_handfree_out_dev); conf.agc_in[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_in_agc; conf.agc_in[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_in_agc; conf.agc_out[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_out_agc; conf.agc_out[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_out_agc; conf.ns_in[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_in_ns; conf.ns_in[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_in_ns; conf.ns_out[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_out_ns; conf.ns_out[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_out_ns; conf.aec[DEV_PICKUP] = !!ep_conf->audio_dsp.audio_pickup_aec; conf.aec[DEV_HANDFREE] = !!ep_conf->audio_dsp.audio_handfree_aec; rc = audio_session_create(&conf, &call->audio); if (rc != 0) { DbgWithLink(LOG_LEVEL_ERROR, LOG_TYPE_SYSTEM)("create audio session failed! rc = %d", rc); DbgWithLink(LOG_LEVEL_WARN, LOG_TYPE_USER).setLogCode("QLR0402301A4").setResultCode("RTA3109")("视频连线创建音频通道失败"); return; } else { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_USER).setLogCode("QLR0402301A4")("视频连线创建音频通道成功"); } } audio_session_phonemedia_conf_t phone_conf = {0}; phone_conf.dir = audio_desc->media_dir; phone_conf.dev_type = (e_dev_type)dev_type; phone_conf.local_dtmf_pt = audio_desc->local_telephone_event_pt; phone_conf.local_pt = audio_desc->local_pt; phone_conf.local_ptime = audio_desc->local_ptime; phone_conf.local_rtp_ip = audio_desc->local_ip; phone_conf.local_rtp_port = audio_desc->local_port; phone_conf.remote_dtmf_pt = audio_desc->remote_telephone_event_pt; phone_conf.remote_pt = audio_desc->remote_pt; phone_conf.remote_ptime = audio_desc->remote_ptime; phone_conf.remote_rtp_ip = audio_desc->remote_ip; phone_conf.remote_rtp_port = audio_desc->remote_port; if (NULL != call){ phone_conf.eCalltype = call->nCallType; phone_conf.eDeviceType = call->eDeviceType; } rc = audio_session_start_phonemedia(call->audio, &phone_conf); if (rc != 0) { LogWarn(Severity_Middle, Error_Debug, ERROR_MOD_SIP_AUDIO_INITFAIL, CSimpleStringA::Format("start audio session failed! rc = %d.", rc).GetData()); audio_session_destroy(call->audio); call->audio = NULL; return; } else { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_USER).setLogCode("QLR0402301A5")("视频连线启动音频通道成功"); } } } static unsigned int __hash_media_desc(media_desc_t *media_desc, unsigned int hash_code) { if (media_desc) { hash_code += (unsigned int)media_desc->local_ip * 33; hash_code += (unsigned int)media_desc->remote_ip * 33; hash_code += (unsigned int)media_desc->local_port * 33; hash_code += (unsigned int)media_desc->remote_port * 33; hash_code += (unsigned int)media_desc->media_dir * 33; hash_code += (unsigned int)media_desc->local_pt * 33; hash_code += (unsigned int)media_desc->local_ptime * 33; hash_code += (unsigned int)media_desc->remote_pt * 33; hash_code += (unsigned int)media_desc->remote_ptime * 33; hash_code += (unsigned int)media_desc->local_telephone_event_pt * 33; hash_code += (unsigned int)media_desc->remote_telephone_event_pt * 33; } return hash_code; } static unsigned int hash_media_desc(media_desc_t *audio) { unsigned int hash_code = 0; hash_code = __hash_media_desc(audio, hash_code); return hash_code; } #define USE_ALAW 1 static const char *call_make_offer(endpoint_call_t *call, char *buf, int size) { int local_audio_rtp_port = call->local_media_port; int local_video_rtp_port = call->local_video_port; int local_video_pt = REC_COMMON_VIDEO_PT; int need; const char *fmt; { fmt = "c=IN IP4 %s\r\n" #ifdef USE_ALAW "m=audio %d RTP/AVP 8\r\n" "a=rtpmap:8 PCMA/8000\r\n" "a=ptime:30\r\n" #elif defined(USE_G729) "m=audio %d RTP/AVP 18\r\n" "a=rtpmap:18 G729/8000\r\n" "a=fmtp:18 annexb=no\r\n" "a=ptime:30\r\n" #endif //"a=rtpmap:97 telephone-event/8000\r\n" //"a=fmtp:97 0-15\r\n" #ifdef USE_H263_VIDEO "m=video %d RTP/AVP 34\r\n" "a=rtpmap:34 H263/90000\r\n" "a=fmtp:34 QCIF=2 MaxBR=5040\r\n" #endif //"a=inactive\r\n" //for debug only ; } need = _scprintf(fmt, call->local_ip, local_audio_rtp_port); if (size < 0 || size > need) { sprintf(buf, fmt, call->local_ip, local_audio_rtp_port); return buf; } else { return NULL; } } static int new_call_id(endpoint_t *ep) { return ++ep->call_seq; } int new_media_port(endpoint_t *ep) { ep->media_port_seq += 2; if (ep->media_port_seq >= ep->conf.media_stop_port) { ep->media_port_seq = ep->conf.media_start_port; } return ep->media_port_seq; } static void handle_invite(CONDITION_PARAMS) { // we are client only, not accept in-comming call nua_respond(nh, SIP_403_FORBIDDEN, TAG_END()); } static void handle_reinvite(CONDITION_PARAMS) { char sdp[512] = {0}; sip_content_type_t const *ct = NULL; tl_gets(tags, SIPTAG_CONTENT_TYPE_REF(ct), TAG_END()); if (ct) { call_make_offer(call, sdp, sizeof(sdp)); } nua_respond(nh, SIP_200_OK, TAG_IF(ct, SOATAG_USER_SDP_STR(sdp)), TAG_IF(ct, SOATAG_AUDIO_AUX("telephone-event")), TAG_END()); } static void handle_r_invite(CONDITION_PARAMS) { // nothing to do } static int dir_from_attr(const sdp_media_t *m) { int dir = DIR_NONE; if (m) { if (m->m_attributes) { static const char *ds[] = { "inactive", "sendonly", "recvonly", "sendrecv" }; int i; int n = array_size(ds); for (i = 0; i < n; ++i) { sdp_attribute_t *attr = sdp_attribute_find(m->m_attributes, ds[i]); if (attr) { dir = i; break; } } if (i >= n) dir = DIR_BOTH; } else { dir = DIR_BOTH; } } return dir; } static int ptime_from_attr(const sdp_media_t *m) { int ptime = 0; // zero for default if (m) { if (m->m_attributes) { sdp_attribute_t *attr = sdp_attribute_find(m->m_attributes, "ptime"); if (attr && attr->a_value) { ptime = atoi(attr->a_value); if (ptime < 0) ptime = 0; } } } return ptime; } static const sdp_rtpmap_t *find_codec(const sdp_rtpmap_t *list, const char *codec) { if (!codec) return NULL; for (;list; list=list->rm_next) { if (!_stricmp(list->rm_encoding, codec)) { break; } } return list; } static int get_telephone_event_pt(const sdp_rtpmap_t *list) { int pt = -1; const sdp_rtpmap_t *rm = find_codec(list, "telephone-event"); if (rm) { pt = rm->rm_pt; } return pt; } static int get_media_direction(const sdp_media_t *lm, const sdp_media_t *rm) { int media_dir = DIR_NONE; if (rm->m_port == 0 || lm->m_port == 0) { media_dir = DIR_NONE; } else { int ldir = dir_from_attr(lm); int rdir = dir_from_attr(rm); if (ldir == DIR_NONE || rdir == DIR_NONE) { media_dir = DIR_NONE; } else if (ldir == DIR_TX || rdir == DIR_RX) { media_dir = DIR_TX; } else if (ldir == DIR_RX || rdir == DIR_TX) { media_dir = DIR_RX; } else { media_dir = DIR_BOTH; } } return media_dir; } static int negotiate_audio(endpoint_call_t *call, media_desc_t *audio_desc, const sdp_media_t *lm, const sdp_media_t *rm) { memset(audio_desc, 0, sizeof(media_desc_t)); const char *local_ip; const char *remote_ip; local_ip = lm->m_connections ? lm->m_connections->c_address : lm->m_session->sdp_connection->c_address; remote_ip = rm->m_connections ? rm->m_connections->c_address : rm->m_session->sdp_connection->c_address; audio_desc->local_ip = strlen(call->local_ip) ? inet_addr(call->local_ip) : 0; audio_desc->local_port = call->local_media_port; audio_desc->remote_ip = remote_ip ? inet_addr(remote_ip) : 0; audio_desc->remote_port = rm->m_port; audio_desc->local_telephone_event_pt = 97; audio_desc->remote_telephone_event_pt = get_telephone_event_pt(rm->m_rtpmaps); audio_desc->local_ptime = ptime_from_attr(lm); audio_desc->remote_ptime = ptime_from_attr(rm); int pt = -1; for (sdp_rtpmap_t *t = lm->m_rtpmaps; t; t = t->rm_next) { sdp_rtpmap_t *tt = sdp_rtpmap_find_matching(rm->m_rtpmaps, t); if (tt && tt->rm_pt < 96) { pt = tt->rm_pt; break; } } audio_desc->local_pt = audio_desc->remote_pt = pt; audio_desc->media_dir = get_media_direction(lm, rm); return 0; } static int negotiate_video(endpoint_call_t *call, media_desc_t *video_desc, const sdp_media_t *lm, const sdp_media_t *rm) { memset(video_desc, 0, sizeof(media_desc_t)); const char *local_ip; const char *remote_ip; local_ip = lm->m_connections ? lm->m_connections->c_address : lm->m_session->sdp_connection->c_address; remote_ip = rm->m_connections ? rm->m_connections->c_address : rm->m_session->sdp_connection->c_address; video_desc->local_ip = strlen(call->local_ip) ? inet_addr(call->local_ip) : 0; video_desc->local_port = call->local_video_port; video_desc->remote_ip = remote_ip ? inet_addr(remote_ip) : 0; video_desc->remote_port = rm->m_port; video_desc->local_telephone_event_pt = 97; video_desc->remote_telephone_event_pt = get_telephone_event_pt(rm->m_rtpmaps); video_desc->local_ptime = ptime_from_attr(lm); video_desc->remote_ptime = ptime_from_attr(rm); int pt = -1; for (sdp_rtpmap_t *t = lm->m_rtpmaps; t; t = t->rm_next) { sdp_rtpmap_t *tt = sdp_rtpmap_find_matching(rm->m_rtpmaps, t); if (tt) { pt = tt->rm_pt; break; } } video_desc->local_pt = video_desc->remote_pt = pt; video_desc->media_dir = get_media_direction(lm, rm); return 0; } static void negotiate_sdp(endpoint_call_t *call, const sdp_session_t *local_sdp, const sdp_session_t* remote_sdp) { const sdp_media_t *lm = local_sdp->sdp_media; const sdp_media_t *rm = remote_sdp->sdp_media; media_desc_t audio_desc = {0}, video_desc = {0}; int rc = -1, rv = -1; for(; lm; lm = lm->m_next) { if (0 == lm->m_port) { continue; } const sdp_media_t *rm = remote_sdp->sdp_media; for (; rm; rm = rm->m_next) { if (rm->m_type == lm->m_type) { if (sdp_media_audio == rm->m_type) { rc = negotiate_audio(call, &audio_desc, lm, rm); } if (call->nCallType != NORMAL_CALLTYPE && call->nCallType != DOUBLERECORD_CALLTYPE) { if (sdp_media_video == rm->m_type) { rv = negotiate_video(call, &video_desc, lm, rm); } } } } } if (rc == 0) { unsigned int hash_code = hash_media_desc(&audio_desc); if (hash_code != call->last_media_desc_hash) { endpoint_media_update_audio(call, &audio_desc, call->ep->curr_audio_dev_type); call->last_media_desc_hash = hash_code; //char strmsg[MAX_PATH] = {0}; //snprintf(strmsg, MAX_PATH, "negotiate audio success!local audio ip=%s port=%d pt=%d,remote audio ip=%s port=%d,pt=%d",str_local,audio_desc.local_port,audio_desc.local_pt,str_remote,audio_desc.remote_port,audio_desc.remote_pt); //LogWarn(Severity_Low, Error_Debug, EVENT_MOD_SIP_CALL_AUDIO_NEGOTIATE, strmsg); } } if (rv == 0) { unsigned int hash_code = hash_media_desc(&video_desc); if (hash_code != call->last_media_desc_hash) { call->sdpvieo_desc.local_pt = video_desc.local_pt; call->sdpvieo_desc.local_rtp_ip = video_desc.local_ip; call->sdpvieo_desc.local_rtp_port = video_desc.local_port; call->sdpvieo_desc.remote_pt = video_desc.remote_pt; call->sdpvieo_desc.remote_rtp_ip = video_desc.remote_ip; call->sdpvieo_desc.remote_rtp_port = video_desc.remote_port; call->last_media_desc_hash = hash_code; //char strmsg[MAX_PATH] = {0}; //_snprintf(strmsg, MAX_PATH, "negotiate video success!local video ip=%s port=%d pt=%d,remote video ip=%s port=%d pt=%d",str_local,video_desc.local_port,video_desc.local_pt,str_remote,video_desc.remote_port,video_desc.remote_pt); //LogWarn(Severity_Low, Error_Debug, EVENT_MOD_SIP_CALL_VIDEO_NEGOTIATE, strmsg); } } else { if (call->nCallType != NORMAL_CALLTYPE && call->nCallType != DOUBLERECORD_CALLTYPE) { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("negotiate video Fail!!!"); } } } static void on_state(CONDITION_PARAMS) { int offer_recv = 0, answer_recv = 0, offer_sent = 0, answer_sent = 0; int state = nua_callstate_init; const char *replaces_str = NULL; const sdp_session_t *remote_sdp = NULL; const char *remote_sdp_str = NULL; const sdp_session_t *local_sdp = NULL; const char *local_sdp_str = NULL; int st; if (!call) { nua_handle_destroy(nh); return; } if (g_IsExternalTerminalted) { return; } tl_gets(tags, NUTAG_CALLSTATE_REF(state), NUTAG_OFFER_RECV_REF(offer_recv), NUTAG_ANSWER_RECV_REF(answer_recv), NUTAG_OFFER_SENT_REF(offer_sent), NUTAG_ANSWER_SENT_REF(answer_sent), SIPTAG_REPLACES_STR_REF(replaces_str), SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str), SOATAG_LOCAL_SDP_STR_REF(local_sdp_str), TAG_END()); switch (state) { case nua_callstate_calling: { if (local_sdp_str) { sdp_parser_t* parser = sdp_parse(call->home, local_sdp_str, strlen(local_sdp_str), -1); assert(call->last_sdp == NULL); call->last_sdp = sdp_session_dup(call->home, sdp_session(parser)); sdp_parser_free(parser); } st = CALLING; call->cb.on_call_state(st, state_desc[st], phrase, call->cb.user_data); } break; case nua_callstate_proceeding: { st = PROCEEDING; call->cb.on_call_state(st, state_desc[st], phrase, call->cb.user_data); if (remote_sdp_str) { sdp_parser_t* parser = sdp_parse(call->home, remote_sdp_str, strlen(remote_sdp_str), -1); remote_sdp = sdp_session(parser); if (call->last_sdp) { negotiate_sdp(call, call->last_sdp, remote_sdp); su_free(call->home, call->last_sdp); call->last_sdp = NULL; } sdp_parser_free(parser); } } break; case nua_callstate_completing: { st = COMPLETING; call->cb.on_call_state(st, state_desc[st], phrase, call->cb.user_data); if (remote_sdp_str) { sdp_parser_t* parser = sdp_parse(call->home, remote_sdp_str, strlen(remote_sdp_str), -1); remote_sdp = sdp_session(parser); if (call->last_sdp) { negotiate_sdp(call, call->last_sdp, remote_sdp); su_free(call->home, call->last_sdp); call->last_sdp = NULL; } sdp_parser_free(parser); } } break; case nua_callstate_completed: { if (remote_sdp_str) { if (!call->last_sdp) { sdp_parser_t* parser = sdp_parse(call->home, remote_sdp_str, strlen(remote_sdp_str), -1); call->last_sdp = sdp_session_dup(call->home, sdp_session(parser)); sdp_parser_free(parser); } } if (local_sdp_str) { if (call->last_sdp) { sdp_parser_t* parser = sdp_parse(call->home, local_sdp_str, strlen(local_sdp_str), -1); local_sdp = sdp_session(parser); negotiate_sdp(call, local_sdp, call->last_sdp); su_free(call->home, call->last_sdp); call->last_sdp = NULL; sdp_parser_free(parser); } } } break; case nua_callstate_ready: { call->connected = true; st = READY; call->cb.on_call_state(st, state_desc[st], phrase, call->cb.user_data); if (remote_sdp_str) { sdp_parser_t* parser = sdp_parse(call->home, remote_sdp_str, strlen(remote_sdp_str), -1); remote_sdp = sdp_session(parser); if (call->last_sdp) { negotiate_sdp(call, call->last_sdp, remote_sdp); su_free(call->home, call->last_sdp); call->last_sdp = NULL; } sdp_parser_free(parser); } #if 0 if (remote_sdp_str) { sdp_parser_t* parser = sdp_parse(call->home, remote_sdp_str, strlen(remote_sdp_str), -1); remote_sdp = sdp_session(parser); negotiate_sdp(call, call->last_sdp, remote_sdp); sdp_parser_free(parser); } else if (local_sdp_str) { sdp_parser_t* parser = sdp_parse(call->home, local_sdp_str, strlen(local_sdp_str), -1); local_sdp = sdp_session(parser); negotiate_sdp(call, local_sdp, call->last_sdp); sdp_parser_free(parser); } #endif } break; case nua_callstate_terminated: { st = TERMINATED; { media_desc_t audio_desc = { 0 }; audio_desc.media_dir = DIR_NONE; endpoint_media_update_audio(call, &audio_desc, call->ep->curr_audio_dev_type); } call->cb.on_call_state(st, state_desc[st], phrase, call->cb.user_data); nua_handle_bind(nh, NULL); nua_handle_destroy(nh); call->nh = NULL; } break; default: break; } } void terminatedcall(endpoint_call_t *call) { int st = TERMINATED; g_IsExternalTerminalted = true; { media_desc_t audio_desc = {0}; audio_desc.media_dir = DIR_NONE; endpoint_media_update_audio(call, &audio_desc, call->ep->curr_audio_dev_type); } } static void endpoint_callback(nua_event_t event, int status, char const *phrase, nua_t *nua, nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic, sip_t const *sip, tagi_t tags[]) { switch (event) { if (!g_IsExternalTerminalted) { case nua_i_invite: if (hmagic == NULL) { handle_invite(status, phrase, nua, (endpoint_t*)magic, nh, (endpoint_call_t*)hmagic, sip, tags); } else { handle_reinvite(status, phrase, nua, (endpoint_t*)magic, nh, (endpoint_call_t*)hmagic, sip, tags); } break; case nua_r_invite: handle_r_invite(status, phrase, nua, (endpoint_t*)magic, nh, (endpoint_call_t*)hmagic, sip, tags); break; case nua_i_bye: //..... break; case nua_i_info: break; case nua_i_state: on_state(status, phrase, nua, (endpoint_t*)magic, nh, (endpoint_call_t*)hmagic, sip, tags); break; case nua_i_active: break; case nua_i_options: //nua_respond(nh, SIP_200_OK, NUTAG_WITH_THIS(nua), SOATAG_USER_SDP_STR(NULL), TAG_END()); nua_respond(nh, SIP_200_OK, TAG_END()); break; case nua_r_cancel: if (status > 300) { nua_bye(nh, TAG_END()); } break; case nua_i_prack: case nua_r_prack: break; case nua_r_bye: //LOG_TRACE("nua_r_bye"); if (status > 300) { nua_bye(nh, TAG_END()); } break; /* and so on ... */ default: if (status > 100) { /* unknown event -> print out error message */ DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("unknown event %d: %03d %s.", event, status, phrase); } else { DbgWithLink(LOG_LEVEL_DEBUG, LOG_TYPE_SYSTEM)("unknown event %d.", event); } break; } } if (!hmagic) { nua_handle_destroy(nh); return; } } #ifdef RVC_OS_WIN static unsigned int __stdcall __event_thread(void* arg) #else void* __event_thread(void* arg) #endif { endpoint_t *ep = (endpoint_t *)arg; ep->entity->GetFunction()->InitLogCurrentThread(); #ifdef RVC_OS_WIN CoInitialize(NULL); #else #endif // RVC_OS_WIN ep->root = su_root_create(ep); ep->nua = nua_create(ep->root, &endpoint_callback, ep, NUTAG_URL(ep->conf.uri), TAG_END()); nua_set_params(ep->nua, SIPTAG_ALLOW_STR("INVITE,CANCEL,BYE,ACK,INFO,OPTIONS"), NUTAG_AUTOALERT(1), NUTAG_ALLOW("PRACK"), NUTAG_EARLY_MEDIA(1), NUTAG_SESSION_TIMER(1), NUTAG_AUTOANSWER(0), NUTAG_AUTOACK(1), NTATAG_SIP_T1X64(8000), TAG_NULL()); su_root_run(ep->root); nua_shutdown(ep->nua); ep->nua = NULL; su_root_destroy(ep->root); ep->root = NULL; #ifdef RVC_OS_WIN CoUninitialize(); #else #endif // RVC_OS_WIN return 0; } static int __endpoint_break(void *arg) { endpoint_t *ep = (endpoint_t *)arg; //.....nua_shutdown su_root_break(ep->root); return 0; } static endpoint_t *__endpoint_create(CEntityBase *pEntity, const endpoint_conf_t *conf,int nDev) { endpoint_t *ep; ep = ZALLOC_T(endpoint_t); su_home_init(ep->home); memcpy(&ep->conf, conf, sizeof(endpoint_conf_t)); ep->media_port_seq = (conf->media_start_port + (SP::Module::Comm::RVCGetTickCount() % (conf->media_stop_port - conf->media_start_port))) & 0xfffc; ep->entity = pEntity; ep->call_seq = SP::Module::Comm::RVCGetTickCount(); ep->curr_audio_dev_type = nDev; INIT_LIST_HEAD(&ep->call_list); return ep; } static void __endpoint_destroy(endpoint_t *ep) { su_root_destroy(ep->root); su_home_deinit(ep->home); free(ep); } static int __endpoint_start(endpoint_t *ep) { #ifdef RVC_OS_WIN ep->event_thread = (HANDLE)_beginthreadex(NULL, 0, &__event_thread, ep, 0, NULL); if (ep->event_thread) { while (!ep->root) { DWORD dwRet = WaitForSingleObject(ep->event_thread, 1); if (dwRet == WAIT_OBJECT_0) { CloseHandle(ep->event_thread); ep->event_thread = NULL; break; } } } return ep->event_thread ? 0 : -1; #else int err = pthread_create(&ep->ievent_threadid, NULL, __event_thread, ep); if (0 == err) { while (!ep->root) { struct timespec ts; clock_gettime(CLOCK_REALTIME, &ts); long unsec = ts.tv_nsec + (1000 * 1000); ts.tv_sec += (unsec / 1000000000); ts.tv_nsec = (unsec % 1000000000); if (0 == pthread_timedjoin_np(ep->ievent_threadid, NULL, &ts)) { ep->ievent_threadid = 0; break; } } } else { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("create event thread failed."); } return ep->ievent_threadid > 0 ? 0 : -1; #endif } static void __endpoint_stop(endpoint_t *ep) { #ifdef RVC_OS_WIN if (ep->event_thread) { int rc; su_task_execute(su_root_task(ep->root), &__endpoint_break, ep, &rc); WaitForSingleObject(ep->event_thread, INFINITE); CloseHandle(ep->event_thread); ep->event_thread = NULL; } #else if (ep->ievent_threadid > 0) { int rc; su_task_execute(su_root_task(ep->root), &__endpoint_break, ep, &rc); if (0 == pthread_join(ep->ievent_threadid, NULL)) { ep->ievent_threadid = 0; } else { DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("pthread join error for %s", strerror(errno)); } } #endif } static void rvc_sofia_logger(void* stream, char const* fmt, va_list ap) { if (!fmt) return; char line[255]; vsnprintf(line, sizeof(line), fmt, ap); DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("rvc_sofia_logger : %s", line); } int endpoint_init_lib() { int result = su_init(); //su_log_set_level(NULL, 9); //su_log_redirect(NULL, rvc_sofia_logger, NULL); return result; } void endpoint_deinit_lib() { su_deinit(); } endpoint_t *endpoint_create(CEntityBase *pEntity, const endpoint_conf_t *conf,int nDev) { endpoint_t *endpt; endpt = __endpoint_create(pEntity, conf,nDev); if (endpt) { int rc = __endpoint_start(endpt); if (rc != 0) { __endpoint_destroy(endpt); endpt = NULL; } } return endpt; } void endpoint_destroy(endpoint_t *ep) { if (ep) { __endpoint_stop(ep); __endpoint_destroy(ep); } } int endpoint_invoke(endpoint_t *ep, int (*func)(void*), void *user_data, int *result) { if (ep){ return su_task_execute(su_root_task(ep->root), func, user_data, result); } else { *result = Error_Param; return -1; } } void endpoint_change_audio_dev(endpoint_t *ep, int dev_type) { if (ep){ if (ep->curr_audio_dev_type != dev_type) { ep->curr_audio_dev_type = dev_type; if (ep->active_call) { endpoint_media_change_audio_dev(ep->active_call, (e_dev_type)dev_type); } } } } endpoint_call_t *endpoint_call_create(endpoint_t *ep, const endpoint_call_params_t* pcallparam, const endpoint_call_callback_t *cb) { endpoint_call_t *call = NULL; su_home_t *home = NULL; if (!pcallparam || !pcallparam->to_uri){ return NULL; } home = su_home_create(); if (!home){ goto on_error; } call = (endpoint_call_t*)su_home_new(sizeof(endpoint_call_t)); if (!call){ goto on_error; } memset(call, 0, sizeof(endpoint_call_t)); call->connected = 0; call->ep = ep; call->home = home; call->last_media_desc_hash = 0; call->to_uri = su_strdup(home, pcallparam->to_uri); if (pcallparam->from_uri != NULL){ call->from_uri = su_strdup(home, pcallparam->from_uri); } if (pcallparam->call_id != NULL){ call->call_id = su_strdup(home, pcallparam->call_id); } if (NULL != pcallparam->local_ip){ _snprintf(call->local_ip, RVC_MAX_IP_LEN, "%s", pcallparam->local_ip); } call->eDeviceType = pcallparam->nDeviceType; call->nCallType = pcallparam->nCallType; memcpy(&call->cb, cb, sizeof(endpoint_call_callback_t)); call->id = new_call_id(ep); call->local_media_port = pcallparam->ilocal_audio_port; call->local_video_port = pcallparam->ilocal_video_port; REF_COUNT_INIT(&call->ref_cnt); list_add_tail(&call->entry, &ep->call_list); ep->active_call = call; return call; on_error: if (home) su_home_unref(home); return call; } void endpoint_call_destroy(endpoint_call_t *call) { endpoint_call_dec_ref(call); } int endpoint_call_start(endpoint_call_t *call) { char sdp[512] = {0}; if (call->nh) return -1; if (call->from_uri != NULL && call->call_id != NULL){ call->nh = nua_handle(call->ep->nua, call, SIPTAG_CALL_ID_STR(call->call_id), SIPTAG_FROM_STR(call->from_uri), SIPTAG_TO_STR(call->to_uri), TAG_END()); } else if (call->from_uri != NULL){ call->nh = nua_handle(call->ep->nua, call, SIPTAG_FROM_STR(call->from_uri), SIPTAG_TO_STR(call->to_uri), TAG_END()); }else if (call->call_id != NULL){ call->nh = nua_handle(call->ep->nua, call, SIPTAG_FROM_STR(call->ep->conf.uri), SIPTAG_CALL_ID_STR(call->call_id), SIPTAG_TO_STR(call->to_uri), TAG_END()); }else{ call->nh = nua_handle(call->ep->nua, call, SIPTAG_FROM_STR(call->ep->conf.uri), SIPTAG_TO_STR(call->to_uri), TAG_END()); } call_make_offer(call, sdp, -1); if (0 == strlen(sdp)){ LogWarn(Severity_Middle, Error_Debug, EVENT_MOD_SIP_INVALID_SDP, "invalid sdp."); } nua_invite(call->nh, //NUTAG_URL(call->remote_uri), SOATAG_USER_SDP_STR(sdp), NUTAG_SESSION_TIMER(20000), //SOATAG_AUDIO_AUX("telephone-event"), TAG_END()); return 0; } int endpoint_call_hangup(endpoint_call_t *call) { if (call->nh) { if (call->connected) { nua_bye(call->nh, NUTAG_SESSION_TIMER(8000),TAG_END()); } else { nua_cancel(call->nh, NUTAG_SESSION_TIMER(8000),TAG_END()); } } return 0; } #ifdef RVC_OS_WIN int endpoint_call_start_video(endpoint_call_t *call, unsigned long remote_ip, int remote_video_rtp, unsigned long local_ip, int local_video_rtp, int remote_width, int remote_height, int local_view_x, int local_view_y, int local_view_cx, int local_view_cy, int remote_view_x, int remote_view_y, int remote_view_cx, int remote_view_cy, int local_move, int remote_move, video_session_callback_t* cb) { if (call) { int i = 0; media_desc_t video_desc = {0}; video_desc.media_dir = DIR_BOTH; if (call->nCallType == NORMAL_CALLTYPE || call->nCallType == DOUBLERECORD_CALLTYPE)//如果是可视柜台模式,使用协助通道的协商结果 { video_desc.local_ip = local_ip; video_desc.local_port = local_video_rtp; video_desc.remote_port = remote_video_rtp; video_desc.remote_ip = remote_ip; } else //如果是非可视柜台模式,使用SDP协商的参数 { video_desc.local_ip = call->sdpvieo_desc.local_rtp_ip; video_desc.local_port = call->sdpvieo_desc.local_rtp_port; video_desc.local_pt = call->sdpvieo_desc.local_pt; video_desc.remote_ip = call->sdpvieo_desc.remote_rtp_ip; video_desc.remote_port = call->sdpvieo_desc.remote_rtp_port; video_desc.remote_pt = call->sdpvieo_desc.remote_pt; } video_desc.param[i++] = local_view_x; video_desc.param[i++] = local_view_y; video_desc.param[i++] = local_view_cx; video_desc.param[i++] = local_view_cy; video_desc.param[i++] = remote_view_x; video_desc.param[i++] = remote_view_y; video_desc.param[i++] = remote_view_cx; video_desc.param[i++] = remote_view_cy; video_desc.param[i++] = remote_width; video_desc.param[i++] = remote_height; video_desc.param[i++] = local_move; video_desc.param[i++] = remote_move; endpoint_media_update_video(call, &video_desc, cb); return 0; } else { return Error_Param; } } #else int endpoint_call_start_video(endpoint_call_t* call, endpoint_call_param_t* pcallparam, rvc_video_render_callback_t* render_cb) { if (call) { int i = 0; media_desc_t video_desc = {0}; video_desc.media_dir = DIR_BOTH; if (call->nCallType == NORMAL_CALLTYPE || call->nCallType == DOUBLERECORD_CALLTYPE)//如果是可视柜台模式,使用协助通道的协商结果 { video_desc.local_ip = pcallparam->local_ip; video_desc.local_port = pcallparam->local_port; video_desc.remote_port = pcallparam->remote_port; video_desc.remote_ip = pcallparam->remote_ip; } else //如果是非可视柜台模式,使用SDP协商的参数 { video_desc.local_ip = call->sdpvieo_desc.local_rtp_ip; video_desc.local_port = call->sdpvieo_desc.local_rtp_port; video_desc.local_pt = call->sdpvieo_desc.local_pt; video_desc.remote_ip = call->sdpvieo_desc.remote_rtp_ip; video_desc.remote_port = call->sdpvieo_desc.remote_rtp_port; video_desc.remote_pt = call->sdpvieo_desc.remote_pt; } video_desc.param[i++] = pcallparam->remote_width; video_desc.param[i++] = pcallparam->remote_height; endpoint_media_update_video(call, &video_desc, render_cb); return 0; } else { return Error_Param; } } #endif int endpoint_call_stop_video(endpoint_call_t *call) { if (call) { media_desc_t video_desc = {0}; video_desc.media_dir = DIR_NONE; endpoint_media_update_video(call, &video_desc,NULL); return 0; } else { return Error_Param; } } #ifdef RVC_OS_WIN int endpoint_call_stop_double_record_broadcast_video() { double_record_broadcast_video_session_stop(); return Error_Succeed; } int local_remote_show_video(endpoint_call_t *call,int local_view_x, int local_view_y, int local_view_cx, int local_view_cy,int remote_view_x, int remote_view_y, int remote_view_cx, int remote_view_cy, int local_move, int remote_move, video_session_callback_t* cb) { int rc; endpoint_conf_t *ep_conf = &call->ep->conf; if (call->video) { video_session_destroy(call->video); call->video = NULL; } int remote_video_width = REC_COMMON_VIDEO_SSM_AGENT_WIDTH; int remote_video_height = REC_COMMON_VIDEO_SSM_AGENT_HEIGHT; if (eStand2sType == call->eDeviceType){ remote_video_width = REC_COMMON_VIDEO_DSM_AGENT_WIDTH; remote_video_height = REC_COMMON_VIDEO_DSM_AGENT_HEIGHT; } video_session_conf_t video_conf = {0}; video_conf.bit_rate = 256 * 1024; video_conf.local_rtp_ip = 0; video_conf.local_rtp_port = 0; video_conf.local_video_view_x = local_view_x; video_conf.local_video_view_y = local_view_y; video_conf.local_video_view_cx = local_view_cx; video_conf.local_video_view_cy = local_view_cy; video_conf.local_move = local_move; video_conf.mtu = ep_conf->mtu; video_conf.video_quant = ep_conf->quant; video_conf.remote_rtp_ip = 0; video_conf.remote_rtp_port = 0; video_conf.remote_video_view_x = remote_view_x; video_conf.remote_video_view_y = remote_view_y; video_conf.remote_video_view_cx = remote_view_cx; video_conf.remote_video_view_cy = remote_view_cy; video_conf.remote_move = remote_move; video_conf.remote_video_width = remote_video_width; video_conf.remote_video_height = remote_video_height; video_conf.ref_active_camera = call->ep->conf.ref_active_camera; video_conf.ref_camera_switch = call->ep->conf.ref_camera_switch; video_conf.ref_camera_state = call->ep->conf.ref_camera_state; video_conf.ref_window_state = call->ep->conf.ref_window_state; video_conf.ref_active_img = call->ep->conf.ref_active_img; video_conf.ref_Is_ActiveInspect = call->ep->conf.ref_Is_ActiveInspect; video_conf.ref_Is_showPersonArea = call->ep->conf.ref_Is_showPersonArea; video_conf.camera_count = call->ep->conf.camera_count; video_conf.screen_count = call->ep->conf.screen_count; video_conf.eDeviceType = call->eDeviceType; video_conf.video_echo_cb = cb; video_conf.ilocal_wind_flags = VIDEOPLAYER_FLAG_PULL|VIDEOPLAYER_FLAG_CHECKTOP; video_conf.iremote_wind_flags = VIDEOPLAYER_FLAG_PUSH|VIDEOPLAYER_FLAG_CHECKTOP; video_conf.eType = (eVideoRenderType)call->ep->conf.irendertype; rc = local_video_session_create(&video_conf, &call->video, true); if (rc != 0){ DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("create video session failed! rc = %d", rc); return -1; } rc = video_session_start(call->video); if (rc != 0){ DbgWithLink(LOG_LEVEL_INFO, LOG_TYPE_SYSTEM)("start video session failed! rc = %d", rc); video_session_destroy(call->video); call->video = NULL; return -1; } return 0; } int local_play_stop_video(endpoint_call_t *call) { if (call) { media_desc_t video_desc = {0}; video_desc.media_dir = DIR_NONE; endpoint_media_update_video(call, &video_desc,NULL); return 0; } else { return Error_Param; } } #endif static void __endpoint_call_destroy(endpoint_call_t *call) { list_del(&call->entry); if (call->ep && call->ep->active_call == call) { call->ep->active_call = NULL; } su_home_unref(call->home); } IMPLEMENT_REF_COUNT_MT_STATIC(endpoint_call, endpoint_call_t, ref_cnt, __endpoint_call_destroy)