linphone函数拨号过程分析

extern "C" jobject Java_org_linphone_core_LinphoneCoreImpl_inviteAddress(JNIEnv* env
        ,jobject  thiz
        ,jlong lc
        ,jlong to) {
    return getCall(env, linphone_core_invite_address((LinphoneCore*)lc,(LinphoneAddress*)to));
}

linphone 建立通话过程说明
1 拨号call 过程
用户执行呼叫,调用call 命令
Linphone 中调用该命令对应的执行函数lpc_cmd_call
如果lc-call 不为空,也就是说linphone 全局结构体上当前的会话还存在,则输出打印,要求用户
先关闭当前call;否则调用linphone_care_invite 处理call 命令。(该版本只支持一个call 存在)
在linphone_core_invite 中:
首先调用linphone_core_interpret_usl 解析URL 地址。输入的地址可能是一串字符,通过该函数
将其中的关键元素解析出来,主要是按照osip_from结构体的格式解析出来。
然后调用linphone_core_invite_address 进一步的以osip_from格式的地址为参数进行处理,完了释
放地址参数。

在linphone_core_invite_address 中:

/**
 * Initiates an outgoing call given a destination LinphoneAddress
 *
 * @ingroup call_control
 * @param lc the LinphoneCore object
 * @param addr the destination of the call (sip address).
 *
 * The LinphoneAddress can be constructed directly using linphone_address_new(), or
 * created by linphone_core_interpret_url().
 * The application doesn't own a reference to the returned LinphoneCall object.
 * Use linphone_call_ref() to safely keep the LinphoneCall pointer valid within your application.
 *
 * @return a LinphoneCall object or NULL in case of failure
**/
LinphoneCall * linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *addr){
    LinphoneCall *call;
    LinphoneCallParams *p=linphone_core_create_default_call_parameters(lc);
    p->has_video &= !!lc->video_policy.automatically_initiate;
    call=linphone_core_invite_address_with_params (lc,addr,p);
    linphone_call_params_destroy(p);
    return call;
}

在该函数中对地址信息进行进一步的简单处理,获取到对外的本地信息,然后调用

/**
 * Initiates an outgoing call given a destination LinphoneAddress
 *
 * @ingroup call_control
 * @param lc the LinphoneCore object
 * @param addr the destination of the call (sip address).
    @param params call parameters
 *
 * The LinphoneAddress can be constructed directly using linphone_address_new(), or
 * created by linphone_core_interpret_url().
 * The application doesn't own a reference to the returned LinphoneCall object.
 * Use linphone_call_ref() to safely keep the LinphoneCall pointer valid within your application.
 *
 * @return a LinphoneCall object or NULL in case of failure
**/
LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const LinphoneAddress *addr, const LinphoneCallParams *params)
{

调用
linphone_call_new_outgoing 发起会话请求。
linphone_call_new_outgoing 中,
首先创建linphone call 内存对象实例。

LinphoneCall *call = belle_sip_object_new(LinphoneCall);

方向设置为call outgoing,创建一个salop 对象实例,其root指针指向初始化时创建的全局sal 结构体上,并对op 的一些基本域进行设置。在会话过程中,与sal相关的操作主要还是基于salop 的。另外,userpointer 指针指向call 本身。
调用linphone_core_get_local_ip 获取本地的ip 地址。

call->dir=LinphoneCallOutgoing;
call->core=lc;
linphone_call_outgoing_select_ip_version(call,to,cfg);
linphone_call_get_local_ip(call, to);
linphone_call_init_common(call,from,to);
call->params = linphone_call_params_copy(params);
/**
 * Policy to use to pass through firewalls.
 * @ingroup network_parameters
**/
typedef enum _LinphoneFirewallPolicy {
    LinphonePolicyNoFirewall, /**< Do not use any mechanism to pass through firewalls */
    LinphonePolicyUseNatAddress, /**< Use the specified public adress */
    LinphonePolicyUseStun, /**< Use a STUN server to get the public address */
    LinphonePolicyUseIce, /**< Use the ICE protocol */
    LinphonePolicyUseUpnp, /**< Use the uPnP protocol */
} LinphoneFirewallPolicy;

如果设置了nat 防火墙策略,则就用nat 防火墙地址为本地ip 地址,否则,如果配置了ipv6,则使用ipv6 地址。如果仍然没有得到结果,则
调用sal_get_default_local_ip 接口来获取。该接口使用底层exosip 提供的方法,即利用socket 的
gethostbyname 接口来在与网关建立connected 连接后从socket 中获取本地ip 地址信息。
调用create_local_media_description 创建本地媒体描述结构体。在该接口中,创建一个
SalMediaDescription 对象实例,并用之前获取的信息对其进行设置,包括流数量为1,本地ip 地址,
用户名,带宽。针对其携带的流,流的地址为本地ip 地址,流的端口为配置的RTP 音频端口,proto
为SalProtoRtpAvp,类型为audio,ptime 为netconfig 中的down_ptime。对于payload 的配置逻辑如
下:从编解码器配置项中读取所有的音频编解码器payload,如果是enabled,并且linphone 当前支
持该编解码器,带宽也允许,则将该payload 添加到streamer 的payload 链表上。目前payload 的带
宽是通过payload 的Bitrate 计算出来的。计算方法:包大小为ip4 头+UDP 头+RTP 头+Bitrate 除以
50*8,即除以400。因为Bitrate 为基于比特单位,除以8 变为字节,但是这里除以50 是为什么?貌
似是包的数量,也就是一秒钟采样的数据用50 个包来传送。此时计算出来的为包的大小,将其再乘
以8 乘以50 变为包含ip、UDP 及RTP 包头的情况下的Bitrate,此值除以1000 作为linphonecore 中
的音频带宽值。基于上述计算得出来的音频带宽值和网络配置中配置的上下行带宽值更新
linphonecore 的带宽配置的方法如下:如果给的带宽值为0,即表示无穷,音视频的下载带宽设置为
-1,否则,计算出来的音频带宽值和给定带宽值中较小者作为音频下载带宽,给定带宽值与音频下
载带宽的差值减去10 与0 的较大者作为视频下载带宽值,也就是视频带宽或者为0,或者为可用带
宽减去音频所用的带宽(优先保证音频)再减去10 作为缓冲界限后的值。上传带宽的配置方法也一
致。如果当前配置的带宽值中的较小者(上行和下行选择)大于当前编解码器Bitrate 所占用的带宽,
则将其clone一份放到streamer的payload链表上,否则不添加。在处理完配置的音频编解码器payload
后,从全局av_profile 中找到telephone-event 也将其添加到streamer 的payload 链表上。Streamer 的
带宽值被设置为音频下载带宽值,应该小于等于media 的带宽配置。如果视频也被允许,那么
streamer1 将作为视频流的描述符,同样从RTP 配置信息中得到视频端口赋给该描述符,proto 同音
频部分,类型此时为SalVideo。类似音频部分,将codec 配置中的符合带宽限制的视频编解码器
payload 添加到该streamer 的payload 链表上。如果视频下载带宽不为0,则该streamer 的带宽值被
设置为视频下载带宽值。至此,media 描述符就创建完成。Media 描述符当前是被挂载到call 的
localdesc 上。
调用linphone_call_init_common 对call 的其他域进行设置,状态为LCStateInit,start_time 为当前
时间,media_start_time 为0,创建call_log 实例记录拨号记录,通知所有的friends 我们当前的
onthephone 状态。如果是设置了stun 服务器,则调用linphone_core_run_stun_tests 测试stun 服务器,
并配置streamer 的endpoint candidate 的端口和地址。
调用discover_mtu 获取当前的mtu 值,这只在当前netconfig 中的mtu 设置为0 时才进行。发现
mtu 的过程也是通过向对端发送数据,然后根据socket 的options 操作来查看,比如根据收到的ICMP
包信息,根据获取到的mtu 值重新设置mediastream的mtu。
综上,在new outgoing 的过程中,我们创建了call 实例,salop 实例,media 实例同时包含streamer
实例。基本关系为call–>salop,call–>media–>stream。本质上来讲还是在初始化call,但在此过程中
也初始化了需要的salop,以及media。
上一步通过linphone_call_new_outgoing 为发起一个新的会话做好了准备,包括创建了需要的call
实例,salop 实例等,这些相关信息保存到call 对象中,该对象在此时被挂载到linphonecore 上。
如果目的代理不为空, 或者sipconfi 的ping_with_options 为FALSE , 则掉用
linphone_core_start_invite 发起会话请求,否则,call 的状态被设置为LCStatePreEstablishing,该状态
指示稍后,即ping 完后继续发起invite 请求。为了完成ping 操作,先创建一个用于ping 的salop,
调用sal_ping 基于该op 以及from 和real_url 参数发送sip 的ping 数据,重新将call 的start_time 设
置为当前时间。
对于linphone_core_start_invite 调用:
在调用该接口前,我们已经创建了linphone core 实例,并在发起invite 请求的准备过程中创建了
call 实例,以及得到了dest_proxy 地址信息。在这些准备工作完成的前提下,系统进一步处理invite
相关的后续操作:
首先调用get_fixed_contact 来获取联系人信息。
如果当前设置了防火墙,并配置了nat 地址,则从linphone core 结构体实例中获取首要的contact
信息。这些信息基本上是从sip_conf 中拿取的。
如果上一步失败,并且call 上的salop 结构体实例已经被创建了,并且其上的contact 信息不为空,
则返回空,表明不需要修改contact 信息。
如果上一步失败,则判断ping_op 操作是否成功完成,如果是,则使用ping_op 上的contact 信息。
如果上一步失败,并使用了代理,则使用register 时的contact 信息
如果还失败,则使用本地ip 地址和配置到linphone core 上的端口信息组合出contact 信息返回
如果在上一步获取sip_conf 中的contact 信息时返回失败,则该接口此时返回空
通过上面调用,如果获取到了contact 信息,则将其设置到call 的op 的contact 域上。
Call 的state 设置为LCStateInit
调用linphone_core_init_media_streams 初始化媒体流。参数为linphone core 实例和call 实例
首先从call 实例的localdesc 上拿到media 描述符
基于media 描述符上的stream[0]也就是音频流的端口调用audio_stream_new 创建audiostream。
在audio_stream_new 中,创建了一个audiostream 结构体实例,stream 的session 域被初始化
为一个RTP session,这是通过调用create_duplex_rtpsession 创建的。在该接口中,创建了一个全双
工的RTP session 结构体实例。首先通过调用rtp_session_new 为RTP session 实例分配内存,并调用
rtp_session_init 对这个session 进行初始化。这里的初始化包括设置session 的mode(send or recv or
send_and_recv)。并根据mode 设置session 的flags。如果可以发送,发送ssrc 初始化为一个随机值。
并设置默认的源描述信息for rtcp,挂在session 的sd 上。设置session 的rcv 和snd 的profile,此处
都设置为av_profile 全局变量了。初始化rtp 和rtcp 的socket 都为-1,配置默认的接收和发送socket
buffer 大小。
从rtp_session_init 中出来接着配置RTP session 的一些类似全局的参数。Recv_buff_size 配置为
MAX_RTP_SIZE,调度模式为关闭状态,阻塞模式为不使用该模式,自适应平衡抖动补偿,对称
RTP,设置本地地址,此时会创建rtp和rtcpsocket,并对socket的参数进行配置。比如是否reuse address,
设置socket buffer size 等。注册timestamp 和ssrc_changed 事件的回调函数,以及ssrc changed 的触发
阈。最后返回创建的RTP session 结构体实例。
返回的RTP session 实例被挂到了stream的session 域上。接着调用ms_filter_new 创建了RTP
send filter 结构体实例。这被挂载到了stream 的RTP send 域上了。最后对流的相关参数进行了初始
化。在ms_filter_new 中,会遍历系统最初初始化时创建的filter 描述符链表desc_list,从其上找到id
与当前要创建的id 一致的描述符,然后调用ms_filter_new_from_desc 基于该描述符创建filter。在这
个接口中,首先初始化了一个msfilter 的结构体实例,然后把当前找到的描述符挂到该filter 的desc
域上,调用描述符的init 接口对filter 进行初始化,最后返回这个filter。
Audiostream 创建成功后被挂载到了linphone core 结构体实例的audiostream域上。
基于初始化linphone core config 时,从配置文件及系统中获取的对sound 部分的配置信息对
audiostream 进行实际使用上的配置。也就是针对具体使用实例的配置。之前可能就是全局的系统参
数级别的配置。这包括增益的配置,回音消除的配置,echo limiter 的配置,自动增益的配置,噪音
的相关配置等。
如果在linphone core 初始化时已经初始化了rtp_transport,则将audiostream上的RTP session 上的
rtp 和rtcp 上的tr 指向linphone core 的 a_rtp 和a_rtcp 上。相应的,这两个结构体的session 指针指向
这里的session。
至此,音频流的初始化工作基本完成。

Linphone是一款开源的VoIP(Voice over Internet Protocol,网络电话)库和应用程序,它提供了C++接口,用于集成视频会议功能。以下是一个简单的示例,概述如何使用Linphone SDK在C++中实现视频会议: 1. **环境准备**: 首先,你需要在项目中包含Linphone库,并设置编译选项。确保已安装了Linphone开发包,并添加相应的头文件和链接器依赖。 2. **初始化 Linphone**: 在程序启动时,创建一个`LinphoneCore`实例,这是整个通信的核心。使用`linphone_core_new`函数并注册必要的回调,如呼叫状态改变等。 ```cpp LinphoneCore* core = linphone_core_new(); linphone_core_set_username(core, "your_username"); linphone_core_set_password(core, "your_password"); ``` 3. **创建会话**: 创建一个`LinphoneCall`对象来发起或接受视频通话。使用`linphone_core_make_call`创建呼叫,传入接收者的地址。 ```cpp char* callee_jid = "callee@example.com"; LinphoneCall* call = linphone_core_make_call(core, callee_jid); ``` 4. **加入会议**(如果是个会议): 对于视频会议,可能需要找到会议ID或加入会议邀请,然后调用`linphone_call_answer_with_video`加入会议。 5. **配置视频和音频**: 使用`LinphoneVideoProfile`和`LinphoneAudioProfile`设置视频和音频的编码器、分辨率等参数。 6. **处理事件**: 当通话开始后,你需要处理`LinphoneCall`对象的事件,如视频流可用、音频流可用等,可能需要创建`LinphoneMediaSection`对象来处理媒体数据。 7. **释放资源**: 通话结束后,记得释放`LinphoneCall`、`LinphoneCore`以及其他相关的资源。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值