发起呼叫
发起呼叫请求的入口在inviteAddressWithParams(address,params); jni接口在linphonecore_jni.cc 中 line 5023;
按照流程走,最终调用linphone_core_start_invite(), 在linphonecore.c ——line3161
int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, const LinphoneAddress* destination /* = NULL if to be taken from the call log */){
ms_message("linphone_core_start_invite");
int err;
char *real_url,*barmsg;
char *from;
/*try to be best-effort in giving real local or routable contact address */
linphone_call_set_contact_op(call);
linphone_core_stop_dtmf_stream(lc);
linphone_call_make_local_media_description(call);
if (lc->ringstream==NULL) {
if (lc->sound_conf.play_sndcard && lc->sound_conf.capt_sndcard){
/*give a chance a set card prefered sampling frequency*/
if (call->localdesc->streams[0].max_rate>0) {
ms_snd_card_set_preferred_sample_rate(lc->sound_conf.play_sndcard, call->localdesc->streams[0].max_rate);
}
if (!lc->use_files){
ms_message("start invite call audio_stream_prepare_sound");
audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard);
}
}
}
real_url=linphone_address_as_string( destination ? destination : call->log->to);
from=linphone_address_as_string(call->log->from);
if (!lc->sip_conf.sdp_200_ack){
/*we are offering, set local media description before sending the call*/
sal_call_set_local_media_description(call->op,call->localdesc);
}
barmsg=ortp_strdup_printf("%s %s", _("Contacting"), real_url);
linphone_core_notify_display_status(lc,barmsg);
ms_free(barmsg);
err=sal_call(call->op,from,real_url);
if (err < 0){
if (call->state != LinphoneCallError &&
call->state != LinphoneCallReleased){
/*sal_call() may invoke call_failure() and call_released() SAL callbacks synchronously,
* in which case there is no need to perform a state change here.*/
linphone_core_notify_display_status(lc,_("Could not call"));
linphone_call_stop_media_streams(call);
linphone_call_set_state(call,LinphoneCallError,"Call failed");
}
goto end;
}
if (lc->sip_conf.sdp_200_ack){
/*we are NOT offering, set local media description after sending the call so that we are ready to
process the remote offer when it will arrive*/
sal_call_set_local_media_description(call->op,call->localdesc);
}
call->log->call_id=ms_strdup(sal_op_get_call_id(call->op)); /*must be known at that time*/
linphone_call_set_state(call,LinphoneCallOutgoingProgress,"Outgoing call in progress");
end:
ms_free(real_url);
ms_free(from);
return err;
}
linphone_call_make_local_media_description(call);
if (lc->ringstream==NULL) {
if (lc->sound_conf.play_sndcard && lc->sound_conf.capt_sndcard){
/*give a chance a set card prefered sampling frequency*/
if (call->localdesc->streams[0].max_rate>0) {
ms_snd_card_set_preferred_sample_rate(lc->sound_conf.play_sndcard, call->localdesc->streams[0].max_rate);
}
if (!lc->use_files){
ms_message("start invite call audio_stream_prepare_sound");
audio_stream_prepare_sound(call->audiostream,lc->sound_conf.play_sndcard,lc->sound_conf.capt_sndcard);
}
}
}
real_url=linphone_address_as_string( destination ? destination : call->log->to);
from=linphone_address_as_string(call->log->from);
if (!lc->sip_conf.sdp_200_ack){
/*we are offering, set local media description before sending the call*/
sal_call_set_local_media_description(call->op,call->localdesc);
}
barmsg=ortp_strdup_printf("%s %s", _("Contacting"), real_url);
linphone_core_notify_display_status(lc,barmsg);
ms_free(barmsg);
err=sal_call(call->op,from,real_url);
if (err < 0){
if (call->state != LinphoneCallError &&
call->state != LinphoneCallReleased){
/*sal_call() may invoke call_failure() and call_released() SAL callbacks synchronously,
* in which case there is no need to perform a state change here.*/
linphone_core_notify_display_status(lc,_("Could not call"));
linphone_call_stop_media_streams(call);
linphone_call_set_state(call,LinphoneCallError,"Call failed");
}
goto end;
}
if (lc->sip_conf.sdp_200_ack){
/*we are NOT offering, set local media description after sending the call so that we are ready to
process the remote offer when it will arrive*/
sal_call_set_local_media_description(call->op,call->localdesc);
}
call->log->call_id=ms_strdup(sal_op_get_call_id(call->op)); /*must be known at that time*/
linphone_call_set_state(call,LinphoneCallOutgoingProgress,"Outgoing call in progress");
end:
ms_free(real_url);
ms_free(from);
return err;
}
1、设置本地的媒体描述参数 linphone_call_make_local_media_description(call)
2、判断ringstream是否为空,即响铃的音频流是否构建好,如果没有则准备启动音频库
audio_stream_prepare_sound();
3、在对方还没接响应sip消息前,设置好sip的媒体参数sal_call_set_local_media_description();
4、发起呼叫sal_call();
5、最后根据响应结果,来更新本地媒体描述,sal_call_set_local_media_description,
6、更新状态为LinphoneCallOutgoingProgress;
本次不介绍呼叫过程中的sip消息的处理,因此只看下1、2、3步的过程;
第一步 linphone_call_make_local_media_description(), linphonecall.c ——line731
代码太长了,主要过程是:
构建一个SalMediaDescription* md对象;
然后设置md->stream[]中的audio、video的multicast_role、ttl属性
设置md的session_id、bandwidth等属性
设置md->stream[]中的audio、video的采样率、端口、协议等于音视频通信协议相关的设置参数
第二步 audio_stream_prepare_sound; audiostream.c ——line240
void audio_stream_prepare_sound(AudioStream *stream, MSSndCard *playcard, MSSndCard *captcard){
ms_message("audio_stream_prepare_sound playcard = %s, captcard = %s",playcard->desc->driver_type,captcard->desc->driver_type);
audio_stream_unprepare_sound(stream);
stream->dummy=ms_factory_create_filter(stream->ms.factory, MS_RTP_RECV_ID);
rtp_session_set_payload_type(stream->ms.sessions.rtp_session,0);
rtp_session_enable_rtcp(stream->ms.sessions.rtp_session, FALSE);
ms_filter_call_method(stream->dummy,MS_RTP_RECV_SET_SESSION,stream->ms.sessions.rtp_session);
if (captcard && playcard){
#ifdef __ios
int muted = 1;
stream->soundread=ms_snd_card_create_reader(captcard);
stream->soundwrite=ms_snd_card_create_writer(playcard);
ms_filter_link(stream->dummy,0,stream->soundwrite,0);
ms_filter_call_method(stream->soundwrite, MS_AUDIO_PLAYBACK_MUTE, &muted);
#else
stream->ms.voidsink=ms_factory_create_filter(stream->ms.factory, MS_VOID_SINK_ID);
ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
#endif
} else {
stream->ms.voidsink=ms_factory_create_filter(stream->ms.factory, MS_VOID_SINK_ID);
ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
}
if (stream->ms.sessions.ticker == NULL) media_stream_start_ticker(&stream->ms);
ms_ticker_attach(stream->ms.sessions.ticker,stream->dummy);
stream->ms.state=MSStreamPreparing;
}
ms_factory_create_filter(stream->ms.factory, MS_RTP_RECV_ID);
rtp_session_set_payload_type(stream->ms.sessions.rtp_session,0);
rtp_session_enable_rtcp(stream->ms.sessions.rtp_session, FALSE);
ms_filter_call_method(stream->dummy,MS_RTP_RECV_SET_SESSION,stream->ms.sessions.rtp_session);
if (captcard && playcard){
#ifdef __ios
int muted = 1;
stream->soundread=ms_snd_card_create_reader(captcard);
stream->soundwrite=ms_snd_card_create_writer(playcard);
ms_filter_link(stream->dummy,0,stream->soundwrite,0);
ms_filter_call_method(stream->soundwrite, MS_AUDIO_PLAYBACK_MUTE, &muted);
#else
stream->ms.voidsink=ms_factory_create_filter(stream->ms.factory, MS_VOID_SINK_ID);
ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
#endif
} else {
stream->ms.voidsink=ms_factory_create_filter(stream->ms.factory, MS_VOID_SINK_ID);
ms_filter_link(stream->dummy,0,stream->ms.voidsink,0);
}
if (stream->ms.sessions.ticker == NULL) media_stream_start_ticker(&stream->ms);
ms_ticker_attach(stream->ms.sessions.ticker,stream->dummy);
stream->ms.state=MSStreamPreparing;
}
先释放掉call中原有的audiostream: audio_stream_unprepare_sound(stream);
然后获取ms_factory中MS_RTP_RECV_ID的MSFilter对象:ms_factory_create_filter(),设置到stream->dummy;
后面继续获取MS_VOID_SINK_ID的MSFilter对象,设置到stream->ms.voidsink上;
然后判断stream->ms.sessions.ticker == null的时候,调用media_stream_start_ticker()去初始化并启动一个ms_ticker;
最后调用ms_ticker_attach()来将ms_ticker与stream->dummy关联上;
音频准备工作重点就是启用MSTicker并关联到本次通话过程中,
首先获取MSFilter* :ms_factory_create_filter()。 msfactory.c ——line362
MSFilter *ms_factory_create_filter(MSFactory* factory, MSFilterId id){
MSFilterDesc *desc;
if (id==MS_FILTER_PLUGIN_ID){
ms_warning("cannot create plugin filters with ms_filter_new_from_id()");
return NULL;
}
desc=ms_factory_lookup_filter_by_id(factory,id);
if (desc) return ms_factory_create_filter_from_desc(factory,desc);
ms_error("No such filter with id %i",id);
return NULL;
}
ms_factory_lookup_filter_by_id(factory,id);
if (desc) return ms_factory_create_filter_from_desc(factory,desc);
ms_error("No such filter with id %i",id);
return NULL;
}
使用的id是MS_RTP_RECV_ID,通过ms_factory_lookup_filter_by_id来找到过滤器的描述;——line385
MSFilterDesc* ms_factory_lookup_filter_by_id( MSFactory* factory, MSFilterId id){
bctbx_list_t *elem;
for (elem=factory->desc_list;elem!=NULL;elem=bctbx_list_next(elem)){
MSFilterDesc *desc=(MSFilterDesc*)elem->data;
if (desc->id==id){
return desc;
}
}
return NULL;
}
实际就是遍历factory的desc_list列表,找到跟id匹配的第一个desc描述体
回到上面的函数中,继续通过dessc找到真正的filter
ms_factory_create_filter_from_desc(); ——line337
MSFilter *ms_factory_create_filter_from_desc(MSFactory* factory, MSFilterDesc *desc){
MSFilter *obj;
obj=(MSFilter *)ms_new0(MSFilter,1);
ms_mutex_init(&obj->lock,NULL);
obj->desc=desc;
if (desc->ninputs>0) obj->inputs=(MSQueue**)ms_new0(MSQueue*,desc->ninputs);
if (desc->noutputs>0) obj->outputs=(MSQueue**)ms_new0(MSQueue*,desc->noutputs);
if (factory->statistics_enabled){
obj->stats=find_or_create_stats(factory,desc);
}
obj->factory=factory;
if (obj->desc->init!=NULL)
obj->desc->init(obj);
return obj;
}
init(obj);
return obj;
}
初始化一个MSFilter,然后将传入的desc设置到obj上:obj->desc = desc;
然后初始化obj->inputs和obj->outputs;
关联MSFactory :obj->factory = factory;
最后调用obj->desc->init(obj)进行MSFilter的初始化;
再回到audio_stream_prepare_sound中,看看MSTicker的初始化流程
media_stream_start_ticker(),位于mediastream.c ——line144
void media_stream_start_ticker(MediaStream *stream) {
ms_message("media_stream_start_ticker");
MSTickerParams params = {0};
char name[32] = {0};
if (stream->sessions.ticker) return;
snprintf(name, sizeof(name) - 1, "%s MSTicker", media_stream_type_str(stream));
name[0] = toupper(name[0]);
params.name = name;
params.prio = __ms_get_default_prio((stream->type == MSVideo) ? TRUE : FALSE);
stream->sessions.ticker = ms_ticker_new_with_params(¶ms);
}
ms_ticker_new_with_params(¶ms);
}
构建一个MSTickerParams参数,然后通过它构建MS_Ticker,
ms_ticker_new_with_params(), 位于msticker.c ——line87
MSTicker *ms_ticker_new_with_params(const MSTickerParams *params){
MSTicker *obj=(MSTicker *)ms_new0(MSTicker,1);
ms_ticker_init(obj,params);
return obj;
}
ms_ticker_init(obj,params);
return obj;
}
static void ms_ticker_init(MSTicker *ticker, const MSTickerParams *params)
{
ms_message("ms_ticker_init");
ms_mutex_init(&ticker->lock,NULL);
ticker->execution_list=NULL;
ticker->task_list=NULL;
ticker->ticks=1;
ticker->time=0;
ticker->interval=TICKER_INTERVAL;
ticker->run=FALSE;
ticker->exec_id=0;
ticker->get_cur_time_ptr=&get_cur_time_ms;
ticker->get_cur_time_data=NULL;
ticker->name=ms_strdup(params->name);
ticker->av_load=0;
ticker->prio=params->prio;
ticker->wait_next_tick=wait_next_tick;
ticker->wait_next_tick_data=ticker;
ticker->late_event.lateMs = 0;
ticker->late_event.time = 0;
ticker->late_event.current_late_ms = 0;
ms_ticker_start(ticker);
}
static void ms_ticker_start(MSTicker *s){
s->run=TRUE;
ms_thread_create(&s->thread,NULL,ms_ticker_run,s);
}
ms_ticker_start(ticker);
}
static void ms_ticker_start(MSTicker *s){
s->run=TRUE;
ms_thread_create(&s->thread,NULL,ms_ticker_run,s);
}
初始化分为三个步骤
1、new一个MSTicker*对象;
2、配置ticker的属性,默认的间隔是10秒;
3、添加到线程中启动ms_ticker;
接着看ms_ticker_attach(),位于msticker.c ——line135
int ms_ticker_attach(MSTicker *ticker, MSFilter *f){
return ms_ticker_attach_multiple(ticker,f,NULL);
}
int ms_ticker_attach_multiple(MSTicker *ticker,MSFilter *f,...)
{
ms_message("ms_ticker_attach_multiple");
bctbx_list_t *sources=NULL;
bctbx_list_t *filters=NULL;
bctbx_list_t *it;
bctbx_list_t *total_sources=NULL;
va_list l;
va_start(l,f);
do{
if (f->ticker==NULL) {
ms_message("f->ticker == null");
filters=ms_filter_find_neighbours(f);
sources=get_sources(filters);
if (sources==NULL){
ms_fatal("No sources found around filter %s",f->desc->name);
bctbx_list_free(filters);
break;
}
/*run preprocess on each filter: */
for(it=filters;it!=NULL;it=it->next)
ms_filter_preprocess((MSFilter*)it->data,ticker);
bctbx_list_free(filters);
total_sources=bctbx_list_concat(total_sources,sources);
}else ms_message("Filter %s is already being scheduled; nothing to do.",f->desc->name);
}while ((f=va_arg(l,MSFilter*))!=NULL);
va_end(l);
if (total_sources){
ms_mutex_lock(&ticker->lock);
ticker->execution_list=bctbx_list_concat(ticker->execution_list,total_sources);
ms_mutex_unlock(&ticker->lock);
}
return 0;
}
for(it=filters;it!=NULL;it=it->next)
ms_filter_preprocess((MSFilter*)it->data,ticker);
bctbx_list_free(filters);
total_sources=bctbx_list_concat(total_sources,sources);
}else ms_message("Filter %s is already being scheduled; nothing to do.",f->desc->name);
}while ((f=va_arg(l,MSFilter*))!=NULL);
va_end(l);
if (total_sources){
ms_mutex_lock(&ticker->lock);
ticker->execution_list=bctbx_list_concat(ticker->execution_list,total_sources);
ms_mutex_unlock(&ticker->lock);
}
return 0;
}
刚启动的时候,f->ticker=null,
ms_filter_find_neighbours(f),获取整个f的filters列表;
然后遍历整个filters,调用每个ms_filter_preprocess();
执行完preprocess以后,会将当前的total_sources添加到ticker->execution列表中、
系统每个生成的MSTicker下的execution_list都会保存当前所有构建的MSTickers对象,这个会在ms_ticker_run中用到;
ms_filter_preprocess() 位于msfilter.c ——line234
void ms_filter_preprocess(MSFilter *f, struct _MSTicker *t){
ms_message("ms_filter_preprocess %s" ,f->desc->name);
f->last_tick=0;
f->ticker=t;
if (f->desc->preprocess!=NULL)
f->desc->preprocess(f);
}
preprocess(f);
}
调用每个MSFilter中定义的MSFilterDesc中的preprocess()函数;
这里主要有两种类型的MSFilter,一个是MSRtpRecv,一个是MSVoidSink;
MSRtpRecv定义在msrtp.c中,应该是作为rtp数据接收使用的;
MSVoidSink定义在void.c中,这个没有preprocess函数接口;
看一下MSRtpRecv中的preprocess(), 在msrtp.c ——line648
static void receiver_preprocess(MSFilter * f){
ms_message("receiver_preprocess msrtp");
ReceiverData *d = (ReceiverData *) f->data;
d->starting=TRUE;
}
修改了f->data->starting的状态值,后面在执行process的时候会校验这个状态;
f->data是真实的媒体库的执行类;
至此,第二步的ringstream的初始化完成了;
第三步,sal_call_set_local_media_description();
位于sal_op_call.c ——line723
int sal_call_set_local_media_description(SalOp *op, SalMediaDescription *desc){
if (desc)
sal_media_description_ref(desc);
if (op->base.local_media)
sal_media_description_unref(op->base.local_media);
op->base.local_media=desc;
if (op->base.remote_media){
/*case of an incoming call where we modify the local capabilities between the time
* the call is ringing and it is accepted (for example if you want to accept without video*/
/*reset the sdp answer so that it is computed again*/
if (op->sdp_answer){
belle_sip_object_unref(op->sdp_answer);
op->sdp_answer=NULL;
}
}
return 0;
}