/*
*本文基于LIVE555的嵌入式的RTSP流媒体服务器一个设计文档,个中细节现剖于此,有需者可参考指正,同时也方便后期自己查阅。(本版本是基于2011年的live555)
作者:llf_17@qq.com
*/
RTSP
SERVER(基于live555)详细设计
这个server
的最终情况如下:
性能:D1
数据时:
1.
8
路全开udp
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1175root 20 0 65712 23m 3112 R 29.0 34.5 285:13.05 dvrapp_SN6108
2.
8
路全开tcp
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1175root 20 0 65612 23m 3112 R 31.0 34.4 289:03.86 dvrapp_SN6108
文件:
静态库大小:Live555.a 1,585KB
文件个数: 150
左右
目录
1.代码移植... 3
1.1代码获取... 3
1.2文件初步裁剪... 3
1.3修改Makefile. 3
1.4将live555生成的静态库链接到我司的可执行程序中... 3
2.功能添加... 4
2.1 数据的输入... 4
2.2实时视频(H264)的输入和输出... 5
2.3实时音频(G711.a)的输入和输出... 6
2.4
H264视频离散nal单元输入的实现... 6
2.5单播的实现... 7
2.6多路连接的实现... 7
2.7最大连接数的限制... 9
2.8服务端主动断开连接的实现... 9
2.9
sdp信息的添加修改... 10
2.10重定向的实现... 10
3. 代码裁剪... 11
4. 效率优化... 12
4.1Ring
buffer双队列的修改... 12
4.2内存拷贝的去除及socketwritev 的实现... 13
5. 部分函数说明:... 16
6. 部分实验结果... 17
1.代码移植
将live555
代码移植到我司嵌入式平台上。
1.1代码获取
http://www.live555.com/liveMedia/public/
本次移植使用的版本是2011.12.23.
1.2文件初步裁剪
Live555
为跨平台库,本移移植旨在arm linux
上运行,所以需先裁剪掉其它无关文件。
a.
删除之前先生成用于linux
的makefile.
进入live
文件夹,
运行./genMakefiles
linux.
此时生成了用于linux
的Makefile.
b.
删除冗余文件和文件夹。
每个文件夹下只保留*.cpp,*.hh, *.c, *.h, Makefile.
其余全部删除。
删除文件夹:WindowsAudioInputDevice
和mediaServer
1.3修改Makefile
Makefile
变量主要做以下修改:
C_COMPILER = arm-hismall-linux-gcc
CPLUSPLUS_COMPILER = arm-hismall-linux-g++
LINK = arm-hismall-linux-g++-o
LINK_OPTS = -L.-lpthread
C_FLAGS = $(COMPILE_OPTS) $(CFLAGSARM)
CPLUSPLUS_FLAGS = $(COMPILE_OPTS)$(CFLAGSARM)
1.4将live555生成的静态库链接到我司的可执行程序中
如:dvrapp_sn6108
裁剪到此时的Live555
编译时会生成所有的库,不可能将所有库链进可执行程序中。我司只用到了视频:H264
,音频 G711.a.
其余不用。故只需将这几个有关库链接进即可。
我司链接静态库到可执行程序的做法是,先成一个xx.a,
然后在生成可执行程序时,链接所有.a
文件。所以我们这里只需将需要的.o
文件装进live555.a
即可。
具体做法:在live555
文件夹下新建文件夹liveLib
用于存放其它文件生成的.a.
此文件夹用于生成live555.a
最终库。(将所有.a
先打散成.o
,再合成一个live555.a
).
每个文件夹下在生成.a
时都拷贝一份.a
到liveLib
中。最后将生成的live555.a
拷贝到masterLIBARMSN6108
中。可执行程序链接时在此文件夹下可找到live555.a
。
2.功能添加
Live555
源码的功能要用到我司具体项目中还需做一定的修改,不是拿来就能用的。
原本对于开源项目,尤其是c++
项目,最好不要改动原有的类,所以修改应该是继承父类,在子类中修改。这样有利于代码的升级,和维护。但是由于考虑到c++
的继承的层数以及虚函数的运行时绑定对性能的影响,以及编译出文件的大小,所以本修改中只对一部分类作了继承,另一些直接在原有类中添加新方法。
2.1 数据的输入
数据从ringbuffer
到server
模块的传输,使用了通知机制,即当ringbuf
有数据时,数据发出通道,告知某一通道有数据可用,则server
在需要的时候会来这个通道来取数据。
数据输入框图
通知机制主要由以下函数实现。
void
signalNewFrameData(int
mediaType, int chanel,int trans_mode,int buf_len)
signalNewFrameData为一个全局函数,可被外部线程调用(注意,整个live555
是一个单线程程序)。signalnewFrameData
会调用virtual
void triggerEvent(EventTriggerId eventTriggerId, void*clientData = NULL),
这个函数共两个参数,一个是触发的事件id,
另一个是此id
对应的事件处理函数所在的类实例指针,这里具体是各个输入的videoSource
和audioSource
类实例指针。
数据的写入ringbuffer
由以下函数实现:
在write_unicast_data_live里(
只用于单播)
,每当一个数据到来后,先判断是音频还是视频,然后再装入各自对应的ringbuffer
,接着调用 signalnewFrameData
通知相应的server.
通知时刻落在server
刚好需要数据的时刻区间间的概率较小,大部分情况是(
经实验证明了的)
:server
正在处理其它数据;
或已经取完数据,正在等下次取数据时刻的到来(
此时可能正停留在Eventloop
里的sigleStep
的select
中)
。所以通知后都会把事件记入一个bitmask
类型的变量fTriggersAwaitingHandling(
最多可累计挂入32
个待处理事件)
中,
然后在select
结束后,再处理每个TriggerNum
所对应的事件(调用Source
中的deliverFrame
将数据向后传送)。处理完一个事件,则将fTriggersAwaitingHandling
中对应的bitmask
位清0,singleStep
每一次循环中TriggerEvent
只处理一个事件(
如有未处理完事件,等下一循环再处理)
。
2.2实时视频(H264)的输入和输出
Live555
提供的示例里面有直接读文件的类和使用方法,但没有实时输入的类及其实现。
Live555
中数据流基本路线是:
Source
àFilter1
àFilter2…
àSink
Filter
可能有多个,也可能一个也没有。对于h264
,filter
有两个,对于音频g711.a
,实现中则没有Filter.
本设计具体实现视频实时输入方法如下:
1.视频输入
在live555
中,输入为Source
类,输出为 Sink
类。中间处理环节类称为Filter.
SNDeviceSource
类继承于FramedSource,
用于实时输入h264
视频。该类的实现参考了DeviceSource
。
在SNDeviceSource::deliverFrame()
中实现数据的输入。Memmove