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 OPTIONS(詢問S有哪些方法可用)
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.4 RTSP DESCRIBE(詢問S媒體初始化描述信息SDP)
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.5 RTSP SETUP(設置會話的屬性,以及傳輸模式,提醒S建立會話)
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:
這個參數的值是一個整型, 指出了每一秒最大的宏塊處理速度.
5.4 RTP timestamp
(1)RTP timestamp是用時鐘頻率(clock rate)計算而來表示時間的。
(2)RTP timestamp表示每幀的時間,由於一個幀(如I幀)可能被分成多個RTP包,所以多個相同幀的RTP timestamp相等。(可以通過每幀最後一個RTP的marker標誌區別幀,但最可靠的方法是查看相同RTP timestamp包爲同一幀。)
(3)視頻的時鐘頻率90000,音頻的時鐘頻率80000;
兩幀之間RTP timestamp的增量 = 時鐘頻率 / 幀率
若視頻幀率爲25fps,則相鄰幀間RTP timestamp增量值 = 90000/25 = 3600。
(4)RTP timestamp與播放時間npt的計算關係可參考《RTSP回放時如何通過RTP的timestamp計算npt時間》(http://blog.csdn.net/jasonhwang/article/details/7316168)。
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;
}