Linphone 分析
一 linphone 架构及组成模块..............................................................................................................................2
二 linphone 系统框图...........................................................................................................................................3
三 linphone 中各个模块说明..............................................................................................................................3
四 linphone 中数据结构说明..............................................................................................................................7
五 linphone 的初始化过程...................................................................................................................................7
六 linphone 建立通话过程说明........................................................................................................................10
1 拨号 call 过程...........................................................................................................................................10
2 等待响应...................................................................................................................................................16
3 Answer 过程分析.......................................................................................................................................21
4 关于 RTP 及音视频流的网络传输........................................................................................................22
5 总结............................................................................................................................................................23
七 linphone 会话执行过程 log 分析.................................................................................................................24
八 linphone 使用参考.........................................................................................................................................40
岳维功
基于 linphone-3.3.2 版本,新版本 linphone-3.4.3 支持同时有多路 call,所以,相比之前版本会有不少
变化。
一 linphone 架构及组成模块
Linphone 是一款跨平台的可视电话客户端软件,同时支持视频通话功能。Linphone 可以在 Linux,
windows 等主流操作系统平台上运行。
Linphone 基于开源软件构建,本身也是开源软件。Linphone 架构中 sip 协议的处理基于 osip 以及
exosip 两个开源库实现,媒体数据的选择整合处理使用mediastream2 完成,该软件使用ffmepg、speedx
等多款开源软件完成音视频的编解码,并通过 ortp 完成基于 rtp 协议的音视频数据传输。ortp 是一款
处理 RTP 会话的开源软件。
1 整体架构图如下:
整个软件分为两层,上层为用户接口前端(user interface frontends),下层为 linphone 核心引擎
(linphone core engine)。
2 功能模块说明:
Liblinphone 核心引擎实现了 linphone 所有的功能函数,而且能够方便的添加音频和视频的呼叫
功能。Liblinphone 也提供高层的 API,用来初始化,接收或者终止呼叫。Liblinphone 依赖于下面三
个组件:
1 Mediastreamer2
这是一个支持多种平台的轻量级的流技术引擎,主要适合于开发语音和视频电话应用程序。该引
擎主要为 linphone 的多媒体流的收发,包括语音和视频的捕获、编码解码以及渲染。
2 ortp2
Ortp 是一个 RTP 库。为基于 RTP 协议的媒体流传输提供支持。通过 mediastream2 编码的数据就
是使用 ortp 库发送到网络的另一端。
3 eXosip2
Exosip2 为 sip 协议的实现。这部分实际上是由 exosip2 和 osip2 两个库共同完成的。使用 sip 协议
完成路由、媒体协商以及会话的建立和管理,为直接的媒体流的传输提供基础。
二 linphone 系统框图
系统运行框图
Linphone core
任务
iterate
process
Sip
sal_iterate
Ict exec
Ist exec
Nict exec
Nist exec
Read msg
Exosip任务
Audio stream
process
Vedio stream
process
Hard-
read
filters
Ms/
ticker/
audio
Hard-
read
hard
hard
filters
filters
Ms/
ticker/
vudio
filters
Rtp recv
Rtp send
Rtp recv
Rtp send
Sip msg
Sip msg
接收
发送
Rtp
接收
Rtp
发送
Rtp
接收
Rtp
发送
用户界面
网络接口
关于上面框图的一些说明:
通话双方在通信前使用 exosip 进行会话协商。上图左边部分展示这一部分的流程。Exosip 后台
任务完成数据的接收和发送,并通过事件队列通知 linphone 底层的状态变化。
filter 的构建在会话协商成功建立后就顺带完成了,并且 ticker 任务也跑起来了。此时按照 filter
graphics 构建的通道,音视频流不断的从硬件设备上读取,并经过编码压缩送给 RTP 会话,之后送
到对端,对端到达的音视频流也经过 RTP 会话接收送到解码解压缩 filter,还原出原始的音视频流交
给硬件设备播放。媒体数据在这两路流中源源不断的流动,完成了双方的可视通话。
上层 linphone 的 core 任务也不断的对底层进行迭代检查。所做的基本工作如下:
对于 sip 协议部分,core 一直等待从事件队列上拿事件。这些事件是 exosip 任务在处理 sip 消息
过程中添加到事件队列上的。每当得到新的事件后,core 就从应用层的角度出发,进行处理。
对于视频流:基本上只处理 rtcp 数据包到达的事件。stream 上也有一个事件队列,用于保存该流
上的相关事件。对于 rtcp 数据包事件,core 也只处理 sr 类型 rtcp 包,即发送端报告,得到 jitter 和
包丢失率。如果设置了自适应比特率,则调用相关接口进行处理。此过程不断进行,直到当前事件
上的包处理完。
对于音频流,检查流是否还是活动的。通过比较 RTP stats 中接收的数据包数目是否发生变化,
如果在超时时间到达后,接收的数据量还没有发生变化,则认为音频没有响应。
三 linphone 中各个模块说明
1 Linphone coreapi 中子模块说明:
Coreapi 中的各个模块就是上层的处理模块,包括 configure 文件的处理接口,address 的处理接口,
chat 的处理接口,sal 的处理接口,proxy 的处理接口,authorization 的处理接口,friends 的处理接口,
callback 的处理接口,state 的处理接口,杂项处理接口等。这相当于高层的几个模块,提供给用户
的接口调用主要都在 linphonecore.c 中
1) Callback 模块:
该模块下的回调函数都是用于 sal 模块调用的。当 sal 处理完 sip 协议的处理后,就会调用相应
的 callback 函数继续后续的处理,包括启动一个音视频传输流,启动响铃等。也就是说这里的 callback
完成了 media 媒体层的处理以及 linphone 上层的处理。
回调函数被保存在全局变量linphone_sal_callbacks 中,在linphone 初始化时调用sal_set_callbacks
设置到 sal 的 callback 上去的。
2) Genera_stat 模块:
主要提供 linphone 全局状态的修改与设置的接口
3) Address 模块:
调用 sal 提供的接口,进行与地址相关的处理,这里的地址主要是 uri 相关的处理。包括获取地
址以及地址中的部分信息或者设置这些信息。在上层地址是一个字符串指针,但是在内部处理时都
会强制转换为 osip_from 结构体来处理。实际上就是对 linphone_address 结构体的处理。
4) Authorization 模块:
处理认证信息。各个认证用户的信息都被保存到 linphone_auto 结构体中兵串接在 linphone_core
结构体上。这里的接口就是处理这些数据结构,提供设置和获取相关信息的接口。
5) Chat 模块:
提供创建和销毁 chat room,向 chat room 发送消息和从 chat room 接收消息的接口,以及设置和
获取用户数据的接口。类似于 authorization 模块,所有的 chat room 信息也是保存在 linphone_chat 结
构体中并串接在 linphone_core 结构体上的。
6) Friends 模块:
提供处理 friends 相关信息的接口。所有的 friends 信息保存在 linphone_friend 结构体中并被串接
在 linphone_core 结构体上,这样操作起来,包括设置,获取,添加以及移除都很方便。
7) Configure 模块:
提供配置文件处理的相关接口,包括配置文件的解析,配置文件中信息的获取,写入,同步等。
配置文件解析后便于程序处理的信息主要都保存在 lpconfig 结构体中,这与文本文件中便于编写和
阅读的配置文件本身不同。
配置文件中的各个配置模块本身也按照 section 的方式进行了划分,各个 section 也都是挂接在
lp_config 的 section 链表上的。这个模块可以单独提取出来进行测试。
8) Offer_answer 模块:
管理基于 sdp 的媒体协商。根据本地的支持能力和远端支持的能力,根据就低的原则,获得双方都
可以支持的媒体信息。比如编解码格式等。
9) Presence 模块:
提供与在线状态相关的处理。
10) Proxy 模块:
处理代理相关的处理。代理相关的信息保存在 linphone_proxy 结构体上,但是该结构体只是代
表了当前 linphone_core 使用的 proxy。代理可能不止一个,所有的代理其信息都被串接在链表上,
并被挂接在 sip_conf 的 proxies 上。添加一个代理,取得一个代理以及其他相关的操作接口也都在该
模块中提供处理接口。
11) Sal 模块:
Sal 模块其实应该是最重要的,最核心的模块了。该模块对 exosip 进行了简单的封装,间接的
对 osip 模块进行了封装,使用该模块的接口可以完成 sip 协议的处理以及媒体描述的处理。
Sal.c 文件主要是对一些 sal 相关的结构体的操作,包括 SalMediaDescription 和 sal_op。处理包括
创建这些结构体的实例,获取或者设置其中的一些操作域。
Sal_exosip2_sdp.c 基于 osip 库提供的 sdp 相关操作的接口,在 sal 层实现将其与 sal 相关的结构
体关联起来操作。比如根据 SalMediaDescription 结构体信息将其转换为 sdp 结构体,或者反之。
Sal_exosip2_presence.c 包括了对 in 和 out 的 subscribe 的操作。Text 数据的发送(基于 osip 和
exosip)。
Sal_exosip2.c sip 这块比较重要的封装。包含了对 sal_op 结构体的创建和基本操作。对 exosip 重
要结构体的封装,包括初始化和释放。包含了对 sal 结构体的创建和基本操作的封装,更重要的是
包含了对 sal 和 sal_op,sal_media_desc,sal_stream_desc 这些上层结构体与底层 osip_message,
sip_message,sdp_message 等数据结构之间数据的转换和共享,以及对底层相关接口的调用。这种
调用主要包括跟据上层结构体中包含的信息设置底层结构体,并调用底层接口完成具体功能,以及
根据底层结构体得到的数据设置上层结构体的相关信息。
一个基本的描述就是:sal 作为 signal abstract layer 包含了上层所主要理解的交互信息,这些信
息对于理解电话操作而言已经足够了,在底层,选择了 osip 和 exosip 来支持这项操作。所以实际上
来说,可以用其他支持 sip 的库的接口来替代现有的,保留 sal 层接口的功能定义。在 linphone 中,
虽然大部分使用了 sal 层的封装来完成 sip 交互过程,但是也调用 osip 和 exosip 库本身的其他接口,
所以这层封装主要还是再次简化协议层的处理,使得功能更具体,而不是更单一。
几个关键数据结构之间的关系:
Sal 一个基本的结构体,通过这个结构体可以搜寻到上层所需的所有 sip 协议相关的信息。具体
的 call,register 等信息保存在 sal_op 这个结构体中,多个实体通过链表串接起来,挂在 sal 上。Sal_op
包含了 sal_op_base 结构体,这个结构体保存了一些通用的不变的信息,对多个实体而言,比如路由
信息,本地媒体信息,远端媒体信息等。其 root 指针由返回指向了 sal 这个基础,所以通过 sal_op
可以找到 sal。另外,在媒体信息中包含了所有流的信息,所有这些类似一个树的组织结构,sal 类
似树根,通过它可以找到所有的枝叶及其上的信息。这些数据结构之间的关系如下图:
12) Core 模块:
上层 API 及其封装实现。通过这些 API 接口,可以快速构建基于 sip 的可视电话系统。
2 底层模块说明
1) Mediastream 实现的说明:
从代码上来看 mediastreamer 库,它的构成非常结构化。在 mediastream2 中实现了大量的 filter,
包括声卡视频卡的 filter,编解码器的 filter,RTP 传输与接收的 filter 等等。每个.c 文件实现一个 filter,
而且每个 filter 的实现也是非常结构化的。首先需要定义一个 filter 结构体实例,对于实例的各个部
分进行赋值,主要是包括定义 filter 私有数据,filter 的 methods,init,preprocess,process,postprocess
和 uninit 这几个函数的实现等。而关键的一些实现,比如编解码器的处理,是基于 ffmpeg 库提供的
接口来完成的,而声卡视频卡摄像头数据的捕获由其他库实现,但是也是基于标准的驱动接口来完
成的。对于 RTP 的接收和传输则是基于 ortp 库来完成的。
另外提供了两个文件,audiostream 和 vediostream,用来处理音视频流的 filter 连接,linphonecore
主要就是调用这几个文件提供的接口来完成媒体流的启动的。
其他的就是辅助数据结构,包括 filter 的定义注册,queue 队列的操作,声卡和摄像头的 handler
的操作,ticker 的处理等。
考虑到设备描述符,实际上 mediastream 可以分为两部分来看,一部分就是之前主要的有关大量
filter 的操作定义部分,一部分就是后来的有关声卡设备和摄像头设备的设备描述符的定义和操作。
关于摄像头部分,视频数据的捕获是通过创建一个新的任务来进行的,该任务源源不断的从设备上
读取数据,当然除此之外也提供了配置设备的参数的接口,比如图形的大小格式等。而且对于 video
模块,设备处理和读取数据是与 V4lState 这个结构体挂钩的,这个结构体实例又挂到 filter 的私有数
据上,这样数据读取任务不断的从驱动读取数据,放到该结构体实例上,filter 处理时从自己的私有
数据结构指向的 queue 中取数据自然的就取到了摄像头捕获的数据,实际上这里包含了设备的操作
(基于设备描述符)和 filter 的操作(基于 filter 的描述符)以及二者的结合。
在 filter 中 webcam 设备可能不止一个,有关这些设备的操作辅助接口就在 webcam.c 中实现。
之前说了摄像头部分,对于声卡部分也是极其类似的,管理辅助函数在 mssndcard 文件中实现,
另外还配以几个声卡设备本身的描述符文件来描述,其与 filter 的关系类同于视频部分。还有就是这
些设备的描述符结构体汇总在 mscommon 中,而 filter 的汇总在 alldescs.h 头文件中。
2) ortp
关于 ortp 模块的说明参见参考资料 1
四 linphone 中数据结构说明
程序中定义了一个比较大的数据结构体——linphonecore,将其作为总的控制结构。通过该结构
体,可以找到所需的大部分信息。这些信息要么是直接在该结构体中定义,要么是在其包含的模块
相关的子结构体中。可以想象,内存中保存的该结构体,就像整体软件信息的总控点,通过该总控
点,可以直接或者间接的得到系统相关的信息,这在一定程度上可以简化系统的架构和实现。(在许
多开源软件中都可以看到这种代码架构方式。)下图展示了 linphonecore 结构体:
指针 sal 指向 sal 数据结构图,通过该指针,就可以找到 sal 模块用到的所有数据结构体。
Config 部分指向 linphone 的配置信息,包括网络,sip 协议,rtp 传输,音视频参数以及编解码器
信息。
call 指针挂载了所有的通话,每一路通话都由 linphonecall 结构体表示。
Audiostream 和 videostream 保存了媒体信息,a_rtp 和 a_rtcp 保存了 RTP 相关的信息。
五 linphone 的初始化过程
首 先 初 始 化 全 局 的 状 态 : power 为 GSTATE_POWER_OFF , 即 关 闭 状 态 ; reg 为
GSTATE_REG_NONE,没有注册;call 为 GSTATE_CALL_IDLE,空闲状态。
设置新的 power 状态为 startup。
1) Ortp 库初始化,调用 ortp_init
在 ortp_init 中主要创建了 payload type 链表,所有支持的 payload type 都被创建在一起了,这里注
册的 payloadtype 就是底层 RTP 传输所能够支持的。
添加 payload type,先为音频,后为视频。
2) 调用 ms_init 初始化 mediastream 库,该库封装了媒体处理接口,使得多媒体数据的处理变得简
单。
Ms_init 中形成了三个全局链表,一个为 filter 描述符链表,所有 media streamer 支持的 filter 的描
述符在这里被串接在一起了,包括编解码,音视频读写以及 RTP 发送和接收处理相关的 filter。第二
个为音频卡描述符链表,所有支持的音频卡相关的描述符也被串接在一起。第三个就是视频卡描述
符链表,处理类似音频卡描述符。
3) 调用 sal_init 进行 sip 协议栈的初始化。该过程将返回一个 sal 结构体。
Exosip 全局结构体的创建以及初始化。
需要注意,在这里相当于有三层封装调用:一层为 sal 层的封装调用,一层为 exosip 层的封装调
用,最底层为 osip 层的基本调用。
另外需要注意的是在这里没有创建 exosip 任务,而是在后面的读取并配置 sip 配置信息时才创建
exosip 任务,并监听特定端口。
将 lc->sal 的 up 指针指回 linphone core 全局结构体
设置 sal 上的回调函数,这些回调函数在对应的 sip 协议处理完后用于调用来处理外层有关 call
与 media 流的一些处理。
如果配置文件中没有设置 sip 会话的过期时间,则在这时将其设置为 200
将所有 sip setup 配置串联到 registered_sip_setups 全局链表上
4) 读取配置文件中有关音频的设置并对声卡进行设置
根据配置文件中描述的声卡设备 id 将声卡设备配置到 linphone core 结构体的 sound 配置结构体
对应声卡的描述项上。
设置用于响铃的音频文件的路径。该文件路径会被保存到 sound configure 结构体的 local_ring 项
目上。
类似上面,设置用于提示远端响铃的音频文件的路径,最终保存到 sound configure 结构体的
remote ring 项目上。
检查系统中的声卡设备
配置是否使能回声消除
配置回声限制
配置增益
配置回放增益
5) 读取配置文件中有关网络部分的配置并对网络参量进行配置
读取配置文件中有关带宽的设置,并对 linphone 中音视频使用的带宽进行配置。首先保存带宽值
到 net config 结构体中,音频带宽配置为配置文件中读出的值与默认值的小者,视频带宽配置为读出
值减去音频值减去 10 和 0 之间的较大者。这里 10 相当于是一个缓冲。
设置 stun server
设置 nat 防火墙地址
配置是否使用防火墙策略
配置是否只对 SDP 进行 nat 转换