Linphone 被叫方如何解析来电SIP消息中的自定义头消息
2017-12-01 15:38
1086 查看
linphone源码中其实暂无提供自定义头消息的解析功能,所以这里需要添加一部分代码,至于在什么地方添加自定义头消息,就需要了解linphone处理来电的sip请求的过程。
个人梳理了下大概分为以下几个过程:
接到请求后,从socket中解析出完整的SIP字符串,经过多次转换后生成event类型的结构体。
归类到INVITE类型的event后,通过provider来处理,过程中会生成SalOp对象。
生成JAVA层可以使用的LinphoneCall对象后,通过callState()接口回调给java层;
第一部分
首先看接收到来电INVITE类型的SIP请求时,触发了什么?
通过底层的log,来电后大致会触发到belle_sip_channel_process_data,位于channel.c line 705;
入参的obj是已经经过初步解析的来电请求,revents是来电的事件类别;这里是一个READ的事件;
进入belle_sip_channel_process_read_data(obj);
过程分析:
1、因为是刚接收到来电,通过channel_begin_recv_background_task(obj),这个主要是开启android的wake lock,
2、进入belle_sip_channel_recv(xxxx),这个过程主要是根据obj中存的的channel_recv回调,我们这边使用的是udp,所以这里最终是调用了udp_channel_recv(obj,buf,buflen),在udp_channel.c中,将obj此时指定的socket中的sip消息读取并存到obj->input_stream.write_ptr中,返回值是读取的消息长度;
3、判断是否读取到消息,也就是num>0?如果有的话,调用belle_sip_channel_process_stream(obj,FALSE);
4、最后调用channel_end_recv_background_task(obj)来关闭android的wake lock;
接着看第三步的belle_sip_channel_process_stream(obj,FALSE);
过程分析:
1、调用belle_sip_channel_parse_stream(obj,eos)来进行第一次的解析;
2、调用notify_incoming_messages(obj)来通知有来电接入;
先看第一步的belle_sip_channel_parse_stream(obj,eos); 还在channel.c中;
过程分析:
第一个if中,大致是在初始化obj->input_stream.read_ptr,并将obj->input_stream.state改为MESSAGE_AQUISITION;
第二个if中,通过belle_sip_message_parse_raw()来解析sip消息,并存储到obj->input_stream.msg中;
最后调用acquire_body(),内部调用acquire_body_simple(obj,eos),内部调用belle_sip_channel_message_ready(obj)来将obj->input_stream.msg中的数据设置到obj->incoming_messages中,并释放掉obj->input_stream中的资源;
关于第二个if中调用的belle_sip_message_parse_raw();
这里具体是怎么执行的,不是很明确,大致会通过parse->message_raw()来读取sip消息,这个函数接口的最终实现是在belle_sip_messageParser.c中,有一个
message_header(pbelle_sip_messageParser ctx,belle_sip_message_t* message)
函数内有下面一段:
所以这里会通过lheader指针,来遍历obj->input_streams.read_ptr指向的sip消息的每一行,然后将每一行的内容通过belle_sip_message_add_header()来添加到belle_sip_message_t*的结构体中,并最终返回给obj->input_stream.msg中;
到这里belle_sip_channel_parse_stream(obj,eos)过程结束;
sip消息被保存在obj->incoming_messages中了,是个belle_sip_list_t的结构体;
接着看第二步的notify_incoming_messages(obj);位于channel.c line513;
过程分析:
1、首先找出obj->full_listeners下指定的所有需要处理sip消息的回调,
2、然后遍历listeners和obj->incoming_messages,执行listener下指定的on_message(listener,obj,msg),
3、最后释放到obj->incoming_messages;
对于来电呼叫,这里的msg实际上就是obj->incoming_messages里面的内容,这里的on_message对应的是channel_on_message() 位于provider.c中;
只是作了一个消息分发,同时将obj转成BELLE_SIP_PROVIDER_T结构体;
因为是sip呼入,这里是一个request请求,进入belle_sip_provider_dispatch_request(prov,msg);
过程简单分析:
1、第一个”CANCEL”判断的地方,创建了一个belle_sip_server_transaction_t对象,并执行了belle_sip_server_transaction_send_response()。是针对接收到来电后回执一个sip消息,具体的操作暂不细究了;
2、最后构建了一个belle_sip_request_event_t*对象,通过BELLE_SIP_PROVIDER_INVOKE_LISTENERS执行对应一个process_request_event()接口;
注意原来保存sip消息的msg已经被转换成belle_sip_message_t结构体,并保存在ev.request下了
到这里,个人理解,针对sip消息解析的第一个过程基本结束,完成了从socket中读取SIP消息字符串,生成了一个表示request事件的结构体,来保存完整的SIP消息。后面通过找到处理这一类event的provider来处理这个请求。
第二部分
BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov->listeners,process_request_event,&ev)是个宏,prov是之前的ojb
这个宏最终是调用method方法,这里的method的就是process_request_event()。入参的arg就是&ev,至于_obj是哪里来的,在上一句 __BELLE_SIP_INVOKE_LISTENER_BEGIN(XXXXX);
这里的_obj是通过遍历prov->listeners,找到每个listener下的data对象。
至于这个data对象在哪里设置的,暂时没找到,通过log找到这里的method调用的是sal_impl.c下的:
process_request_event(void*, belle_sip_request_event_t * event)
部分过程省略:
1、构建一个空SalOp对象,以及获取event中保存的完整sip消息结构体 request;
2、如果是invite类型的消息,初始化一个新的SalOp对象,然后通过sal_op_call_fill_cbs(op)补充op下的回调callbacks,这里的回调接口实现全部都在sal_op_call.c下;
3、填充op中的属性值,包括from、to等一系列基础信息;
4、调用sal_op_assign_recv_headers()将req中的消息头全部填入到op中,后面就直接使用op中的数据来反馈来电给java层了
5、调用op->callbacks中的process_request_event(op,event);
重点看一下过程4中的:
入参就是之前的request里面的message,将其保存到op->base.recv_custom_headers下;
接着就调用了op->callbacks下的process_requset_event();
部分过程省略了,关键两个步骤,
1、process_sdp_for_invite(op,req),判断这个request是不是invite类型的sdp请求;
2、如果是调用op下的base属性中的root->callbacks.call_received(op);
到这里,个人理解,第二个环节的处理基本完成,通过provider将接收到的event再次解析并重新构建了一个SalOp对象,而来电请求完整的SIP消息体,被保存在op->base.recv_custom_headers链表中。
第三部分
回调给JAVA层;
上一步最后执行的call_received(op)位于callbacks.c line263
过程分析:
1、根据传进来的SalOp,并解析了其中的通话信息,通过linphone_call_new_incoming()构建LinphoneCall对象;
2、通过linphone_core_add_call()将该call添加到linphonecore中;
3、最后通过linphone_core_notify_incoming_call(lc,call),将来电信息传递到上层java中;
先看第一步:linphone_call_new_incoming
a、通过belle_sip_object_new()构建一个空的LinphoneCall对象,然后关联SalOp和LinhoneCore;
b、然后通过linphone_call_params_new()构建一个LinphoneCallParams对象,并关联到call->params下,再通过linphone_call_init_common()和linphone_core_init_default_params()来初始化LinphoneCallParams的配置;
c、后面还有一些配置media属性的操作,暂时不看了。
参考发起呼叫时添加自定义头消息的过程,自定义的消息头是存放在LinphoneCall下的LinphoneCallParams的,并且通话过程中的状态回调每次都会回调LinphoneCall对象。
所以如果要解析来电中的自定义头消息,可以将解析出来的消息头存放在这里构建出来的LinphoneCallParams中;
用的linphone源码中自带的一些函数
sal_op_get_recv_custom_header(op),是获取op.base->recv_custom_header对象,其实就是存的sip完整消息;
sal_custom_header_find(xx,”x-extraData”);查找sip消息中字段名为:x-extraData的字符串值;
如果这个字段存在,通过linphone_call_params_add_custom_header()将这个key和value保存到call->params下的custom_headers;
再看call_received中的第二步:linphone_core_add_call(lc,call)
其实就是关联lc和call,将当前的call保存到lc->calls下的链表中;
最后第三步:linphone_core_notify_incoming_call()
简单说这个过程的流程:
内部调用linphone_call_set_stata(LinphoneCall,LinphoneCallState,char
message),将当前的通话状态设置为LinphoneCallIncomingReceived
内部调用linphone_core_notify_call_state_changed(lc,call,cstate,message)来通知通话状态已修改,
内部调用NOTIFY_IF_EXIST(call_state_changed,lc,call,cstate,message);
NOTIFY_IF_EXIST是个宏,第一个参数是方法名,后面的是他的参数,这里调用的就是call_state_changed()接口,
NOTIFY_IF_EXIST宏内可以调用的方法,都定义在linphonecore_jni.cc中的LinphoneCoreTable下:
在callStateChange中通过反射的方式,加载了LinphoneCoreListenerBase下的call_State()回调。
最后我们就可以在java层获取来电sip消息中的自定义字段值,
使用LinphoneCallParams自带的getCustomHeader()就可以获取指定key的头消息值。
注意:如果自定义消息想直接传json或者xml等其他格式的字符串,会造成sip格式解析异常,所以这里做了base64编码转换,另外base64转码后会在字符串尾部多一个’\n’,也会影响sip解析,需要去掉
全流程总结:
来电的sip请求触发后,消息数据最先保存在一个belle_sip_channel_t下的input_stream中,经过初步解析成字符串后,保留到该结构体下的incoming_messages属性中,多次转换后生成一个表示event事件的belle_sip_request_event_t结构体,
通过预先设置的provider来处理这个event结构体,过程中构建了一个SalOp对象,将这部分消息头字符串保存到了这个op下的SalOpBase结构体中的recv_custom_headers属性下,通过provider分析后确认这是一个invite类型的call,后调用预先设置的call_received接口去处理。
在call_received()中构建一个java层可用的LinphoneCallParams和LinphoneCall对象,同时从op中解析需要传递给java层的数据,填充到LinphoneCallParams对象中,这个param也被关联到call下,
最终通过callState()回调接口,将来电信息回调给java层
linphone源码中是不包含自定义头的数据解析和填充的,我们只需要参考发起呼叫时添加的自定义头消息的存储方式,将来电的自定义头消息存储到相应的位置即可。也就是在linphone_call_new_incoming()添加需要解析的消息头,补充到params->custom_headers,结束。
个人梳理了下大概分为以下几个过程:
接到请求后,从socket中解析出完整的SIP字符串,经过多次转换后生成event类型的结构体。
归类到INVITE类型的event后,通过provider来处理,过程中会生成SalOp对象。
生成JAVA层可以使用的LinphoneCall对象后,通过callState()接口回调给java层;
第一部分
首先看接收到来电INVITE类型的SIP请求时,触发了什么?
通过底层的log,来电后大致会触发到belle_sip_channel_process_data,位于channel.c line 705;
int belle_sip_channel_process_data(belle_sip_channel_t *obj,unsigned int revents){ belle_sip_message("belle_sip_channel_process_data"); int ret=BELLE_SIP_CONTINUE; if (revents & BELLE_SIP_EVENT_READ) { int rret=belle_sip_channel_process_read_data(obj); if (rret==BELLE_SIP_STOP) ret=BELLE_SIP_STOP; } if (revents & BELLE_SIP_EVENT_WRITE){ /*if we are here, this is because we had an EWOULDBLOCK while sending a message*/ /*continue to send pending messages but before check the channel is still alive because it may have been closed by belle_sip_channel_process_read_data() above.*/ if (obj->state == BELLE_SIP_CHANNEL_READY){ channel_process_queue(obj); } } return ret; }
入参的obj是已经经过初步解析的来电请求,revents是来电的事件类别;这里是一个READ的事件;
进入belle_sip_channel_process_read_data(obj);
static int belle_sip_channel_process_read_data(belle_sip_channel_t *obj){ belle_sip_message("belle_sip_channel_process_read_data"); int num; int ret=BELLE_SIP_CONTINUE; /*prevent system to suspend the process until we have finish reading everything from the socket and notified the upper layer*/ if (obj->input_stream.state == WAITING_MESSAGE_START) { channel_begin_recv_background_task(obj); } if (obj->simulated_recv_return>0) { num=belle_sip_channel_recv(obj,obj->input_stream.write_ptr,belle_sip_channel_input_stream_get_buff_length(&obj->input_stream)-1); } else { belle_sip_message("channel [%p]: simulating recv() returning %i",obj,obj->simulated_recv_return); num=obj->simulated_recv_return; } if (num>0){ char *begin=obj->input_stream.write_ptr; obj->input_stream.write_ptr+=num; /*first null terminate the read buff*/ *obj->input_stream.write_ptr='\0'; .... belle_sip_channel_process_stream(obj,FALSE); if (obj->input_stream.state == WAITING_MESSAGE_START){ channel_end_recv_background_task(obj); }/*if still in message acquisition state, keep the backgroud task*/ } ...... return ret; }
过程分析:
1、因为是刚接收到来电,通过channel_begin_recv_background_task(obj),这个主要是开启android的wake lock,
2、进入belle_sip_channel_recv(xxxx),这个过程主要是根据obj中存的的channel_recv回调,我们这边使用的是udp,所以这里最终是调用了udp_channel_recv(obj,buf,buflen),在udp_channel.c中,将obj此时指定的socket中的sip消息读取并存到obj->input_stream.write_ptr中,返回值是读取的消息长度;
3、判断是否读取到消息,也就是num>0?如果有的话,调用belle_sip_channel_process_stream(obj,FALSE);
4、最后调用channel_end_recv_background_task(obj)来关闭android的wake lock;
接着看第三步的belle_sip_channel_process_stream(obj,FALSE);
static void belle_sip_channel_process_stream(belle_sip_channel_t *obj, int eos){ belle_sip_channel_parse_stream(obj,eos); if (obj->incoming_messages) { if (obj->simulated_recv_return == 1500) { belle_sip_list_t *elem; for(elem=obj->incoming_messages;elem!=NULL;elem=elem->next){ belle_sip_message_t *msg=(belle_sip_message_t*)elem->data; char* dump = belle_sip_message_to_string(msg); belle_sip_message("Silently discarding incoming message [%.50s...] on channel [%p]",dump, obj); belle_sip_free(dump); } belle_sip_list_free_with_data(obj->incoming_messages,belle_sip_object_unref); obj->incoming_messages=NULL; } else { notify_incoming_messages(obj); } } }
过程分析:
1、调用belle_sip_channel_parse_stream(obj,eos)来进行第一次的解析;
2、调用notify_incoming_messages(obj)来通知有来电接入;
先看第一步的belle_sip_channel_parse_stream(obj,eos); 还在channel.c中;
void belle_sip_channel_parse_stream(belle_sip_channel_t *obj, int end_of_stream){ belle_sip_message("belle_sip_channel_parse_stream"); int offset; size_t read_size=0; int num; while ((num=(int)(obj->input_stream.write_ptr-obj->input_stream.read_ptr))>0){ // belle_sip_message("num = %i",num); if (obj->input_stream.state == WAITING_MESSAGE_START) { int i; /*first, make sure there is \r\n in the buffer, otherwise, micro parser cannot conclude, because we need a complete request or response line somewhere*/ for (i=0;i<num-1;i++) { if ((obj->input_stream.read_ptr[i]=='\r' && obj->input_stream.read_ptr[i+1]=='\n') || belle_sip_channel_input_stream_get_buff_length(&obj->input_stream) <= 1 /*1 because null terminated*/ /*if buffer full try to parse in any case*/) { /*good, now we can start searching for request/response*/ if ((offset=get_message_start_pos(obj->input_stream.read_ptr,num)) >=0 ) { // belle_sip_message("log 1"); /*message found !*/ if (offset>0) { belle_sip_warning("trashing [%i] bytes in front of sip message on channel [%p]",offset,obj); obj->input_stream.read_ptr+=offset; } obj->input_stream.state=MESSAGE_AQUISITION; } else { ...... } break; } } ...... if (obj->input_stream.state==MESSAGE_AQUISITION) { /*search for \r\n\r\n*/ char* end_of_message=NULL; if ((end_of_message=strstr(obj->input_stream.read_ptr,"\r\n\r\n"))){ ...... obj->input_stream.msg=belle_sip_message_parse_raw(obj->input_stream.read_ptr ,bytes_to_parse ,&read_size); *end_of_message=tmp; obj->input_stream.read_ptr+=read_size; .... }else break; /*The message isn't finished to be receive, we need more data*/ } if (obj->input_stream.state==BODY_AQUISITION) { if (acquire_body(obj,end_of_stream)==BELLE_SIP_STOP) break; } } }
过程分析:
第一个if中,大致是在初始化obj->input_stream.read_ptr,并将obj->input_stream.state改为MESSAGE_AQUISITION;
第二个if中,通过belle_sip_message_parse_raw()来解析sip消息,并存储到obj->input_stream.msg中;
最后调用acquire_body(),内部调用acquire_body_simple(obj,eos),内部调用belle_sip_channel_message_ready(obj)来将obj->input_stream.msg中的数据设置到obj->incoming_messages中,并释放掉obj->input_stream中的资源;
关于第二个if中调用的belle_sip_message_parse_raw();
belle_sip_message_t* belle_sip_message_parse_raw (const char* buff, size_t buff_length,size_t* message_length ) { \ pANTLR3_INPUT_STREAM input; pbelle_sip_messageLexer lex; pANTLR3_COMMON_TOKEN_STREAM tokens; pbelle_sip_messageParser parser; belle_sip_message_t* l_parsed_object; input = ANTLR_STREAM_NEW("message",buff,buff_length); lex = belle_sip_messageLexerNew (input); tokens = antlr3CommonTokenStreamSourceNew (1025, lex->pLexer->rec->state->tokSource); parser = belle_sip_messageParserNew (tokens); belle_sip_message("belle_sip_message_parse_raw buff "); l_parsed_object = parser->message_raw(parser,message_length); belle_sip_message("belle_sip_message_parse_raw message_raw done "); /* if (*message_length < buff_length) {*/ /*there is a body*/ /* l_parsed_object->body_length=buff_length-*message_length; l_parsed_object->body = belle_sip_malloc(l_parsed_object->body_length+1); memcpy(l_parsed_object->body,buff+*message_length,l_parsed_object->body_length); l_parsed_object->body[l_parsed_object->body_length]='\0'; }*/ parser ->free(parser); tokens ->free(tokens); lex ->free(lex); input ->close(input); return l_parsed_object; }
这里具体是怎么执行的,不是很明确,大致会通过parse->message_raw()来读取sip消息,这个函数接口的最终实现是在belle_sip_messageParser.c中,有一个
message_header(pbelle_sip_messageParser ctx,belle_sip_message_t* message)
函数内有下面一段:
belle_sip_header_t* lheader = BELLE_SIP_HEADER(header_extension_base10); do { if (lheader == NULL) break; /*sanity check*/ belle_sip_message("message_header arm 167619"); belle_sip_message_add_header(message,lheader); } while((lheader=belle_sip_header_get_next(lheader)) != NULL);
所以这里会通过lheader指针,来遍历obj->input_streams.read_ptr指向的sip消息的每一行,然后将每一行的内容通过belle_sip_message_add_header()来添加到belle_sip_message_t*的结构体中,并最终返回给obj->input_stream.msg中;
到这里belle_sip_channel_parse_stream(obj,eos)过程结束;
sip消息被保存在obj->incoming_messages中了,是个belle_sip_list_t的结构体;
接着看第二步的notify_incoming_messages(obj);位于channel.c line513;
static void notify_incoming_messages(belle_sip_channel_t *obj){ belle_sip_list_t *elem,*l_it; belle_sip_list_t *listeners=belle_sip_list_copy_with_data(obj->full_listeners,(void *(*)(void*))belle_sip_object_ref); for(l_it=listeners;l_it!=NULL;l_it=l_it->next){ belle_sip_channel_listener_t *listener=(belle_sip_channel_listener_t*)l_it->data; for(elem=obj->incoming_messages;elem!=NULL;elem=elem->next){ belle_sip_message_t *msg=(belle_sip_message_t*)elem->data; BELLE_SIP_INTERFACE_METHODS_TYPE(belle_sip_channel_listener_t) *methods; methods=BELLE_SIP_INTERFACE_GET_METHODS(listener,belle_sip_channel_listener_t); if (methods->on_message) methods->on_message(listener,obj,msg); } } belle_sip_list_free_with_data(listeners,belle_sip_object_unref); belle_sip_list_free_with_data(obj->incoming_messages,belle_sip_object_unref); obj->incoming_messages=NULL; }
过程分析:
1、首先找出obj->full_listeners下指定的所有需要处理sip消息的回调,
2、然后遍历listeners和obj->incoming_messages,执行listener下指定的on_message(listener,obj,msg),
3、最后释放到obj->incoming_messages;
对于来电呼叫,这里的msg实际上就是obj->incoming_messages里面的内容,这里的on_message对应的是channel_on_message() 位于provider.c中;
static void channel_on_message(belle_sip_channel_listener_t *obj, belle_sip_channel_t *chan, belle_sip_message_t *msg){ belle_sip_message("channel_on_message"); belle_sip_object_ref(msg); belle_sip_provider_dispatch_message(BELLE_SIP_PROVIDER(obj),msg); }
只是作了一个消息分发,同时将obj转成BELLE_SIP_PROVIDER_T结构体;
void belle_sip_provider_dispatch_message(belle_sip_provider_t *prov, belle_sip_message_t *msg){ belle_sip_message("belle_sip_provider_dispatch_message"); if (TRUE #ifndef BELLE_SIP_DONT_CHECK_HEADERS_IN_MESSAGE && belle_sip_message_check_headers(msg) #endif ){ if (belle_sip_message_is_request(msg)){ belle_sip_provider_dispatch_request(prov,(belle_sip_request_t*)msg); }else{ belle_sip_provider_dispatch_response(prov,(belle_sip_response_t*)msg); } }else{ /* incorrect message received, answer bad request if it was a request.*/ if (belle_sip_message_is_request(msg)){ belle_sip_response_t *resp=belle_sip_response_create_from_request(BELLE_SIP_REQUEST(msg),400); if (resp){ belle_sip_provider_send_response(prov,resp); } }/*otherwise what can we do ?*/ } belle_sip_object_unref(msg); }
因为是sip呼入,这里是一个request请求,进入belle_sip_provider_dispatch_request(prov,msg);
static void belle_sip_provider_dispatch_request(belle_sip_provider_t* prov, belle_sip_request_t *req){ belle_sip_message("belle_sip_provider_dispatch_request"); belle_sip_server_transaction_t *t; belle_sip_request_event_t ev; t=belle_sip_provider_find_matching_server_transaction(prov,req); if (t){ belle_sip_object_ref(t); belle_sip_server_transaction_on_request(t,req); belle_sip_object_unref(t); }else{ const char *method=belle_sip_request_get_method(req); ev.dialog=NULL; /* Should we limit to ACK ? */ /*Search for a dialog if exist */ if (strcmp("CANCEL",method) == 0) { /* Call leg does not exist */ belle_sip_server_transaction_t *tr = belle_sip_provider_create_server_transaction(prov, req); belle_sip_server_transaction_send_response(tr, belle_sip_response_create_from_request(req, 481)); return; } ...... if (prov->unconditional_answer_enabled && strcmp("ACK",method)!=0) { /*always answer predefined value (I.E 480 by default)*/ belle_sip_server_transaction_t *tr=belle_sip_provider_create_server_transaction(prov,req); belle_sip_server_transaction_send_response(tr,belle_sip_response_create_from_request(req,prov->unconditional_answer)); return; } else { ev.source=(belle_sip_object_t*)prov; ev.server_transaction=NULL; ev.request=req; BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov->listeners,process_request_event,&ev); } } }
过程简单分析:
1、第一个”CANCEL”判断的地方,创建了一个belle_sip_server_transaction_t对象,并执行了belle_sip_server_transaction_send_response()。是针对接收到来电后回执一个sip消息,具体的操作暂不细究了;
2、最后构建了一个belle_sip_request_event_t*对象,通过BELLE_SIP_PROVIDER_INVOKE_LISTENERS执行对应一个process_request_event()接口;
注意原来保存sip消息的msg已经被转换成belle_sip_message_t结构体,并保存在ev.request下了
到这里,个人理解,针对sip消息解析的第一个过程基本结束,完成了从socket中读取SIP消息字符串,生成了一个表示request事件的结构体,来保存完整的SIP消息。后面通过找到处理这一类event的provider来处理这个请求。
第二部分
BELLE_SIP_PROVIDER_INVOKE_LISTENERS(prov->listeners,process_request_event,&ev)是个宏,prov是之前的ojb
#define BELLE_SIP_PROVIDER_INVOKE_LISTENERS(listeners,callback,event) \ BELLE_SIP_INVOKE_LISTENERS_ARG((listeners),belle_sip_listener_t,callback,(event)) #define BELLE_SIP_INVOKE_LISTENERS_ARG(list,interface_name,method,arg) \ __BELLE_SIP_INVOKE_LISTENER_BEGIN(list,interface_name,method)\ method(__obj,arg);\ __BELLE_SIP_INVOKE_LISTENER_END;
这个宏最终是调用method方法,这里的method的就是process_request_event()。入参的arg就是&ev,至于_obj是哪里来的,在上一句 __BELLE_SIP_INVOKE_LISTENER_BEGIN(XXXXX);
#define __BELLE_SIP_INVOKE_LISTENER_BEGIN(list,interface_name,method) \ if (list!=NULL) {\ belle_sip_list_t *__copy=belle_sip_list_copy_with_data((list), (void* (*)(void*))belle_sip_object_ref);\ const belle_sip_list_t *__elem=__copy;\ do{\ void *__method;\ interface_name *__obj=(interface_name*)__elem->data;\ __method=BELLE_SIP_INTERFACE_GET_METHODS(__obj,interface_name)->method;\ if (__method) BELLE_SIP_INTERFACE_GET_METHODS(__obj,interface_name)-> #define __BELLE_SIP_INVOKE_LISTENER_END \ __elem=__elem->next;\ }while(__elem!=NULL);\ belle_sip_list_free_with_data(__copy,belle_sip_object_unref);\ }
这里的_obj是通过遍历prov->listeners,找到每个listener下的data对象。
至于这个data对象在哪里设置的,暂时没找到,通过log找到这里的method调用的是sal_impl.c下的:
process_request_event(void*, belle_sip_request_event_t * event)
process_request_event(void*, belle_sip_request_event_t * event); static void process_request_event(void *ud, const belle_sip_request_event_t *event) { Sal *sal=(Sal*)ud; SalOp* op=NULL; belle_sip_request_t* req = belle_sip_request_event_get_request(event); ...... if (strcmp("INVITE",method)==0) { op=sal_op_new(sal); op->dir=SalOpDirIncoming; sal_op_call_fill_cbs(op); } sal_op_assign_recv_headers(op,(belle_sip_message_t*)req); if (op->callbacks && op->callbacks->process_request_event) { op->callbacks->process_request_event(op,event); } else { ms_error("sal process_request_event not implemented yet"); } }
部分过程省略:
1、构建一个空SalOp对象,以及获取event中保存的完整sip消息结构体 request;
2、如果是invite类型的消息,初始化一个新的SalOp对象,然后通过sal_op_call_fill_cbs(op)补充op下的回调callbacks,这里的回调接口实现全部都在sal_op_call.c下;
3、填充op中的属性值,包括from、to等一系列基础信息;
4、调用sal_op_assign_recv_headers()将req中的消息头全部填入到op中,后面就直接使用op中的数据来反馈来电给java层了
5、调用op->callbacks中的process_request_event(op,event);
重点看一下过程4中的:
void sal_op_assign_recv_headers(SalOp *op, belle_sip_message_t *incoming){ if (incoming) belle_sip_object_ref(incoming); if (op->base.recv_custom_headers){ belle_sip_object_unref(op->base.recv_custom_headers); op->base.recv_custom_headers=NULL; } if (incoming){ op->base.recv_custom_headers=(SalCustomHeader*)incoming; } }
入参就是之前的request里面的message,将其保存到op->base.recv_custom_headers下;
接着就调用了op->callbacks下的process_requset_event();
static void process_request_event(void *op_base, const belle_sip_request_event_t *event) { SalOp* op = (SalOp*)op_base; belle_sip_server_transaction_t* server_transaction=NULL; belle_sdp_session_description_t* sdp; belle_sip_request_t* req = belle_sip_request_event_get_request(event); belle_sip_dialog_state_t dialog_state; belle_sip_response_t* resp; belle_sip_header_t* call_info; const char *method=belle_sip_request_get_method(req); ...... dialog_state=belle_sip_dialog_get_state(op->dialog); switch(dialog_state) { case BELLE_SIP_DIALOG_NULL: { if (strcmp("INVITE",method)==0) { ...... if (process_sdp_for_invite(op,req) == 0) { if ((call_info=belle_sip_message_get_header(BELLE_SIP_MESSAGE(req),"Call-Info"))) { if( strstr(belle_sip_header_get_unparsed_value(call_info),"answer-after=") != NULL) { op->auto_answer_asked=TRUE; ms_message("The caller asked to automatically answer the call(Emergency?)\n"); } } op->base.root->callbacks.call_received(op); }else{ /*the INVITE was declined by process_sdp_for_invite(). As we are not inside an established dialog, we can drop the op immediately*/ drop_op = TRUE; } break; } /* else same behavior as for EARLY state, thus NO BREAK*/ } ....... default: ms_error("unexpected dialog state [%s]",belle_sip_dialog_state_to_string(dialog_state)); break; } if (server_transaction) belle_sip_object_unref(server_transaction); if (drop_op) sal_op_release(op); }
部分过程省略了,关键两个步骤,
1、process_sdp_for_invite(op,req),判断这个request是不是invite类型的sdp请求;
2、如果是调用op下的base属性中的root->callbacks.call_received(op);
到这里,个人理解,第二个环节的处理基本完成,通过provider将接收到的event再次解析并重新构建了一个SalOp对象,而来电请求完整的SIP消息体,被保存在op->base.recv_custom_headers链表中。
第三部分
回调给JAVA层;
上一步最后执行的call_received(op)位于callbacks.c line263
static void call_received(SalOp *h){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h)); LinphoneCall *call; LinphoneCall *replaced_call; char *alt_contact; LinphoneAddress *from_addr=NULL; LinphoneAddress *to_addr=NULL; LinphoneAddress *from_address_to_search_if_me=NULL; /*address used to know if I'm the caller*/ SalMediaDescription *md; const char * p_asserted_id; .......... call=linphone_call_new_incoming(lc,from_addr,to_addr,h); ... linphone_core_add_call(lc,call); ... linphone_core_notify_incoming_call(lc,call); }
过程分析:
1、根据传进来的SalOp,并解析了其中的通话信息,通过linphone_call_new_incoming()构建LinphoneCall对象;
2、通过linphone_core_add_call()将该call添加到linphonecore中;
3、最后通过linphone_core_notify_incoming_call(lc,call),将来电信息传递到上层java中;
先看第一步:linphone_call_new_incoming
LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){ LinphoneCall *call = belle_sip_object_new(LinphoneCall); SalMediaDescription *md; LinphoneNatPolicy *nat_policy = NULL; int i; call->dir=LinphoneCallIncoming; sal_op_set_user_pointer(op,call); call->op=op; call->core=lc; call->dest_proxy = linphone_core_lookup_known_proxy(call->core, to); linphone_call_incoming_select_ip_version(call, call->dest_proxy); /*note that the choice of IP version for streams is later refined by * linphone_call_set_compatible_incoming_call_parameters() when examining the remote offer, if any. * If the remote offer contains IPv4 addresses, we should propose IPv4 as well*/ sal_op_cnx_ip_to_0000_if_sendonly_enable(op,lp_config_get_default_int(lc->config,"sip","cnx_ip_to_0000_if_sendonly_enabled",0)); md = sal_call_get_remote_media_description(op); if (lc->sip_conf.ping_with_options){ #ifdef BUILD_UPNP if (lc->upnp != NULL && linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp && linphone_upnp_context_get_state(lc->upnp) == LinphoneUpnpStateOk) { #else //BUILD_UPNP { #endif //BUILD_UPNP /*the following sends an option request back to the caller so that we get a chance to discover our nat'd address before answering.*/ call->ping_op=sal_op_new(lc->sal); linphone_configure_op(lc, call->ping_op, from, NULL, FALSE); sal_op_set_route(call->ping_op,sal_op_get_network_origin(op)); sal_op_set_user_pointer(call->ping_op,call); sal_ping(call->ping_op,sal_op_get_from(call->ping_op), sal_op_get_to(call->ping_op)); } } linphone_address_clean(from); linphone_call_get_local_ip(call, from); call->params = linphone_call_params_new(); linphone_call_init_common(call, from, to); call->log->call_id=ms_strdup(sal_op_get_call_id(op)); /*must be known at that time*/ linphone_core_init_default_params(lc, call->params); /*增加自定义的消息头解析*/ const char * extra_data; extra_data = sal_custom_header_find(sal_op_get_recv_custom_header(op),"x-extraData"); if(extra_data){ linphone_call_params_add_custom_header(call->params,"x-extraData",extra_data); } /* * Initialize call parameters according to incoming call parameters. This is to avoid to ask later (during reINVITEs) for features that the remote * end apparently does not support. This features are: privacy, video */ /*set privacy*/ call->current_params->privacy=(LinphonePrivacyMask)sal_op_get_privacy(call->op); /*config params*/ call->current_params->update_call_when_ice_completed = call->params->update_call_when_ice_completed; /*copy config params*/ /*set video support */ call->params->has_video = linphone_core_video_enabled(lc) && lc->video_policy.automatically_accept; if (md) { ...... } ...... discover_mtu(lc,linphone_address_get_domain(from)); return call; }
a、通过belle_sip_object_new()构建一个空的LinphoneCall对象,然后关联SalOp和LinhoneCore;
b、然后通过linphone_call_params_new()构建一个LinphoneCallParams对象,并关联到call->params下,再通过linphone_call_init_common()和linphone_core_init_default_params()来初始化LinphoneCallParams的配置;
c、后面还有一些配置media属性的操作,暂时不看了。
参考发起呼叫时添加自定义头消息的过程,自定义的消息头是存放在LinphoneCall下的LinphoneCallParams的,并且通话过程中的状态回调每次都会回调LinphoneCall对象。
所以如果要解析来电中的自定义头消息,可以将解析出来的消息头存放在这里构建出来的LinphoneCallParams中;
/*增加自定义的消息头解析*/ const char * extra_data; extra_data = sal_custom_header_find(sal_op_get_recv_custom_header(op),"x-extraData"); ms_message("extra_data = %s ",extra_data); if(extra_data){ linphone_call_params_add_custom_header(call->params,"x-extraData",extra_data); }
用的linphone源码中自带的一些函数
sal_op_get_recv_custom_header(op),是获取op.base->recv_custom_header对象,其实就是存的sip完整消息;
sal_custom_header_find(xx,”x-extraData”);查找sip消息中字段名为:x-extraData的字符串值;
如果这个字段存在,通过linphone_call_params_add_custom_header()将这个key和value保存到call->params下的custom_headers;
再看call_received中的第二步:linphone_core_add_call(lc,call)
其实就是关联lc和call,将当前的call保存到lc->calls下的链表中;
最后第三步:linphone_core_notify_incoming_call()
简单说这个过程的流程:
内部调用linphone_call_set_stata(LinphoneCall,LinphoneCallState,char
message),将当前的通话状态设置为LinphoneCallIncomingReceived
内部调用linphone_core_notify_call_state_changed(lc,call,cstate,message)来通知通话状态已修改,
内部调用NOTIFY_IF_EXIST(call_state_changed,lc,call,cstate,message);
NOTIFY_IF_EXIST是个宏,第一个参数是方法名,后面的是他的参数,这里调用的就是call_state_changed()接口,
NOTIFY_IF_EXIST宏内可以调用的方法,都定义在linphonecore_jni.cc中的LinphoneCoreTable下:
vTable->call_state_changed = callStateChange;
在callStateChange中通过反射的方式,加载了LinphoneCoreListenerBase下的call_State()回调。
最后我们就可以在java层获取来电sip消息中的自定义字段值,
public void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message) { LinphoneCallParams params = VoipManager.getLc().createCallParams(call); String extraData = StringTools.decodeStringFromBase64(params.getCustomHeader("x-extraData")); }
使用LinphoneCallParams自带的getCustomHeader()就可以获取指定key的头消息值。
注意:如果自定义消息想直接传json或者xml等其他格式的字符串,会造成sip格式解析异常,所以这里做了base64编码转换,另外base64转码后会在字符串尾部多一个’\n’,也会影响sip解析,需要去掉
全流程总结:
来电的sip请求触发后,消息数据最先保存在一个belle_sip_channel_t下的input_stream中,经过初步解析成字符串后,保留到该结构体下的incoming_messages属性中,多次转换后生成一个表示event事件的belle_sip_request_event_t结构体,
通过预先设置的provider来处理这个event结构体,过程中构建了一个SalOp对象,将这部分消息头字符串保存到了这个op下的SalOpBase结构体中的recv_custom_headers属性下,通过provider分析后确认这是一个invite类型的call,后调用预先设置的call_received接口去处理。
在call_received()中构建一个java层可用的LinphoneCallParams和LinphoneCall对象,同时从op中解析需要传递给java层的数据,填充到LinphoneCallParams对象中,这个param也被关联到call下,
最终通过callState()回调接口,将来电信息回调给java层
linphone源码中是不包含自定义头的数据解析和填充的,我们只需要参考发起呼叫时添加的自定义头消息的存储方式,将来电的自定义头消息存储到相应的位置即可。也就是在linphone_call_new_incoming()添加需要解析的消息头,补充到params->custom_headers,结束。
相关文章推荐
- 如何自定义消息
- MFC 如何添加自定义消息
- RocketMQ原理解析-producer 3.如何发送顺序消息
- 如何让自定义的基于CStatic的控件响应鼠标移动的消息
- 【Java TCP/IP Socket】构建和解析自定义协议消息(含代码)
- VC/MFC中如何自定义消息
- VC/MFC中如何自定义消息
- 如何使用自定义消息?--ESFramework 4.0 快速上手(04)
- Windows消息:如何自定义窗口消息与线程消息
- 使用VC如何处理自定义消息
- VC中如何实现自定义消息
- Sofia-SIP辅助文档十六 - Sofia SIP用户代理库 - "msg" - 消息解析模块
- MFC中,如何自定义用户消息
- 如何在VB例程中接收自定义消息
- Spring中自定义Schema如何解析生效详解
- 如何传送自定义json数据并解析处理
- C++Builder如何响应消息及自定义消息
- c#如何处理自定义消息
- 如何自定义特性并解析它们
- 如何处理自定义消息 (非命令消息)