Linphone 被叫方如何解析來電SIP消息中的自定義頭消息

linphone源碼中其實暫無提供自定義頭消息的解析功能,所以這裏需要添加一部分代碼,至於在什麼地方添加自定義頭消息,就需要了解linphone處理來電的sip請求的過程。

個人梳理了下大概分爲以下幾個過程:

  1. 接到請求後,從socket中解析出完整的SIP字符串,經過多次轉換後生成event類型的結構體
  2. 歸類到INVITE類型的event後,通過provider來處理,過程中會生成SalOp對象。
  3. 生成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;
      }
   }
}

過程分析:

  1. 第一個if中,大致是在初始化obj->input_stream.read_ptr,並將obj->input_stream.state改爲MESSAGE_AQUISITION;
  2. 第二個if中,通過belle_sip_message_parse_raw()來解析sip消息,並存儲到obj->input_stream.msg中;
  3. 最後調用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源碼中自帶的一些函數

  1. sal_op_get_recv_custom_header(op),是獲取op.base->recv_custom_header對象,其實就是存的sip完整消息;
  2. sal_custom_header_find(xx,”x-extraData”);查找sip消息中字段名爲:x-extraData的字符串值;
  3. 如果這個字段存在,通過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()
簡單說這個過程的流程:

  1. 內部調用linphone_call_set_stata(LinphoneCall,LinphoneCallState,char
    message),將當前的通話狀態設置爲LinphoneCallIncomingReceived
  2. 內部調用linphone_core_notify_call_state_changed(lc,call,cstate,message)來通知通話狀態已修改,
  3. 內部調用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解析,需要去掉

全流程總結:

  1. 來電的sip請求觸發後,消息數據最先保存在一個belle_sip_channel_t下的input_stream中,經過初步解析成字符串後,保留到該結構體下的incoming_messages屬性中,多次轉換後生成一個表示event事件的belle_sip_request_event_t結構體,
  2. 通過預先設置的provider來處理這個event結構體,過程中構建了一個SalOp對象,將這部分消息頭字符串保存到了這個op下的SalOpBase結構體中的recv_custom_headers屬性下,通過provider分析後確認這是一個invite類型的call,後調用預先設置的call_received接口去處理。
  3. 在call_received()中構建一個java層可用的LinphoneCallParams和LinphoneCall對象,同時從op中解析需要傳遞給java層的數據,填充到LinphoneCallParams對象中,這個param也被關聯到call下,
  4. 最終通過callState()回調接口,將來電信息回調給java層

linphone源碼中是不包含自定義頭的數據解析和填充的,我們只需要參考發起呼叫時添加的自定義頭消息的存儲方式,將來電的自定義頭消息存儲到相應的位置即可。也就是在linphone_call_new_incoming()添加需要解析的消息頭,補充到params->custom_headers,結束。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章