一、首先看一下如何在发起去电的sip请求中添加自定义的消息头

增加自定义头消息发方法,so已经提供了native方法,

发起呼叫的示例如下:

LinphoneCallParams params = lc.createCallParams(null);
if(!TextUtils.isEmpty(extraData)){params.addCustomHeader("x-extraData",extraData);
}
lc.inviteAddressWithParams(lAddress, params);

extraData是待增加的头消息。
addCustomHeader(key,value)在native下的实现如下: native接口申明全部在linphonecore_jni.cc

extern "C" void Java_org_linphone_core_LinphoneCallParamsImpl_addCustomHeader(JNIEnv *env, jobject thiz, jlong lcp, jstring         jheader_name, jstring jheader_value){const char* header_name = GetStringUTFChars(env, jheader_name);const char* header_value = GetStringUTFChars(env, jheader_value);linphone_call_params_add_custom_header((LinphoneCallParams*)lcp,header_name,header_value);ReleaseStringUTFChars(env, jheader_name, header_name);ReleaseStringUTFChars(env, jheader_value, header_value);
}

linphone_call_params_add_custom_header(),位于call_params.c中;

void linphone_call_params_add_custom_header(LinphoneCallParams *params, const char *header_name, const char     *header_value){params->custom_headers=sal_custom_header_append(params->custom_headers,header_name,header_value);
}

使用了sal_custon_header_append()来将传入的head_key/value构建成call中使用的结构体

SalCustomHeader *sal_custom_header_append(SalCustomHeader *ch, const char *name, const char *value){belle_sip_message_t *msg=(belle_sip_message_t*)ch;belle_sip_header_t *h;if (msg==NULL){msg=(belle_sip_message_t*)belle_sip_request_new();belle_sip_object_ref(msg);}h=belle_sip_header_create(name,value);if (h==NULL){belle_sip_error("Fail to parse custom header.");return (SalCustomHeader*)msg;}belle_sip_message_add_header(msg,h);return (SalCustomHeader*)msg;
}

这是一个追加消息头的过程,

1.params下的custom_headers链表,如果不存在,则在这里先进行初始化belle_sip_request_new(),

2.通过belle_sip_header_create(name,value)生成一个belle_sip_header_t* 结构体h;

3.通过belle_sip_message_add_header(msg,h)将生成的h追加到msg后面,最后返回;

总结下添加过程:
添加的自定义头消息的key-value,首先被保存在LinphoneCallParam对象中,存放在params->custom_headers中了,custom_headers是一个链表结构

二、分析是如何将LinphoneCallParam中配置的参数,加载到发起呼叫的SIP请求中。

发起呼叫是通过 inviteAddressWithParams(LinphoneAddress address, LinphoneCallParams params)来实现的native实现在linphonecore.c line3387。

LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const LinphoneAddress *addr, const LinphoneCallParams *params){ms_message("linphone_core_invite_address_with_params playcard = %s, captcard = %s",lc->sound_conf.play_sndcard->desc->driver_type,lc->sound_conf.capt_sndcard->desc->driver_type);const char *from=NULL;LinphoneProxyConfig *proxy=NULL;LinphoneAddress *parsed_url2=NULL;char *real_url=NULL;LinphoneCall *call;bool_t defer = FALSE;LinphoneCallParams *cp;......cp = linphone_call_params_copy(params);....../* if no proxy or no identity defined for this proxy, default to primary contact*/if (from==NULL) from=linphone_core_get_primary_contact(lc);parsed_url2=linphone_address_new(from);call=linphone_call_new_outgoing(lc,parsed_url2,linphone_address_clone(addr),cp,proxy);....../* this call becomes now the current one*/lc->current_call=call;linphone_call_set_state (call,LinphoneCallOutgoingInit,"Starting outgoing call");call->log->start_date_time=ms_time(NULL);linphone_call_init_media_streams(call);
...if (defer==FALSE) {if (linphone_core_start_invite(lc,call,NULL) != 0){/*the call has already gone to error and released state, so do not return it*/call = NULL;}}if (real_url!=NULL) ms_free(real_url);linphone_call_params_destroy(cp);return call;
}

这个接口下只看三个环节:

1. LinphoneCallParams *cp = linphone_call_params_copy(params);
2. linphone_cal_new_outgoing(lc, fromaddress, toaddress, cp, proxy);
3. linphone_core_start_invite(lc,call,Null);

首先分析第二环节的具体流程;
linphone_cal_new_outgoing()位于linphonecall.c line1236

LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, const LinphoneCallParams *params, LinphoneProxyConfig *cfg){ms_message("linphone_call_new_outgoing");LinphoneCall *call = belle_sip_object_new(LinphoneCall);call->dir=LinphoneCallOutgoing;call->core=lc;call->dest_proxy=cfg;linphone_call_outgoing_select_ip_version(call,to,cfg);linphone_call_get_local_ip(call, to);call->params = linphone_call_params_copy(params);linphone_call_init_common(call, from, to);
...discover_mtu(lc,linphone_address_get_domain (to));if (params->referer){call->referer=linphone_call_ref(params->referer);}linphone_call_create_op(call);return call;
}

这里会构建一个新的LinphoneCall对象,并根据传入address和cp将通话信息全部填入到call中;
call->params = linphone_call_params_copy(params);
最后调用linphone_call_create_op(call),完成后返回call对象;

linphone_call_create_op(call)位于linphonecall.c line1070;

void linphone_call_create_op(LinphoneCall *call){if (call->op) sal_op_release(call->op);call->op=sal_op_new(call->core->sal);sal_op_set_user_pointer(call->op,call);if (call->params->referer)sal_call_set_referer(call->op,call->params->referer->op);linphone_configure_op(call->core,call->op,call->log->to,call->params->custom_headers,FALSE);if (call->params->privacy != LinphonePrivacyDefault)sal_op_set_privacy(call->op,(SalPrivacyMask)call->params->privacy);/*else privacy might be set by proxy */
}

这里会通过sal_op_new()初始化call->op结构体,
然后调用 linphone_configure_op(call->core, call->op, call->log->to, call->params->custom_headers, FALSE)来填充信息到call->op,
到这里,再次出现了call->params->custom_headers,发起呼叫时添加的头消息就是保存在这里的。

linphone_configure_op(lc, SalOp* op, dest, SalCustomHeader *header, with_contact) 位于linphonecore.c line 3338;

void linphone_configure_op(LinphoneCore *lc, SalOp *op, const LinphoneAddress *dest, SalCustomHeader *headers, bool_t with_contact){ms_message("linphone_config_op");bctbx_list_t *routes=NULL;LinphoneProxyConfig *proxy=linphone_core_lookup_known_proxy(lc,dest);const char *identity;......sal_op_set_to_address(op,dest);sal_op_set_token(op,token);sal_op_set_from(op,identity);ms_message("identity = %s  sal_token = %s",identity,((SalOpBase*)op)->token);sal_op_set_sent_custom_header(op,headers);sal_op_set_realm(op,linphone_proxy_config_get_realm(proxy));......
}

这个函数主要是将通话中的一些配置信息设置到SalOp中,
过程中包含一步: sal_op_set_sent_custom_header(op,headers);

void sal_op_set_sent_custom_header(SalOp *op, SalCustomHeader* ch){SalOpBase *b=(SalOpBase *)op;if (b->sent_custom_headers){sal_custom_header_free(b->sent_custom_headers);   b->sent_custom_headers=NULL;      }if (ch) belle_sip_object_ref((belle_sip_message_t*)ch);b->sent_custom_headers=ch;
}

过程比较简单,就是将SalCustomHeaders的自定义头消息保存到op->sent_custom_headers中;

继续分析linphone_core_invite_address_with_params()中的第三环节:linphone_core_start_invite(lc,call,Null);

代码太长,省略其他部分,此函数关键调用了 sal_call(call->op,from,real_url)来发起呼叫;

sal_call位于sal_op_call.c line772,

int sal_call(SalOp *op, const char *from, const char *to){
......invite=sal_op_build_request(op,"INVITE");sal_op_fill_invite(op,invite);sal_op_call_fill_cbs(op);...return sal_op_send_request(op,invite);
}

1. 通过sal_op_build_request(op,”INVITE”),来创建一个INVITE类型的请求结构体belle_sip_request_t;
2. 然后向这个结构体中填充op中存储的部分基础信息;
3. 配置当前这个op需要的呼叫过程中的各类处理回调,这些回调很重要,会频繁用到,在这里的流程分析上暂时不介绍了
4. 返回sal_op_send_request(op,invite)

直接看sal_op_send_request()位于sal_op_impl.c line405

int sal_op_send_request(SalOp* op, belle_sip_request_t* request)  {belle_sip_message("sal_op_send_request");bool_t need_contact=FALSE;if (request==NULL) {return -1; /*sanity check*/}if (strcmp(belle_sip_request_get_method(request),"INVITE")==0||strcmp(belle_sip_request_get_method(request),"REGISTER")==0||strcmp(belle_sip_request_get_method(request),"SUBSCRIBE")==0||strcmp(belle_sip_request_get_method(request),"OPTIONS")==0||strcmp(belle_sip_request_get_method(request),"REFER")==0) /* Despite contact seems not mandatory, call flow example show a Contact in REFER requests*/need_contact=TRUE;return _sal_op_send_request_with_contact(op, request,need_contact);
}

因为是INVITE类型的请求,这里的need_contact是TRUE;

sal_op_send_request_with_contact(op,request,bool_); 位于sal_op_impl.c line315;
函数内会调用_sal_op_add_custom_headers(op,request),

void _sal_op_add_custom_headers(SalOp *op, belle_sip_message_t *msg){ms_message("_sal_op_add_custom_headers");if (op->base.sent_custom_headers){belle_sip_message_t *ch=(belle_sip_message_t*)op->base.sent_custom_headers;belle_sip_list_t *l=belle_sip_message_get_all_headers(ch);belle_sip_list_t *elem;for(elem=l;elem!=NULL;elem=elem->next){add_headers(op,(belle_sip_header_t*)elem->data,msg);}belle_sip_list_free(l);}
}

此处会检查op->base.sent_custom_headers是否存在自定义头消息,
如果有,调用add_headers去添加到request结构体里面, sal_op_impl.c line286;

static void add_headers(SalOp *op, belle_sip_header_t *h, belle_sip_message_t *msg){char* header_string=belle_sip_object_to_string(h);ms_message("add_headers  [%s]",header_string);if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(h,belle_sip_header_contact_t)){belle_sip_header_contact_t* newct;/*special case for contact, we want to keep everything from the custom contact but set automatic mode and add our own parameters as well*/sal_op_set_contact_address(op,(SalAddress*)BELLE_SIP_HEADER_ADDRESS(h));newct = sal_op_create_contact(op);belle_sip_message_set_header(BELLE_SIP_MESSAGE(msg),BELLE_SIP_HEADER(newct));return;}/*if a header already exists in the message, replace it*/belle_sip_message_set_header(msg,h);
}

此处自定义头消息非联系人类型的,所以直接进入最下面的belle_sip_message_set_header(msg,h);

void belle_sip_message_set_header(belle_sip_message_t *msg, belle_sip_header_t* header){char* header_string=belle_sip_object_to_string(header);belle_sip_message("belle_sip_message_set_header [%s]",header_string);headers_container_t *headers_container=get_or_create_container(msg,belle_sip_header_get_name(header));belle_sip_object_ref(header);headers_container->header_list=belle_sip_list_free_with_data(headers_container->header_list,belle_sip_object_unref);headers_container->header_list=belle_sip_list_append(headers_container->header_list,header);
}

到这里自定义的头消息就通过belle_sip_list_append追加到msg下了, msg就是之前创建的request结构体。

后面的sip消息发送就是通过request中的元素去拼接的,这里暂时不看了,

总结:
1、首先通过创建一个LinphoneCall对象,并将LinphoneCallParam保存到call->params下面,
2、然后创建一个SalOp对象,并见call->params中的很多配置信息复制到op中,其中包括将params->custom_headers链表中的很多头消息SalCustomHeader对象,保存到op.base->sent_custom_headers链表中;
3、最后通过sal_op_send_request()来构建一个belle_sip_request_t结构体,并将op.base->sent_custom_headers中的头消息保存到request->headers_container->header_list里面。
4、linphone最终发送sip请求的时候都是从这个request中来取值并完成消息体的拼接的。

三、如何在java层获取去电请求中增加的自定义头消息

linphone的java层提供了直接读取自定义头的方法。
首先需要知道的是,发起去掉后,linphone会通过LinphoneCoreListener中的callState()接口来回调通话过程状态变化的

public void callState(LinphoneCore lc, LinphoneCall call, LinphoneCall.State state, String message) {Logs.d(TAG, "callState state = [" + state + "] , message = " + message);LinphoneCallParams params = VoipManager.getLc().createCallParams(call);String extraData = StringTools.decodeStringFromBase64(params.getCustomHeader("x-extraData"));}

只要是按照linphone提供的LinphoneCallParam.addCustomHeader(“name”,”value”)添加的自定义头消息,都可以通过LinphoneCallParam.getCustomHeader(“name”),来读取到对应的name值。

注:这里我们在读取数据后进行了base64解码,主要是因为如果想在sip消息体中添加json、xml等其他格式的字符串时,会造成SIP消息格式的异常,所以需要进行base64的转码传输,另外base64在转后后会在字符串尾部追加一个’\n’,需要剔除,否则也会造成sip格式异常
————————————————

上一篇:Linphone-android 登录过程增加自定义消息头流程分析_今人不见古时月,今月曾经照古人的博客-CSDN博客

下一篇:Linphone 被叫方如何解析来电SIP消息中的自定义头消息_今人不见古时月,今月曾经照古人的博客-CSDN博客

转载于:https://blog.csdn.net/yfloctar/article/details/78684535

Linphone android去电增加自定义SIP消息头的流程分析相关推荐

  1. SIP消息头域的说明

    当前位置: 主页>流媒体开发>规范及协议> SIP消息头域的说明 编辑:落鹤生 时间:2011-03-11 09:16 点击:378次 为描述消息基本属性的通用头域,可用于请求消息或 ...

  2. Android蓝牙增加自定义按键

    Android蓝牙增加自定义按键 方式1 方式2 公司设备需要增加蓝牙手柄,直接使用可以连接但按键事件没反应,一番研究发现得增加自定义的按键. 说明: Android版本:6.0 蓝牙-设备协议:AV ...

  3. android物理按键输入法,Android输入法框架中按键消息的处理流程

    最近研究了一下Android输入法,发现Android输入法框架中按键消息的处理流程和一般应用程序的处理流程有很大的不同,故在此做个总结. 一.一些名词缩写 IMF(Input MethodFrame ...

  4. android app启动流程分析,Android应用开发之Android 7.0 Launcher3的启动和加载流程分析...

    本文将带你了解Android应用开发Android 7.0 Launcher3的启动和加载流程分析,希望本文对大家学Android有所帮助. Android 7.0 Launcher3的启动和加载流程 ...

  5. Android 7.0 Launcher3的启动和加载流程分析----转载

     Android 7.0 Launcher3的启动和加载流程分析,Launcher的本质就是一个普通应用,它比普通应用多配置了Category的Android:name="android ...

  6. 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | dvmDexFileOpenPartial | dexFileParse | 脱壳点 | 获取 dex 文件在内存中的首地址 )

    文章目录 前言 一.DexPrepare.cpp 中 rewriteDex() 方法分析 二.DvmDex.cpp 中 dvmDexFileOpenPartial() 方法分析 ( 脱壳点 ) 三.D ...

  7. 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmContinueOptimizati() 函数分析 )

    文章目录 前言 一.DexPrepare.cpp 中 dvmContinueOptimizati() 方法分析 前言 上一篇博客 [Android 逆向]整体加固脱壳 ( DEX 优化流程分析 | D ...

  8. ros自定义service消息.srv文件中增加自定义.msg消息

    先制作msg文件 1. 在disinfect_msg包下创建 :testInfo.msg 文件 int32 Id string TargetName string X string Y string ...

  9. Android 7.0 ActivityManagerService(5) 广播(Broadcast)相关流程分析

    本篇博客旨在分析Android中广播相关的源码流程. 一.基础知识 广播(Broadcast)是一种Android组件间的通信方式. 从本质上来看,广播信息的载体是intent.在这种通信机制下,发送 ...

最新文章

  1. windows phone 越狱教程:利用学生帐号解锁并部署软件教程(图文并茂)
  2. 复杂系统设计 企业开发的困境
  3. 【数据库】适用于SQLite的SQL语句(二)
  4. PPT 下载 | 数据治理中的一些挑战与应用
  5. pytorch基础知识整理(一)自动求导机制
  6. Java实现单链表的逆序打印
  7. 详解NLP技术中的:预训练模型、图神经网络、模型压缩、知识图谱
  8. 【“互联网+”大赛华为云赛道】GaussDB命题攻略:支持三种开发语言,轻松完成数据库缓冲池
  9. 提示不支持IE6的脚本
  10. Arp病毒专杀工具下载及其防治解决方案
  11. 【基础】创建react脚手架
  12. STM32H7 ADC hal库
  13. Android 点击按钮切换图片
  14. 实体店运用互联网思维进行客户裂变,不到8个月净赚2000万
  15. python录音详解_Python爬虫实战案例:取喜马拉雅音频数据详解
  16. latex写中文毕业论文(北交大博士毕业论文模版)
  17. 1580_AURIX_TC275_SMU模块初步
  18. 外地户籍应届毕业生落户上海申请及办理流程(2017更新)
  19. numpy中的seed
  20. 获取网络时间(国家标准时间-北京时间为准)

热门文章

  1. Python菜鸟小萌新对正则表达式的理解
  2. 借路人卡多刷4百 事情原因大曝光怎么还钱?
  3. 2013年山东省第四届ACM大学生程序设计竞赛
  4. PAT 乙级 1075  链表元素分类
  5. 僵尸突击队+蒲公英联机平台联机教程
  6. 《MongoDB极简教程》第一章 NoSQL简史 MongoDB安装环境配置
  7. springboot处理参数再转发请求_Springboot 2.0---WebFlux请求处理流程
  8. 网易云音乐数据治理探索与实践
  9. Shell第三天-讲义
  10. git 2.33.1 访问服务器报Permission denied (publickey)