1
1
/*
2
- Example data:
3
- RTSP/1.0 200 OK
4
- CSeq: 3
5
- Content-Type: application/sdp
6
- Content-Base: rtsp://192.168.0.3/axis-media/media.amp/
7
- Server: GStreamer RTSP server
8
- Date: Wed, 03 Jun 2015 14:23:42 GMT
9
- Content-Length: 623
2
+ * The RTSP response format is defined in RFC 7826,
3
+ * using ABNF notation specified in RFC 5234.
4
+ * Strings in ABNF rules ("...") are always case insensitive!
5
+ *
6
+ * Basic rules to help with the headers below:
7
+ * ====
8
+ * CR = %x0D ; US-ASCII CR, carriage return (13)
9
+ * LF = %x0A ; US-ASCII LF, linefeed (10)
10
+ * SP = %x20 ; US-ASCII SP, space (32)
11
+ * HT = %x09 ; US-ASCII HT, horizontal-tab (9)
12
+ * CRLF = CR LF
13
+ * LWS = [CRLF] 1*( SP / HT ) ; Line-breaking whitespace
14
+ * SWS = [LWS] ; Separating whitespace
15
+ * HCOLON = *( SP / HT ) ":" SWS
16
+ *
17
+ * RTSP response rules (a `*` means zero or more):
18
+ * ====
19
+ * Status-Line = RTSP-Version SP Status-Code SP Reason-Phrase CRLF
20
+ * Response = Status-Line
21
+ * *((general-header
22
+ * / response-header
23
+ * / message-body-header) CRLF)
24
+ * CRLF
25
+ * [ message-body-data ]
26
+ *
27
+ * Example response:
28
+ * ====
29
+ * RTSP/1.0 200 OK
30
+ * CSeq: 3
31
+ * Content-Type: application/sdp
32
+ * Content-Base: rtsp://192.168.0.3/axis-media/media.amp/
33
+ * Server: GStreamer RTSP server
34
+ * Date: Wed, 03 Jun 2015 14:23:42 GMT
35
+ * Content-Length: 623
36
+ *
37
+ * v=0
38
+ * ....
39
+ */
10
40
11
- v=0
12
- ....
13
- */
41
+ /**
42
+ * Extract the value of a header.
43
+ *
44
+ * @param buffer The response bytes
45
+ * @param header The header to search for
46
+ */
14
47
export const extractHeaderValue = ( buffer : Buffer , header : string ) => {
15
- const anchor = `\n${ header } : `
16
- const start = buffer . indexOf ( anchor )
48
+ const anchor = `\n${ header . toLowerCase ( ) } : `
49
+ const start = buffer
50
+ . toString ( )
51
+ . toLowerCase ( )
52
+ . indexOf ( anchor )
17
53
if ( start >= 0 ) {
18
54
const end = buffer . indexOf ( '\n' , start + anchor . length )
19
55
const headerValue = buffer
@@ -25,6 +61,10 @@ export const extractHeaderValue = (buffer: Buffer, header: string) => {
25
61
}
26
62
27
63
export const sequence = ( buffer : Buffer ) => {
64
+ /**
65
+ * CSeq = "CSeq" HCOLON cseq-nr
66
+ * cseq-nr = 1*9DIGIT
67
+ */
28
68
const val = extractHeaderValue ( buffer , 'CSeq' )
29
69
if ( val !== null ) {
30
70
return Number ( val )
@@ -33,17 +73,29 @@ export const sequence = (buffer: Buffer) => {
33
73
}
34
74
35
75
export const sessionId = ( buffer : Buffer ) => {
76
+ /**
77
+ * Session = "Session" HCOLON session-id
78
+ * [ SEMI "timeout" EQUAL delta-seconds ]
79
+ * session-id = 1*256( ALPHA / DIGIT / safe )
80
+ * delta-seconds = 1*19DIGIT
81
+ */
36
82
const val = extractHeaderValue ( buffer , 'Session' )
37
83
return val ? val . split ( ';' ) [ 0 ] : null
38
84
}
39
85
40
86
export const sessionTimeout = ( buffer : Buffer ) => {
87
+ /**
88
+ * Session = "Session" HCOLON session-id
89
+ * [ SEMI "timeout" EQUAL delta-seconds ]
90
+ * session-id = 1*256( ALPHA / DIGIT / safe )
91
+ * delta-seconds = 1*19DIGIT
92
+ */
41
93
const val = extractHeaderValue ( buffer , 'Session' )
42
94
if ( val === null ) {
43
95
return null
44
96
}
45
97
const timeoutToken = 'timeout='
46
- const timeoutPosition = val . indexOf ( timeoutToken )
98
+ const timeoutPosition = val . toLowerCase ( ) . indexOf ( timeoutToken )
47
99
if ( timeoutPosition !== - 1 ) {
48
100
let timeoutVal = val . substring ( timeoutPosition + timeoutToken . length )
49
101
timeoutVal = timeoutVal . split ( ';' ) [ 0 ]
@@ -58,15 +110,43 @@ export const statusCode = (buffer: Buffer) => {
58
110
}
59
111
60
112
export const contentBase = ( buffer : Buffer ) => {
113
+ /**
114
+ * Content-Base = "Content-Base" HCOLON RTSP-URI
115
+ */
61
116
return extractHeaderValue ( buffer , 'Content-Base' )
62
117
}
63
118
64
119
export const connectionEnded = ( buffer : Buffer ) => {
65
- return extractHeaderValue ( buffer , 'Connection' ) === 'close'
120
+ /**
121
+ * Connection = "Connection" HCOLON connection-token
122
+ * *(COMMA connection-token)
123
+ * connection-token = "close" / token
124
+ */
125
+ const connectionToken = extractHeaderValue ( buffer , 'Connection' )
126
+ return connectionToken !== null && connectionToken . toLowerCase ( ) === 'close'
66
127
}
67
128
68
129
export const range = ( buffer : Buffer ) => {
69
- // Possible range headers:
130
+ /**
131
+ * Range = "Range" HCOLON ranges-spec
132
+ * ranges-spec = npt-range / utc-range / smpte-range
133
+ * / range-ext
134
+ * npt-range = "npt" [EQUAL npt-range-spec]
135
+ * npt-range-spec = ( npt-time "-" [ npt-time ] ) / ( "-" npt-time )
136
+ * npt-time = "now" / npt-sec / npt-hhmmss / npt-hhmmss-comp
137
+ * npt-sec = 1*19DIGIT [ "." 1*9DIGIT ]
138
+ * npt-hhmmss = npt-hh ":" npt-mm ":" npt-ss [ "." 1*9DIGIT ]
139
+ * npt-hh = 2*19DIGIT ; any positive number
140
+ * npt-mm = 2*2DIGIT ; 0-59
141
+ * npt-ss = 2*2DIGIT ; 0-59
142
+ * npt-hhmmss-comp = npt-hh-comp ":" npt-mm-comp ":" npt-ss-comp
143
+ * [ "." 1*9DIGIT ] ; Compatibility format
144
+ * npt-hh-comp = 1*19DIGIT ; any positive number
145
+ * npt-mm-comp = 1*2DIGIT ; 0-59
146
+ * npt-ss-comp = 1*2DIGIT ; 0-59
147
+ */
148
+
149
+ // Example range headers:
70
150
// Range: npt=now-
71
151
// Range: npt=1154.598701-3610.259146
72
152
const npt = extractHeaderValue ( buffer , 'Range' )
@@ -83,6 +163,11 @@ export const range = (buffer: Buffer) => {
83
163
* @return {Number } The body offset, or -1 if no header end found
84
164
*/
85
165
export const bodyOffset = ( chunk : Buffer ) => {
166
+ /**
167
+ * Strictly speaking, it seems RTSP MUST have CRLF and doesn't allow CR or LF on its own.
168
+ * That means that the end of the header part should be a pair of CRLF, but we're being
169
+ * flexible here and also allow LF LF or CR CR instead of CRLF CRLF.
170
+ */
86
171
const bodyOffsets = [ '\n\n' , '\r\r' , '\r\n\r\n' ]
87
172
. map ( s => {
88
173
const offset = chunk . indexOf ( s )
0 commit comments