1 前言
本文档主要描述了 NewStream Vision 系统中前端视频服务器(DVR, 网络摄像机), 中心转发服务器以及客户端之间的多媒体通信以及控制协议.
本协议主要基于标准的 IETE 的 RTSP/RTP 以及相关协议, 并针对具体应用定义了部分扩展.
本协议只是当前实现的总结和整理, 具体的协议细节以实际实现为准
2 定义
- RTSP
- 实现流协议
- SDP
- 会话描述协议
- RTP
- 实时传输协议
- H.264
- H.264 视频编码标准
3 RTSP 命令
3.1 Request 语法
语法:
RTSP 的语法和 HTTP 的语法基本相同, 具体如下。
COMMAND rtsp_URL RTSP/1.0<CRLF>
Headerfield1: val1<CRLF>
Headerfield2: val2<CRLF>
...
<CRLF>
[Body]
RTSP 消息行之间用回车换行 (CRLF) 分隔. 一个空行表示消息头部分的结束。
3.1.1 RTSP 方法
COMMAND 表示 RTSP 命令名称, 是 DESCRIBE, SETUP, OPTIONS, PLAY, PAUSE, TEARDOWN 或 SET_PARAMETER 等的任意一个.
3.1.2 RTSP URL
完整语法如下:
rtsp_URL = ( "rtsp:" | "rtspu:" )
"//" host [ ":" port ] [ abs_path ]
host = (A legal Internet host domain name of IP address
(in dotted decimal form), as defined by Section 2.1
of RFC 1123 \cite{rfc1123})
port = *DIGIT
如: rtsp://<servername>/live.mp4[?<param>=<value>[&<param>=<value>...]]
<servername> 表示产品的主机名称或者 IP 地址.
3.1.3 RTSP 版本
格式和 HTTP 协议类似, 且 RTSP 版本总是为 "RTSP/1.0"
3.1.4 RTSP 头字段
下面是所有命令都接受的头字段类型,一些命令接受或者必须用到一些附加的特别的头字段。
Authorization | 客户端的认证信息. |
CSeq | 请求序列号. |
Session | 会话 ID (返回自服务端的 SETUP 应答). |
Content-Length | 内容的长度. |
Content-Type | 内容的媒体类型. |
User-Agent | 关于创建这个请求的客户端的信息. |
Require | 查询是否支持指定的选项,不支持的选项会在 Unsupported 头中列出. |
3.2 Response 语法
语法:
RTSP/1.0 <Status Code> <Reason Phrase> <CRLF>
Headerfield3: val3<CRLF>
Headerfield4: val4<CRLF>
...
<CRLF>
[Body]
应答的第一行包含了表示请求是否成功或者失败的状态码和原因短语. 在 RFC 2326 有对状态码的详细描述.
标准的 RTSP 应答状态码和原因短语:
”100” ; Continue (all 100 range)
“200” ; OK
”201” ; Created
”250” ; Low on Storage Space
”300” ; Multiple Choices
”301” ; Moved Permanently
”302” ; Moved Temporarily
”303” ; See Other
”304” ; Not Modified
”305” ; Use Proxy
”350” ; Going Away
”351” ; Load Balancing
”400” ; Bad Request
”401” ; Unauthorized
”402” ; Payment Required
”403” ; Forbidden
”404” ; Not Found
”405” ; Method Not Allowed
”406” ; Not Acceptable
”407” ; Proxy Authentication Required
”408” ; Request Time-out
”410” ; Gone
”411” ; Length Required
”412” ; Precondition Failed
”413” ; Request Entity Too Large
”414” ; Request-URI Too Large
”415” ; Unsupported Media Type
”451” ; Parameter Not Understood
”452” ; reserved
”453” ; Not Enough Bandwidth
”454” ; Session Not Found
”455” ; Method Not Valid in This State
”456” ; Header Field Not Valid for Resource
”457” ; Invalid Range
”458” ; Parameter Is Read-Only
”459” ; Aggregate operation not allowed
”460” ; Only aggregate operation allowed
”461” ; Unsupported transport
”462” ; Destination unreachable
”500” ; Internal Server Error
”501” ; Not Implemented
”502” ; Bad Gateway
”503” ; Service Unavailable
”504” ; Gateway Time-out
”505” ; RTSP Version not supported
”551” ; Option not supported
下面的头字段可以在所有的 RTSP 应答消息中包含。
CSeq | 应答序列号 (和请求序列匹配). |
Session | 会话 ID. |
WWW-Authenticate | 客户端的认证信息. |
Date | 应答的日期和时间. |
Unsupported | 服务端不支持的特性和功能. |
3.3 RTSP DESCRIBE
DESCRIBE 命令用于请求指定的媒体流的 SDP 描述信息。关于 SDP ( Session Description Protocol,会话描述协议) 请参考 RFC 2327.
DESCRIBE 请求消息接受如下附加的头字段:
Accept | 列出客户支持的内容类型 (application/sdp is the only supported type). |
DESCRIBE 命令的应答消息包含如下附加的头字段:
Content-Type | 内容类型 (application/sdp). |
Content-Length | SDP 描述串的长度. |
Content-Base | 如果 SDP 描述串中使用了相对 URL, 这里是相关的基本 URL. |
例如:
请求
DESCRIBE rtsp://myserver/live.mp4 RTSP/1.0
CSeq: 0
User-Agent: Vision MC
Accept: application/sdp
Authorization: Basic cm9vdDpwYXNz
应答
RTSP/1.0 200 OK
CSeq: 0
Content-Type: application/sdp
Content-Base: rtsp://myserver/live.mp4
Date: Wed, 16 Jul 2008 12:48:47 GMT
Content-Length: 847
v=0
o=- 1216212527554872 1216212527554872 IN IP4 myserver
s=Media Presentation
e=NONE
c=IN IP4 0.0.0.0
b=AS:50064
t=0 0
a=control:rtsp://myserver/live.mp4
&resolution=640x480
a=range:npt=0.000000-
m=video 0 RTP/AVP 96
b=AS:50000
a=framerate:30.0
a=control:rtsp://myserver/live.mp4?trackID=1
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; profile-level-id=420029; sprop-parameter-sets=Z0IAKeKQFAe2AtwEBAaQeJEV,aM48gA==
m=audio 0 RTP/AVP 97
b=AS:64
a=control:rtsp://myserver/live.mp4?trackID=2
a=rtpmap:97 mpeg4-generic/16000/1
a=fmtp:97 profile-level-id=15; mode=AAC-hbr;config=1408; SizeLength=13; IndexLength=3;IndexDeltaLength=3; Profile=1; bitrate=64000;
3.4 RTSP OPTIONS
OPTIONS 请求用于返回服务端支持的 RTSP 命令列表 。也可以定时发送这个请求来保活相关的 RTSP 会话。
OPTIONS 命令的应答消息包含如下附加的头字段:
Public | 指出支持的 RTSP 命令. |
例如:列出支持的 RTSP 命令.
请求
OPTIONS * RTSP/1.0
CSeq: 1
User-Agent: Vision MC
Session: 12345678
Authorization: Basic cm9vdDpwYXNz
应答
RTSP/1.0 200 OK
CSeq: 1
Session: 12345678
Public: DESCRIBE, GET_PARAMETER, PAUSE, PLAY, SETUP, SET_PARAMETER, TEARDOWN
Date: Wed, 16 Jul 2008 12:48:48 GMT
3.5 RTSP SETUP
SETUP 命令用于配置数据交付的方法。
SETUP 请求和应答需要一个同样的附加的头字段:
Transport | 指出如何传输数据流。分别支持 RTP/AVP;unicast;client_port=port1-port2 RTP/AVP;multicast;client_port=port1-port2 RTP/AVP/TCP;unicast 等不同的传输方式 |
这个请求的应答返回一个必须在流控制命令 (如 PLAY,PAUSE,TEARMDOWN) 中使用的会话 ID。 如果这个 Session 头字段包含了 timemout 参数, 除非有保活,否则会话会在超时时间后被关闭。会话可以通过发送包含 Session ID 的 RTSP 请求 (如 OPTIONS,GET_PARAMETER) 给服务端来保活。 或者使用 RTCP 消息。不支持中间重新更改传输参数。
例如: 在第一个 SETUP 请求的应答中返回会话的 ID。并且后续的请求中都包含这个会话 ID。
请求
SETUP rtsp://myserver/live.mp4?trackID=1 RTSP/1.0
CSeq: 2
User-Agent: Vision MC
Transport: RTP/AVP;unicast;client_port=20000-20001
Authorization: Basic cm9vdDpwYXNz
应答
RTSP/1.0 200 OK
CSeq: 2
Session: 12345678; timeout=60
Transport: RTP/AVP;unicast;client_port=20000-20001;server_port=50000-50001;ssrc=B0BA7855;mode="PLAY"
Date: Wed, 16 Jul 2008 12:48:47 GMT
例如:
请求
SETUP rtsp://myserver/live.mp4
trackID=2 RTSP/1.0
CSeq: 3
User-Agent: Vision MC
Transport: RTP/AVP;unicast;client_port=20002-20003
Session: 12345678
Authorization: Basic cm9vdDpwYXNz
应答
RTSP/1.0 200 OK
CSeq: 3
Session: 12345678; timeout=60
Transport: RTP/AVP;unicast;client_port=20002-20003;server_port=50002-50003;ssrc=D7EB59C0;mode="PLAY"
Date: Wed, 16 Jul 2008 12:48:48 GMT
Transport 头字段定义:
Transport = "Transport" ":"
1\#transport-spec
transport-spec = transport-protocol/profile[/lower-transport]
parameter
transport-protocol = "RTP"
profile = "AVP"
lower-transport = "TCP" | "UDP"
parameter = ( "unicast" | "multicast" )
| ";" "destination" [ "=" address ]
| ";" "interleaved" "=" channel [ "-" channel ]
| ";" "append"
| ";" "ttl" "=" ttl
| ";" "layers" "=" 1*DIGIT
| ";" "port" "=" port [ "-" port ]
| ";" "client_port" "=" port [ "-" port ]
| ";" "server_port" "=" port [ "-" port ]
| ";" "ssrc" "=" ssrc
| ";" "mode" = <"> 1\#mode <">
ttl = 1*3(DIGIT)
port = 1*5(DIGIT)
ssrc = 8*8(HEX)
channel = 1*3(DIGIT)
address = host
mode = <">Method <"> | Method
Example:
Transport: RTP/AVP;multicast;ttl=127;mode="PLAY",
RTP/AVP;unicast;client_port=3456-3457;mode="PLAY"
3.6 RTSP PLAY
这个 PLAY 用于启动 (当暂停时重启) 交付数据给客户端. PLAY 命令的应答消息包含如下附加的头字段:
Range | 播放时间段. |
RTP-Info | 关于 RTP 流的信息。包含相关的流的第一个包的序列号。 |
例如:
请求
PLAY rtsp://myserver/live.mp4 RTSP/1.0
CSeq: 4
User-Agent: Vision MC
Session: 12345678
Authorization: Basic cm9vdDpwYXNz
应答
RTSP/1.0 200 OK
CSeq: 4
Session: 12345678
Range: npt=0.645272-
RTP-Info: url=rtsp://myserver/live.mp4?trackID=1;seq=46932;rtptime=1027887748, url=rtsp://myserver/live.mp4?trackID=2;seq=3322;rtptime=611053482
Date: Wed, 16 Jul 2008 12:48:48 GMT
例如: Play back the recording “myrecording”.
请求
PLAY rtsp://myserver/live.mp4?recordingid=”myrecording” RTSP/1.0
CSeq: 4
User-Agent: Vision MC
Session: 12345678
Authorization: Basic cm9vdDpwYXNz
3.7 RTSP PAUSE
PAUSE 请求用于临时停止服务端的数据的交付。使用 PLAY 来重新启动数据交付。
例如:
请求
PAUSE rtsp://myserver/live.mp4 RTSP/1.0
CSeq: 5
User-Agent: Vision MC
Session: 12345678
Authorization: Basic cm9vdDpwYXNz
应答
RTSP/1.0 200 OK
CSeq: 5
Session: 12345678
Date: Wed, 16 Jul 2008 12:48:49 GMT
3.8 RTSP TEARDOWN
TEARDOWN 请求用于终止来自服务端的数据的传输。
例如:
请求
TEARDOWN rtsp://myserver/live.mp4 RTSP/1.0
CSeq: 6
User-Agent: Vision MC
Session: 12345678
Authorization: Basic cm9vdDpwYXNz
应答
RTSP/1.0 200 OK
CSeq: 6
Session: 12345678
Date: Wed, 16 Jul 2008 12:49:01 GMT
3.9 RTSP SET_PARAMETER
SET_PARAMETER 命令用于请求尽快生成一个 I 帧。例如当开始录像的时候。
必须包含 X-Request-Key-Frame: 1 的头字段。
请求
SET_PARAMETER rtsp://myserver/live.mp4 RTSP/1.0
CSeq: 7
Session: 12345678
Authorization: Basic cm9vdDpwYXNz
X-Request-Key-Frame: 1
Content-Type: text/parameters
Content-Length: 19 Renew-Stream: yes
应答
RTSP/1.0 200 OK
CSeq: 8
Session: 12345678
Date: Wed, 16 Jul 2008 13:01:25 GMT
3.10 RTSP GET_PARAMETER
标准协议中 GET_PARAMETER 可以用于查询参数状态, 目前设备主要通过 GET 命令来查询设备参数, 所以 GET_PARAMETER 用处不大, 目前主要用来用做会话保活请求.
请求
GET_PARAMETER rtsp://myserver/live.mp4 RTSP/1.0
CSeq: 7
Session: 12345678
Authorization: Basic cm9vdDpwYXNz
应答
RTSP/1.0 200 OK
CSeq: 8
Session: 12345678
Date: Wed, 16 Jul 2008 13:01:25 GMT
3.11 RTSP 代理服务器扩展协议
3.11.1 设备注册
前端设备通过向 RTSP 代理服务器发送 OPTIONS 命令进行注册
注册的时候, 必须包含如下的头字段:
- X-Proxy-ID: 前端设备的序列号.
- X-LocalAddress: 前端设备的本地 IP 地址.
3.11.2 连接请求
服务端通过向前端设备发送 OPTIONS 命令请求新的 RTSP 连接
这个方法主要用于多通道的前端设备.
请求的时候, 必须包含如下的头字段:
- X-Invite: 要请求的通道的 URL.
3.11.2 注册通道连接
前端设备收到服务端的连接请求时, 将向服务端创建一个新的 TCP 连接, 并通过发送 OPTIONS 命令进行注册新的通道.
注册的时候, 必须包含如下的头字段:
- X-Proxy-ID: 前端设备的序列号.
- X-LocalAddress: 前端设备的本地 IP 地址.
例如:
注册: 前端设备 => 转发服务器
OPTIONS * RTSP/1.0
X-Proxy-ID: 0006F609CA03
X-LocalAddress: 192.168.1.192:2100
CSeq: 13
User-Agent: Linux/2.6.14,M898v2/3.10.218,(MAC:0006F609CA03)
请求建立通道连接, 一般一个通道建立一个连接.
OPTIONS /live/0006F609CA03 RTSP/1.0
X-Invite: /live/0006F609CA03/live2.mp4
CSeq: 0
前端设备收到上面的请求后, 向转发服务器创建一个新的连接, 并发送如下的 OPTIONS 命令
OPTIONS * RTSP/1.0
X-Proxy-Channel: /live/0006F609CA03/live2.mp4
X-LocalAddress: 192.168.1.192:2757
CSeq: 1
User-Agent: Linux/2.6.14,M898v2/3.10.218,(MAC:0006F609CA03)
X-Proxy-Channel: 用于指示当前连接绑定的通道, 和 上面的 X-Invite 头字段的值必须完全相同.
3.12 RTSP/RTP 交错传输方式.
实现 RTSP 的系统必须支持通过 TCP 传输 RTSP 数据包,并支持 UDP。对 UDP 和 TCP,RTSP 服务器的缺省端口都是 554。许多目的一致的 RTSP 包被打包成单个低层 UDP 或 TCP 流。RTSP 数据可与 RTP 和 RTCP 包交错传输, 即可以通过 TCP 传输 RTP 包。不像 HTTP,RTSP 消息必须总是包含一个内容长度头 (Content-Length),无论消息是否包含消息内容。如果没有指定消息内容的长度则默认消息的内容的长度为 0。
当 RTP 通过 TCP 和 RTSP 消息交错传输时, 必须在 RTP 包前加 4 个字节长度的头, 它的结构如下:
BYTE 必须是 “$” or 0x24
BYTE Channel id ,在 SETUP 消息中 Transport 头字段中 interleaved 参数指定.
WORD 数据包的长度(从接下来的数据开始算起, 不包括这 4 个字节的头的长度)
例如:
C->S: SETUP rtsp://foo.com/bar.file RTSP/1.0
CSeq: 2
Transport: RTP/AVP/TCP;interleaved=0-1 S->C: RTSP/1.0 200 OK
CSeq: 2
Date: 05 Jun 1997 18:57:18 GMT
Transport: RTP/AVP/TCP;interleaved=0-1
Session: 12345678 C->S: PLAY rtsp://foo.com/bar.file RTSP/1.0
CSeq: 3
Session: 12345678 S->C: RTSP/1.0 200 OK
CSeq: 3
Session: 12345678
Date: 05 Jun 1997 18:59:15 GMT
RTP-Info: url=rtsp://foo.com/bar.file;
seq=232433;rtptime=972948234 S->C: $\000{2 byte length}{"length" bytes data, w/RTP header}
S->C: $\000{2 byte length}{"length" bytes data, w/RTP header}
S->C: $\001{2 byte length}{"length" bytes RTCP packet}
4 SDP 协议
4.1 SDP 协议简介
会话描述协议(SDP)为会话通知、会话邀请和其它形式的多媒体会话初始化等目的提供了多媒体会话描述。
会话目录用于协助多媒体会议的通告,并为会话参与者传送相关设置信息。SDP 即用于将这种信息传输到接收端。SDP 完全是一种会话描述格式 ― 它不属于传输协议 ― 它只使用不同的适当的传输协议,包括会话通知协议(SAP)、会话初始协议(SIP)、实时流协议(RTSP)、MIME 扩展协议的电子邮件以及超文本传输协议(HTTP)。
SDP 的设计宗旨是通用性,它可以应用于大范围的网络环境和应用程序,而不仅仅局限于组播会话目录,但 SDP 不支持会话内容或媒体编码的协商。
SDP 文本信息包括:
- 会话名称和意图;
- 会话持续时间;
- 构成会话的媒体;
- 有关接收媒体的信息(地址等)。
Session description
v= (protocol version)
o= (owner/creator and session identifier).
s= (session name)
i=* (session information)
u=* (URI of description)
e=* (email address)
p=* (phone number)
c=* (connection information - not required if included in all media)
b=* (bandwidth information)
One or more time descriptions (see below)
z=* (time zone adjustments)
k=* (encryption key)
a=* (zero or more session attribute lines)
Zero or more media descriptions (see below)
Time description
t= (time the session is active)
r=* (zero or more repeat times)
Media description
m= (media name and transport address)
i=* (media title)
c=* (connection information - optional if included at session-level)
b=* (bandwidth information)
k=* (encryption key)
a=* (zero or more media attribute lines)
4.2 协议结构
SDP 信息是文本信息,采用 UTF-8 编 码中的 ISO 10646 字符集。SDP 会话描述如下:(标注 * 符号的表示可选字段):
* v = (协议版本)
* o = (所有者/创建者和会话标识符)
* s = (会话名称)
* i = * (会话信息)
* u = * (URI 描述)
* e = * (Email 地址)
* p = * (电话号码)
* c = * (连接信息 ― 如果包含在所有媒体中,则不需要该字段)
* b = * (带宽信息)
一个或更多时间描述(如下所示):
* z = * (时间区域调整)
* k = * (加密密钥)
* a = * (0 个或多个会话属性行)
* 0个或多个媒体描述(如下所示)
时间描述
* t = (会话活动时间)
* r = * (0或多次重复次数)
媒体描述
* m = (媒体名称和传输地址)
* i = * (媒体标题)
* c = * (连接信息 — 如果包含在会话层则该字段可选)
* b = * (带宽信息)
* k = * (加密密钥)
* a = * (0 个或多个会话属性行
5 H.264 视频 RTP 负载格式
5.1. 网络抽象层单元类型 (NALU)
NALU 头由一个字节组成, 它的语法如下:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
- F: 1 个比特.
- forbidden_zero_bit. 在 H.264 规范中规定了这一位必须为 0.
- NRI: 2 个比特.
- nal_ref_idc. 取 00 ~ 11, 似乎指示这个 NALU 的重要性, 如 00 的 NALU 解码器可以丢弃它而不影响图像的回放. 不过一般情况下不太关心这个属性.
- Type: 5 个比特.
- nal_unit_type. 这个 NALU 单元的类型. 简述如下:
0 没有定义
1-23 NAL单元 单个 NAL 单元包.
24 STAP-A 单一时间的组合包
25 STAP-B 单一时间的组合包
26 MTAP16 多个时间的组合包
27 MTAP24 多个时间的组合包
28 FU-A 分片的单元
29 FU-B 分片的单元
30-31 没有定义
5.2. 打包模式
下面是 RFC 3550 中规定的 RTP 头的结构.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
负载类型 Payload type (PT): 7 bits
序列号 Sequence number (SN): 16 bits
时间戳 Timestamp: 32 bits
H.264 Payload 格式定义了三种不同的基本的负载(Payload)结构. 接收端可能通过 RTP Payload 的第一个字节来识别它们. 这一个字节类似 NALU 头的格式, 而这个头结构的 NAL 单元类型字段 则指出了代表的是哪一种结构,
这个字节的结构如下, 可以看出它和 H.264 的 NALU 头结构是一样的.
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
字段 Type: 这个 RTP payload 中 NAL 单元的类型. 这个字段和 H.264 中类型字段的区别是, 当 type 的值为 24 ~ 31 表示这是一个特别格式的 NAL 单元, 而 H.264 中, 只取 1~23 是有效的值.
24 STAP-A 单一时间的组合包
25 STAP-B 单一时间的组合包
26 MTAP16 多个时间的组合包
27 MTAP24 多个时间的组合包
28 FU-A 分片的单元
29 FU-B 分片的单元
30-31 没有定义
可能的结构类型分别有:
- 1. 单一 NAL 单元模式
- 即一个 RTP 包仅由一个完整的 NALU 组成. 这种情况下 RTP NAL 头类型字段和原始的 H.264的 NALU 头类型字段是一样的.
- 2. 组合封包模式
- 即可能是由多个 NAL 单元组成一个 RTP 包. 分别有4种组合方式: STAP-A, STAP-B, MTAP16, MTAP24. 那么这里的类型值分别是 24, 25, 26 以及 27.
- 3. 分片封包模式
- 用于把一个 NALU 单元封装成多个 RTP 包. 存在两种类型 FU-A 和 FU-B. 类型值分别是 28 和 29.
2.1 单一 NAL 单元模式
对于 NALU 的长度小于 MTU 大小的包, 一般采用单一 NAL 单元模式. 对于一个原始的 H.264 NALU 单元常由 [Start Code] [NALU Header] [NALU Payload] 三部分组成, 其中 Start Code 用于标示这是一个
NALU 单元的开始, 必须是 "00 00 00 01" 或 "00 00 01", NALU 头仅一个字节, 其后都是 NALU 单元内容. 打包时去除 "00 00 01" 或 "00 00 00 01" 的开始码, 把其他数据封包的 RTP 包即可.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F|NRI| type | |
+-+-+-+-+-+-+-+-+ |
| |
| Bytes 2..n of a Single NAL unit |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
如有一个 H.264 的 NALU 是这样的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ]
这是一个序列参数集 NAL 单元. [00 00 00 01] 是四个字节的开始码, 67 是 NALU 头, 42 开始的数据是 NALU 内容. 封装成 RTP 包将如下:
[ RTP Header ] [ 67 42 A0 1E 23 56 0E 2F ]
即只要去掉 4 个字节的开始码就可以了.
2.2 组合封包模式
其次, 当 NALU 的长度特别小时, 可以把几个 NALU 单元封在一个 RTP 包中.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| RTP Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|STAP-A NAL HDR | NALU 1 Size | NALU 1 HDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 1 Data |
: :
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | NALU 2 Size | NALU 2 HDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 2 Data |
: :
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
2.3 Fragmentation Units (FUs).
而当 NALU 的长度超过 MTU 时, 就必须对 NALU 单元进行分片封包. 也称为 Fragmentation Units (FUs).
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FU indicator | FU header | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| FU payload |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ Figure 14. RTP payload format for FU-A
The FU indicator octet has the following format:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
The FU header has the following format:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R| Type |
+---------------+
如有一个 H.264 的 NALU 是这样的:
[00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ]
[00 00 00 01 67 42 A0 1E 23 56 0E 2F ... ]
这是一个序列参数集 NAL 单元. [00 00 00 01] 是四个字节的开始码, 67 是 NALU 头, 42 开始的数据是 NALU 内容. 封装成 RTP 包可能如下:
[ RTP Header ] [78, STAP-A NAL HDR, 一个字节 ] [长度, 两个字节] [ 67 42 A0 1E 23 56 0E 2F ...] [长度, 两个字节] [ 67 42 A0 1E 23 56 0E 2F... ]
5.3. SDP 参数
下面描述了如何在 SDP 中表示一个 H.264 流:
- . "m=" 行中的媒体名必须是 "video"
- . "a=rtpmap" 行中的编码名称必须是 "H264".
- . "a=rtpmap" 行中的时钟频率必须是 90000.
- . 其他参数都包括在 "a=fmtp" 行中.
如:
m=video 49170 RTP/AVP 98
a=rtpmap:98 H264/90000
a=fmtp:98 profile-level-id=42A01E; sprop-parameter-sets=Z0IACpZTBYmI,aMljiA==
下面介绍一些常用的参数.
3.1 packetization-mode:
表示支持的封包模式.
- 当 packetization-mode 的值为 0 时或不存在时, 必须使用单一 NALU 单元模式.
- 当 packetization-mode 的值为 1 时必须使用非交错(non-interleaved)封包模式.
- 当 packetization-mode 的值为 2 时必须使用交错(interleaved)封包模式.
这个参数不可以取其他的值.
3.2 sprop-parameter-sets:
这个参数可以用于传输 H.264 的序列参数集和图像参数 NAL 单元. 这个参数的值采用 Base64 进行编码. 不同的参数集间用","号隔开.
3.3 profile-level-id:
这个参数用于指示 H.264 流的 profile 类型和级别. 由 Base16(十六进制) 表示的 3 个字节. 第一个字节表示 H.264 的 Profile 类型, 第 三个字节表示 H.264 的 Profile 级别.
3.4 max-mbps:
这个参数的值是一个整型, 指出了每一秒最大的宏块处理速度.
6 示例
设备注册:
WARN [9516] 16:36:29 OPTIONS * RTSP/1.0
X-Proxy-ID: 0006F609CA03
X-LocalAddress: 192.168.1.192:4314
CSeq: 14
User-Agent: Linux/2.6.14,M898v2/3.10.218,(MAC:0006F609CA03)
客户端请求第二个通道
INFO [9516] 16:36:37 New Connection: 192.168.1.21 ...
WARN [9516] 16:36:37 DESCRIBE /live/0006F609CA03/live2.mp4 RTSP/1.0
Accept: application/sdp
CSeq: 1
User-Agent: NewStream Vision Player 3.3
转发服务器还没有创建这个通道, 请求前端设备创建这个通道的连接.
DEBUG [9516] 16:36:37 Invite: /live/0006F609CA03/live2.mp4
DEBUG [9516] 16:36:37 OPTIONS /live/0006F609CA03 RTSP/1.0
X-Invite: /live/0006F609CA03/live2.mp4
CSeq: 0 WARN [9516] 16:36:37 RTSP/1.0 200 OK
CSeq: 0
Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, SET_PARAMETER, GET_PARAMETER
Server: IPCAM Embed Stream Server
Date: Tue, 13 Jul 2010 16:36:40 +0800
前端设备发起一个新的连接
INFO [9516] 16:36:37 New Connection: 192.168.1.192 ...
DEBUG [9516] 16:36:37 /live/0006F609CA03/live2.mp4: /live2.mp4
DEBUG [9516] 16:36:37 Add Source (/live/0006F609CA03/live2.mp4)
WARN [9516] 16:36:37 AsyncSend: WSASend() failed with error 10057
INFO [9516] 16:36:37 AttachMediaChannel: Bind to /live/0006F609CA03/live2.mp4 DEBUG [9516] 16:36:37 /live/0006F609CA03/live2.mp4: /live2.mp4
DEBUG [9516] 16:36:37 OnStateChanged: 1
DEBUG [9516] 16:36:37 OnStateChanged: 2
WARN [9516] 16:36:37 OPTIONS * RTSP/1.0
X-Proxy-Channel: /live/0006F609CA03/live2.mp4
X-LocalAddress: 192.168.1.192:4315
CSeq: 1
User-Agent: Linux/2.6.14,M898v2/3.10.218,(MAC:0006F609CA03) INFO [9668] 19:35:20 New Connection: ...
7 参考代码
/** 发送指定的 NALU 单元. */
int GEPlayback::SendNaluPacket( BYTE* sliceData, int sliceSize, BOOL isEnd,
BOOL isVideo, int type, time_t pts, INT64 timestamp )
{
// NALU 小于最大 RTP 包大小的情况
if (sliceSize < 1350) {
return SendPacket(sliceData, sliceSize, isEnd, TRUE, type, pts, timestamp);
} // 如果一个 NALU 大于最大的 RTP 包的大小, 则需要把它进行分片后打包发送
BYTE buffer[1500]; BYTE nalHeader = sliceData[0]; // NALU 头
BYTE* data = sliceData + 1;
int leftover = sliceSize - 1;
BOOL isStart = TRUE; while (leftover > 0) {
int size = MIN(1350, leftover);
isEnd = (size == leftover); // 构建 FU 头
buffer[0] = (nalHeader & 0x60) | 28; // FU indicator
buffer[1] = (nalHeader & 0x1f); // FU header
if (isStart) {
buffer[1] |= 0x80;
} if (isEnd) {
buffer[1] |= 0x40;
} memcpy(buffer + 2, data, size);
SendPacket(buffer, size + 2, isEnd, TRUE, type, pts, timestamp); leftover -= size;
data += size;
isStart = FALSE;
} return sliceSize;
}