1. 为什么有 RTSP?
这要从 RTP 说起。RTP 是实时传输协议。具体请参考 RFC3550。RTP 在实际应用中,是被动推送的方式。
即如下图的流程。
则将会产生如下对话:(M: Media Server; C: Client)
C:谁给我发视频呢?
M:我!
C:谁让你给我发视频的?
M:主人。
C:我现在又不用,你发给我干嘛?
M:主人让我发的,我也不知道为啥要发。
C:……
M:我只能持续的发,发啊发…..
C:发你妹~!我死了。
M:那我还是要发,我又不知道你死了,发啊发……
C,M:主人傻×。
RTP 的被动发送方式,使得不管客户端是否准备好接收视频流,是否异常死掉,媒体服务都会不断往外发送视频流。这种处理方式是不符合实际业务逻辑的。正常的业务逻辑应该是,客户端要视频,媒体服务才发送视频;客户端不要视频了,媒体服务停止发送视频;客户端异常死掉了,媒体服务应该能检测到,并停止发送视频。
所以,实际解决方式有两种:
I. 发送到组播组,由网络设备控制。即,媒体服务发送到一个组播地址,客户端加入到组播组后,由网络设备发送视频。当客户端退出组播组,网络设备停止发送给客户端。所有的控制均由组播协议IGMP 相关进行控制。
II. 通过 RTSP 协议。其实 RTSP 协议就是一个媒体服务的控制协议。请参考RFC2326。请翻译下面一段话。
The Real Time Streaming Protocol, or RTSP, is an application-level protocol
for control over the delivery of data with real-time properties.
2. 下载 Live555 源码及 VLC1.1.9 版本
下载 Live555 源码,并解压。
下载slamtv60.264,并放置到 live 源码下的 mediaServer 目录。
下载VLC1.1.9版本
不编译 testProg 及 proxyServer 目录,没用也耗时。修改 Makefile.tail 文件,注释加'#'号
vim Makefile.tail
all:
cd $(LIVEMEDIA_DIR) ; $(MAKE)
cd $(GROUPSOCK_DIR) ; $(MAKE)
cd $(USAGE_ENVIRONMENT_DIR) ; $(MAKE)
cd $(BASIC_USAGE_ENVIRONMENT_DIR) ; $(MAKE)
#cd $(TESTPROGS_DIR) ; $(MAKE)
cd $(MEDIA_SERVER_DIR) ; $(MAKE)
#cd $(PROXY_SERVER_DIR) ; $(MAKE)
install:
cd $(LIVEMEDIA_DIR) ; $(MAKE) install
cd $(GROUPSOCK_DIR) ; $(MAKE) install
cd $(USAGE_ENVIRONMENT_DIR) ; $(MAKE) install
cd $(BASIC_USAGE_ENVIRONMENT_DIR) ; $(MAKE) install
#cd $(TESTPROGS_DIR) ; $(MAKE) install
cd $(MEDIA_SERVER_DIR) ; $(MAKE) install
#cd $(PROXY_SERVER_DIR) ; $(MAKE) install
clean:
cd $(LIVEMEDIA_DIR) ; $(MAKE) clean
cd $(GROUPSOCK_DIR) ; $(MAKE) clean
cd $(USAGE_ENVIRONMENT_DIR) ; $(MAKE) clean
cd $(BASIC_USAGE_ENVIRONMENT_DIR) ; $(MAKE) clean
#cd $(TESTPROGS_DIR) ; $(MAKE) clean
cd $(MEDIA_SERVER_DIR) ; $(MAKE) clean
#cd $(PROXY_SERVER_DIR) ; $(MAKE) clean
vim config.linux
C_COMPILER = gcc
CPLUSPLUS_COMPILER = g++
LINK = g++ -o
生成 Makefile,执行命令
./genMakefile linux
编译程序,执行命令
make
经过一长串文字之后,会在 mediaServer 目录下,生成 live555MediaServer。若没生成,请从头执行。
4. 打开 Live555 的调试信息
修改 UsageEnvironment/include 目录下的 UsageEnvironment.hh 文件,加入宏定义
vim UsageEnvironment/include/UsageEnvironment.hh
#ifndef _USAGE_ENVIRONMENT_HH
#define _USAGE_ENVIRONMENT_HH
#define DEBUG 1 // added by jeremy
#ifndef _USAGEENVIRONMENT_VERSION_HH
重新编译,执行命令
make
这样 Live555 里面很多 fprintf(stderr, ...)的信息,就能显示了。
5. 启动 Live555
执行命令
cd mediaServer
./live555MediaServer > log.txt 2>&1
重启一个命令行窗口,cd 到 live 源码目录下的 mediaServer 目录,执行命令
cat log.txt
查看 rtsp 地址及端口
解释命令
> log.txt 是将打印信息保存到 log.txt 文件中。
2>&1 是将 stderr 输出到 stdout
从图可以看到 rtsp 访问地址为 rtsp://192.168.1.198:8554/slamtv60.264。
解释
where <filename> is a file present in the current directory. 请自己翻译。
8554 是因为没用 sudo 用户,无法绑定到 554 端口。
最新开的命令行窗口,执行命令
tail -f log.txt
此命令将会不断刷新 log.txt 的内容到屏幕上。
6. 启动客户端
宿主机 windoz 上,开始->运行->cmd
执行命令
cd vlc-1.1.9
vlc -vvv --extraintf=logger rtsp://192.168.1.198:8554/slamtv60.264
最后一条命令,是用命令行开启 vlc,并在后台开启调试窗口,同时打开 URL。
如果操作无问题,那么这时应该播放视频了。请播放 30 秒后,按 VLC 的停止播放按钮。
7. 获取日志
Live555 日志都保存到了虚拟机 log.txt 中
VLC 的日志都在后台,通过拖选,右键,复制到剪贴板后,新建个文本文件,进行保存。
8. 分析 Live555 的日志
从 Live555 的 log.txt 日志,我们可以看到整个的处理流程。
accept 连接并显示内容
accept()ed connection from 192.168.1.98
RTSPClientConnection[0x154eca0]::handleRequestBytes() read 132 new bytes:OPTIONS
rtsp://192.168.1.198:8554/slamtv60.264 RTSP/1.0
CSeq: 2
User-Agent: LibVLC/1.1.9 (LIVE555 Streaming Media v2011.01.06)
可以看到地址是 192.168.1.98,使用的库是 LibVLC/1.1.9,它使用的 Live555的库是 2011.01.06 版本。
解析获取到 Option 请求,url 是 slamtv60.264
parseRTSPRequestString() succeeded, returning cmdName "OPTIONS", urlPreSuffix "",
urlSuffix "slamtv60.264", CSeq "2", Content-Length 0, with 0 bytes following the
message.
send 回应信息
sending response: RTSP/1.0 200 OK
CSeq: 2
Date: Fri, Sep 27 2013 03:44:08 GMT
Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER,
SET_PARAMETER
Live555 支持的请求有 OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE,
GET_PARAMETER, SET_PARAMETER
接收到请求
RTSPClientConnection[0x154eca0]::handleRequestBytes() read 158 new bytes:DESCRIBE
rtsp://192.168.1.198:8554/slamtv60.264 RTSP/1.0
CSeq: 3
User-Agent: LibVLC/1.1.9 (LIVE555 Streaming Media v2011.01.06)
Accept: application/sdp
解析为 Describe,并解析了 url 对应的文件的信息
parseRTSPRequestString() succeeded, returning cmdName "DESCRIBE", urlPreSuffix
"", urlSuffix "slamtv60.264", CSeq "3", Content-Length 0, with 0 bytes following
the message.
H264VideoStreamParser::parse() EXCEPTION (This is normal behavior - *not* an
error)
Parsed 22-byte NAL-unit (nal_ref_idc: 3, nal_unit_type: 7 ("Sequence parameter
set"))
profile_idc: 77
constraint_setN_flag: 64
level_idc: 51
seq_parameter_set_id: 0
log2_max_frame_num_minus4: 3
pic_order_cnt_type: 0
log2_max_pic_order_cnt_lsb_minus4: 4
max_num_ref_frames: 1
gaps_in_frame_num_value_allowed_flag: 0
pic_width_in_mbs_minus1: 23
pic_height_in_map_units_minus1: 17
frame_mbs_only_flag: 1
frame_cropping_flag: 0
vui_parameters_present_flag: 1
BEGIN vui_parameters
aspect_ratio_info_present_flag: 0
overscan_info_present_flag: 0
video_signal_type_present_flag: 0
chroma_loc_info_present_flag: 0
timing_info_present_flag: 1
num_units_in_tick: 2
time_scale: 102
fixed_frame_rate_flag: 1
Set frame rate to 25.500000 fps
Presentation time: 1380253448.318998
22 bytes @1380253448.318998, fDurationInMicroseconds: 0 ((0*1000000)/25.500000)
Parsed 4-byte NAL-unit (nal_ref_idc: 3, nal_unit_type: 8 ("Picture parameter
set"))
Presentation time: 1380253448.318998
4 bytes @1380253448.318998, fDurationInMicroseconds: 0 ((0*1000000)/25.500000)
Parsed 3017-byte NAL-unit (nal_ref_idc: 2, nal_unit_type: 1 ("Coded slice of a
non-IDR picture"))
Presentation time: 1380253448.318998
*****This NAL unit ends the current access unit*****
3017 bytes @1380253448.318998, fDurationInMicroseconds: 39215
((1*1000000)/25.500000)
Parsed 3081-byte NAL-unit (nal_ref_idc: 2, nal_unit_type: 1 ("Coded slice of a
non-IDR picture"))
Presentation time: 1380253448.358213
*****This NAL unit ends the current access unit*****
3081 bytes @1380253448.358213, fDurationInMicroseconds: 39215
((1*1000000)/25.500000)
Parsed 2836-byte NAL-unit (nal_ref_idc: 2, nal_unit_type: 1 ("Coded slice of a
non-IDR picture"))
Presentation time: 1380253448.397428
*****This NAL unit ends the current access unit*****
2836 bytes @1380253448.397428, fDurationInMicroseconds: 39215
((1*1000000)/25.500000)
回应 Describe 请求
sending response: RTSP/1.0 200 OK
CSeq: 3
Date: Fri, Sep 27 2013 03:44:08 GMT
Content-Base: rtsp://192.168.1.198:8554/slamtv60.264/
Content-Type: application/sdp
Content-Length: 525
v=0
o=- 1380253448318916 1 IN IP4 192.168.1.198
s=H.264 Video, streamed by the LIVE555 Media Server
i=slamtv60.264
t=0 0
a=tool:LIVE555 Streaming Media v2013.09.18
a=type:broadcast
a=control:*
a=range:npt=0-a=x-qt-text-nam:H.264 Video, streamed by the LIVE555 Media Server
a=x-qt-text-inf:slamtv60.264
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:500
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;profile-level-id=4D4033;sprop-parametersets=Z01AM5JUDAS0IAAAAwBAAAAM0eMGVA==,aO48gA==
a=control:track1
application/sdp 说明下面从 v=0 开始,是 264 文件的 sdp 信息。
可以看到客户端接收端口是 61480-61481
接收到请求
RTSPClientConnection[0x154eca0]::handleRequestBytes() read 189 new bytes:SETUP
rtsp://192.168.1.198:8554/slamtv60.264/track1 RTSP/1.0
CSeq: 4
User-Agent: LibVLC/1.1.9 (LIVE555 Streaming Media v2011.01.06)
Transport: RTP/AVP;unicast;client_port=61480-61481
解析请求并回应
parseRTSPRequestString() succeeded, returning cmdName "SETUP", urlPreSuffix
"slamtv60.264", urlSuffix "track1", CSeq "4", Content-Length 0, with 0 bytes
following the message.
sending response: RTSP/1.0 200 OK
CSeq: 4
Date: Fri, Sep 27 2013 03:44:08 GMT
Transport:
RTP/AVP;unicast;destination=192.168.1.98;source=192.168.1.198;client_port=61480-61481;server_port=6970-6971
Session: BB58AEA2
协议 RTP/AVP,目的地 192.168.1.98,源 192.168.1.198,客户端端口 61480-61481,服务端
端口 6970-6971,session 号 BB58AEA2
接收到请求
RTSPClientConnection[0x154eca0]::handleRequestBytes() read 168 new bytes:PLAY
rtsp://192.168.1.198:8554/slamtv60.264/ RTSP/1.0
CSeq: 5
User-Agent: LibVLC/1.1.9 (LIVE555 Streaming Media v2011.01.06)
Session: BB58AEA2
Range: npt=0.000-
Range 是从 0 秒开始到最后。对应 sdp 里面的 a=range:npt=0-这一行。
解析请求,并回应
parseRTSPRequestString() succeeded, returning cmdName "PLAY", urlPreSuffix
"slamtv60.264", urlSuffix "", CSeq "5", Content-Length 0, with 0 bytes following
the message.
RTCPInstance[0x15795c0]::RTCPInstance()
schedule(1.257133->1380253449.694797)
sending REPORT
sending RTCP packet
80c80006 a05b17ca d5ef7d88 700b7803 66a12610 00000000 00000000 81ca0004 a05b17ca
01064e69 755a6169 00000000
H264VideoStreamParser::parse() EXCEPTION (This is normal behavior - *not* an
error)
sending response: RTSP/1.0 200 OK
CSeq: 5
Date: Fri, Sep 27 2013 03:44:08 GMT
Range: npt=0.000-Session: BB58AEA2
RTP-Info:
url=rtsp://192.168.1.198:8554/slamtv60.264/track1;seq=41987;rtptime=1721837108
其中还启用了 RTCPInstance,进行 RTCP 监听,发送 RTCP 的 packet,内容就是从 80c80006 的一串数字。
再次解析文件
Parsed 22-byte NAL-unit (nal_ref_idc: 3, nal_unit_type: 7 ("Sequence parameter
set"))
profile_idc: 77
constraint_setN_flag: 64
level_idc: 51
seq_parameter_set_id: 0
log2_max_frame_num_minus4: 3
pic_order_cnt_type: 0
log2_max_pic_order_cnt_lsb_minus4: 4
max_num_ref_frames: 1
gaps_in_frame_num_value_allowed_flag: 0
pic_width_in_mbs_minus1: 23
pic_height_in_map_units_minus1: 17
frame_mbs_only_flag: 1
frame_cropping_flag: 0
vui_parameters_present_flag: 1
BEGIN vui_parameters
aspect_ratio_info_present_flag: 0
overscan_info_present_flag: 0
video_signal_type_present_flag: 0
chroma_loc_info_present_flag: 0
timing_info_present_flag: 1
num_units_in_tick: 2
time_scale: 102
fixed_frame_rate_flag: 1
Set frame rate to 25.500000 fps
Presentation time: 1380253448.432657
22 bytes @1380253448.432657, fDurationInMicroseconds: 0 ((0*1000000)/25.500000)
Parsed 4-byte NAL-unit (nal_ref_idc: 3, nal_unit_type: 8 ("Picture parameter
set"))
Presentation time: 1380253448.432657
4 bytes @1380253448.432657, fDurationInMicroseconds: 0 ((0*1000000)/25.500000)
Parsed 3017-byte NAL-unit (nal_ref_idc: 2, nal_unit_type: 1 ("Coded slice of a
non-IDR picture"))
Presentation time: 1380253448.432657
*****This NAL unit ends the current access unit*****
3017 bytes @1380253448.432657, fDurationInMicroseconds: 39215
((1*1000000)/25.500000)
后台,已经开始发送视频了,没有相关的日志打印。
接收到请求
RTSPClientConnection[0x154eca0]::handleRequestBytes() read 158 new
bytes:GET_PARAMETER rtsp://192.168.1.198:8554/slamtv60.264/ RTSP/1.0
CSeq: 6
User-Agent: LibVLC/1.1.9 (LIVE555 Streaming Media v2011.01.06)
Session: BB58AEA2
请求为 GET_PARAMETER
解析并回应请求
parseRTSPRequestString() succeeded, returning cmdName "GET_PARAMETER",
urlPreSuffix "slamtv60.264", urlSuffix "", CSeq "6", Content-Length 0, with 0
bytes following the message.
sending response: RTSP/1.0 200 OK
CSeq: 6
Date: Fri, Sep 27 2013 03:44:08 GMT
Session: BB58AEA2
Content-Length: 10
2013.09.18
后面都是解析 264 文件,类似下面的日志信息。有兴趣的可以对着代码深入研究。
Parsed 3081-byte NAL-unit (nal_ref_idc: 2, nal_unit_type: 1 ("Coded slice of a
non-IDR picture"))
Presentation time: 1380253448.471872
*****This NAL unit ends the current access unit*****
3081 bytes @1380253448.471872, fDurationInMicroseconds: 39215
((1*1000000)/25.500000)
收到保活信息并确认
[0x15795c0]saw incoming RTCP packet (from address 192.168.1.98, port 61481)
81c90007 8000353d a05b17ca 00ffffff 0001a452 00000069 7d88700b 000104b6 81ca0004
8000353d 01094a65 72656d79 2d504300
RR
RTSP client session (id "BB58AEA2", stream name "slamtv60.264"): Liveness
indication
validated RTCP subpacket (type 2): 1, 201, 0, 0x8000353d
UNSUPPORTED TYPE(0xca)
validated RTCP subpacket (type 2): 1, 202, 12, 0x8000353d
validated entire RTCP packet
请留意 incomming 的 RTCP 包的地址及端口。RTCP 并不是从 554 发送的。
Liveness indication,保活指示
validated entire RTCP packet,确认整个 RTCP 包
发送保活信息
sending REPORT
sending RTCP packet
80c80006 a05b17ca d5ef7d8b 24804103 66a4dee9 000000e6 00041352 81ca0004 a05b17ca
01064e69 755a6169 00000000
schedule(1.528875->1380253452.671793)
停止播放时接收到的请求
RTSPClientConnection[0x154eca0]::handleRequestBytes() read 153 new bytes:TEARDOWN
rtsp://192.168.1.198:8554/slamtv60.264/ RTSP/1.0
CSeq: 7
User-Agent: LibVLC/1.1.9 (LIVE555 Streaming Media v2011.01.06)
Session: BB58AEA2
解析并回应请求
parseRTSPRequestString() succeeded, returning cmdName "TEARDOWN", urlPreSuffix
"slamtv60.264", urlSuffix "", CSeq "7", Content-Length 0, with 0 bytes following
the message.
RTCPInstance[0x15795c0]::~RTCPInstance()
sending BYE
sending RTCP packet
80c80006 a05b17ca d5ef7da8 b7925bb8 66cd7c32 000009c1 002d961c 81cb0001 a05b17ca
sending response: RTSP/1.0 200 OK
CSeq: 7
Date: Fri, Sep 27 2013 03:44:40 GMT
RTCP 发送 BYE 信息。
关掉连接
RTSPClientConnection[0x154eca0]::handleRequestBytes() read -1 new bytes (of
10000); terminating connection!
9. 分析 VLC1.1.9 的日志
上面的 OPTIONS、DISCRIBE SETUP PLAY TEARDOWN 命令的请求和发送,均能在 VLC 的日志里面
找到对应。举个栗子:
Sending request: OPTIONS rtsp://192.168.1.198:8554/slamtv60.264 RTSP/1.0
CSeq: 2
User-Agent: LibVLC/1.1.9 (LIVE555 Streaming Media v2011.01.06)
Received 152 new bytes of response data.
Received a complete OPTIONS response:
RTSP/1.0 200 OK
CSeq: 2
Date: Fri, Sep 27 2013 03:44:08 GMT
Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARA
METER
[031da168] live555 demux debug: RTP subsession 'video/H264'
Sending request: SETUP rtsp://192.168.1.198:8554/slamtv60.264/track1 RTSP/1.0
CSeq: 4
User-Agent: LibVLC/1.1.9 (LIVE555 Streaming Media v2011.01.06)
Transport: RTP/AVP;unicast;client_port=61480-61481
Received 204 new bytes of response data.
Received a complete SETUP response:
RTSP/1.0 200 OK
CSeq: 4
Date: Fri, Sep 27 2013 03:44:08 GMT
Transport: RTP/AVP;unicast;destination=192.168.1.98;source=192.168.1.198;client_
port=61480-61481;server_port=6970-6971
Session: BB58AEA2
超时时间
[031da168] live555 demux debug: We have a timeout of 60 seconds
60 秒超时?没有仔细研究。看日志或许是这样。
使用 rtcp 保活
[031da168] live555 demux debug: tk->rtpSource->hasBeenSynchronizedUsingRTCP()
所有 rtcp 相关的操作,没有体现在日志中,所以无法看到发送 rr 包。
10. 关于 RTSP 的保活
RTSP 的保活有两种方式:
发送 RTCP 包,如上所述。详情参考 RTCP
RFC3605(http://tools.ietf.org/html/rfc3605)
发送 PLAY 和 GET_PARAMETER 请求
在 RTSP 的 RFC 里面有这样两段话,说的很清楚,请自己翻译。
If a stream is playing, such a PLAY request causes no further action and
can be used by the client to test server liveness.
GET_PARAMETER with no entity body may be used to test client or server
liveness ("ping")
11. 把流程图形化
![](http://userimage6.360doc.com/15/0806/15/7256015_201508061518050069.png)
12. 写在最后
关于保活,还可以做一个实验,在正常播放时,用任务管理器,把 VLC 砍掉(结束进程树),造成客户
端异常死掉的情况,看看 Live555 是如何处理的?这个当课后作业吧。