📄 pjsua_media.c
字号:
}
/* Destroy media endpoint. */
if (pjsua_var.med_endpt) {
/* Shutdown all codecs: */
# if PJMEDIA_HAS_SPEEX_CODEC
pjmedia_codec_speex_deinit();
# endif /* PJMEDIA_HAS_SPEEX_CODEC */
# if PJMEDIA_HAS_GSM_CODEC
pjmedia_codec_gsm_deinit();
# endif /* PJMEDIA_HAS_GSM_CODEC */
# if PJMEDIA_HAS_G711_CODEC
pjmedia_codec_g711_deinit();
# endif /* PJMEDIA_HAS_G711_CODEC */
# if PJMEDIA_HAS_L16_CODEC
pjmedia_codec_l16_deinit();
# endif /* PJMEDIA_HAS_L16_CODEC */
pjmedia_endpt_destroy(pjsua_var.med_endpt);
pjsua_var.med_endpt = NULL;
/* Deinitialize sound subsystem */
pjmedia_snd_deinit();
}
/* Reset RTP port */
next_rtp_port = 0;
return PJ_SUCCESS;
}
/* Create normal UDP media transports */
static pj_status_t create_udp_media_transports(pjsua_transport_config *cfg)
{
unsigned i;
pjmedia_sock_info skinfo;
pj_status_t status;
/* Create each media transport */
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
status = create_rtp_rtcp_sock(cfg, &skinfo);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create RTP/RTCP socket",
status);
goto on_error;
}
status = pjmedia_transport_udp_attach(pjsua_var.med_endpt, NULL,
&skinfo, 0,
&pjsua_var.calls[i].med_tp);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create media transport",
status);
goto on_error;
}
pjmedia_transport_udp_simulate_lost(pjsua_var.calls[i].med_tp,
PJMEDIA_DIR_ENCODING,
pjsua_var.media_cfg.tx_drop_pct);
pjmedia_transport_udp_simulate_lost(pjsua_var.calls[i].med_tp,
PJMEDIA_DIR_DECODING,
pjsua_var.media_cfg.rx_drop_pct);
}
return PJ_SUCCESS;
on_error:
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
if (pjsua_var.calls[i].med_tp != NULL) {
pjmedia_transport_close(pjsua_var.calls[i].med_tp);
pjsua_var.calls[i].med_tp = NULL;
}
}
return status;
}
/* Create ICE media transports (when ice is enabled) */
static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
{
unsigned i;
pj_sockaddr_in addr;
pj_status_t status;
/* Make sure STUN server resolution has completed */
status = pjsua_resolve_stun_server(PJ_TRUE);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
return status;
}
pj_sockaddr_in_init(&addr, 0, (pj_uint16_t)cfg->port);
/* Create each media transport */
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
pj_ice_strans_comp comp;
int next_port;
status = pjmedia_ice_create(pjsua_var.med_endpt, NULL, 2,
&pjsua_var.stun_cfg,
&pjsua_var.calls[i].med_tp);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create ICE media transport",
status);
goto on_error;
}
status = pjmedia_ice_start_init(pjsua_var.calls[i].med_tp, 0, &addr,
&pjsua_var.stun_srv.ipv4, NULL);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error starting ICE transport",
status);
goto on_error;
}
pjmedia_ice_get_comp(pjsua_var.calls[i].med_tp, 1, &comp);
next_port = pj_ntohs(comp.local_addr.ipv4.sin_port);
next_port += 2;
addr.sin_port = pj_htons((pj_uint16_t)next_port);
}
/* Wait until all ICE transports are ready */
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
/* Wait until interface status is PJ_SUCCESS */
for (;;) {
status = pjmedia_ice_get_init_status(pjsua_var.calls[i].med_tp);
if (status == PJ_EPENDING)
pjsua_handle_events(100);
else
break;
}
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE,
"Error adding STUN address to ICE media transport",
status);
goto on_error;
}
}
return PJ_SUCCESS;
on_error:
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
if (pjsua_var.calls[i].med_tp != NULL) {
pjmedia_transport_close(pjsua_var.calls[i].med_tp);
pjsua_var.calls[i].med_tp = NULL;
}
}
return status;
}
/*
* Create UDP media transports for all the calls. This function creates
* one UDP media transport for each call.
*/
PJ_DEF(pj_status_t)
pjsua_media_transports_create(const pjsua_transport_config *app_cfg)
{
pjsua_transport_config cfg;
unsigned i;
pj_status_t status;
/* Make sure pjsua_init() has been called */
PJ_ASSERT_RETURN(pjsua_var.ua_cfg.max_calls>0, PJ_EINVALIDOP);
PJSUA_LOCK();
/* Delete existing media transports */
for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
if (pjsua_var.calls[i].med_tp != NULL) {
pjmedia_transport_close(pjsua_var.calls[i].med_tp);
pjsua_var.calls[i].med_tp = NULL;
}
}
/* Copy config */
pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg);
if (pjsua_var.media_cfg.enable_ice) {
status = create_ice_media_transports(&cfg);
} else {
status = create_udp_media_transports(&cfg);
}
PJSUA_UNLOCK();
return status;
}
pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
pjsip_role_e role)
{
pjsua_call *call = &pjsua_var.calls[call_id];
if (pjsua_var.media_cfg.enable_ice) {
pj_ice_sess_role ice_role;
pj_status_t status;
ice_role = (role==PJSIP_ROLE_UAC ? PJ_ICE_SESS_ROLE_CONTROLLING :
PJ_ICE_SESS_ROLE_CONTROLLED);
/* Restart ICE */
pjmedia_ice_stop_ice(call->med_tp);
status = pjmedia_ice_init_ice(call->med_tp, ice_role, NULL, NULL);
if (status != PJ_SUCCESS)
return status;
}
return PJ_SUCCESS;
}
pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
pj_pool_t *pool,
pjmedia_sdp_session **p_sdp)
{
pjmedia_sdp_session *sdp;
pjmedia_sock_info skinfo;
pjsua_call *call = &pjsua_var.calls[call_id];
pj_status_t status;
/* Return error if media transport has not been created yet
* (e.g. application is starting)
*/
if (call->med_tp == NULL) {
return PJ_EBUSY;
}
/* Get media socket info */
pjmedia_transport_get_info(call->med_tp, &skinfo);
/* Create SDP */
status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pool, 1,
&skinfo, &sdp);
if (status != PJ_SUCCESS)
goto on_error;
if (pjsua_var.media_cfg.enable_ice) {
status = pjmedia_ice_modify_sdp(call->med_tp, pool, sdp);
if (status != PJ_SUCCESS)
goto on_error;
}
*p_sdp = sdp;
return PJ_SUCCESS;
on_error:
pjsua_media_channel_deinit(call_id);
return status;
}
static void stop_media_session(pjsua_call_id call_id)
{
pjsua_call *call = &pjsua_var.calls[call_id];
if (call->conf_slot != PJSUA_INVALID_ID) {
pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);
call->conf_slot = PJSUA_INVALID_ID;
}
if (call->session) {
pjmedia_session_destroy(call->session);
call->session = NULL;
PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed",
call_id));
}
call->media_st = PJSUA_CALL_MEDIA_NONE;
}
pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id)
{
pjsua_call *call = &pjsua_var.calls[call_id];
stop_media_session(call_id);
if (pjsua_var.media_cfg.enable_ice) {
pjmedia_ice_stop_ice(call->med_tp);
}
return PJ_SUCCESS;
}
/*
* DTMF callback from the stream.
*/
static void dtmf_callback(pjmedia_stream *strm, void *user_data,
int digit)
{
PJ_UNUSED_ARG(strm);
if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
pjsua_call_id call_id;
call_id = (pjsua_call_id)user_data;
pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit);
}
}
pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
const pjmedia_sdp_session *local_sdp,
const pjmedia_sdp_session *remote_sdp)
{
int prev_media_st = 0;
pjsua_call *call = &pjsua_var.calls[call_id];
pjmedia_session_info sess_info;
pjmedia_port *media_port;
pj_str_t port_name;
char tmp[PJSIP_MAX_URL_SIZE];
pj_status_t status;
/* Destroy existing media session, if any. */
prev_media_st = call->media_st;
stop_media_session(call->index);
/* Create media session info based on SDP parameters.
* We only support one stream per session at the moment
*/
status = pjmedia_session_info_from_sdp( call->inv->dlg->pool,
pjsua_var.med_endpt,
1,&sess_info,
local_sdp, remote_sdp);
if (status != PJ_SUCCESS)
return status;
/* Check if media is put on-hold */
if (sess_info.stream_cnt == 0 ||
sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE)
{
/* Determine who puts the call on-hold */
if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
/* It was local who offer hold */
call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
} else {
call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
}
}
call->media_dir = PJMEDIA_DIR_NONE;
/* Shutdown ICE session */
if (pjsua_var.media_cfg.enable_ice) {
pjmedia_ice_stop_ice(call->med_tp);
}
/* No need because we need keepalive? */
} else {
/* Start ICE */
if (pjsua_var.media_cfg.enable_ice) {
status = pjmedia_ice_start_ice(call->med_tp, call->inv->pool,
remote_sdp, 0);
if (status != PJ_SUCCESS)
return status;
}
/* Override ptime, if this option is specified. */
if (pjsua_var.media_cfg.ptime != 0) {
sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t)
(pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime);
if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0)
sess_info.stream_info[0].param->setting.frm_per_pkt = 1;
}
/* Disable VAD, if this option is specified. */
if (pjsua_var.media_cfg.no_vad) {
sess_info.stream_info[0].param->setting.vad = 0;
}
/* Optionally, application may modify other stream settings here
* (such as jitter buffer parameters, codec ptime, etc.)
*/
sess_info.stream_info[0].jb_init = pjsua_var.media_cfg.jb_init;
sess_info.stream_info[0].jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
sess_info.stream_info[0].jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
sess_info.stream_info[0].jb_max = pjsua_var.media_cfg.jb_max;
/* Create session based on session info. */
status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
&call->med_tp,
call, &call->session );
if (status != PJ_SUCCESS) {
return status;
}
/* If DTMF callback is installed by application, install our
* callback to the session.
*/
if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
pjmedia_session_set_dtmf_callback(call->session, 0,
&dtmf_callback,
(void*)(call->index));
}
/* Get the port interface of the first stream in the session.
* We need the port interface to add to the conference bridge.
*/
pjmedia_session_get_port(call->session, 0, &media_port);
/*
* Add the call to conference bridge.
*/
port_name.ptr = tmp;
port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
call->inv->dlg->remote.info->uri,
tmp, sizeof(tmp));
if (port_name.slen < 1) {
port_name = pj_str("call");
}
status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
media_port,
&port_name,
(unsigned*)&call->conf_slot);
if (status != PJ_SUCCESS) {
return status;
}
/* Call's media state is active */
call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
call->media_dir = sess_info.stream_info[0].dir;
}
/* Print info. */
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -