Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
6148 | serge | 1 | /* |
2 | * RTSP demuxer |
||
3 | * Copyright (c) 2002 Fabrice Bellard |
||
4 | * |
||
5 | * This file is part of FFmpeg. |
||
6 | * |
||
7 | * FFmpeg is free software; you can redistribute it and/or |
||
8 | * modify it under the terms of the GNU Lesser General Public |
||
9 | * License as published by the Free Software Foundation; either |
||
10 | * version 2.1 of the License, or (at your option) any later version. |
||
11 | * |
||
12 | * FFmpeg is distributed in the hope that it will be useful, |
||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
15 | * Lesser General Public License for more details. |
||
16 | * |
||
17 | * You should have received a copy of the GNU Lesser General Public |
||
18 | * License along with FFmpeg; if not, write to the Free Software |
||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||
20 | */ |
||
21 | |||
22 | #include "libavutil/avstring.h" |
||
23 | #include "libavutil/intreadwrite.h" |
||
24 | #include "libavutil/mathematics.h" |
||
25 | #include "libavutil/random_seed.h" |
||
26 | #include "libavutil/time.h" |
||
27 | #include "avformat.h" |
||
28 | |||
29 | #include "internal.h" |
||
30 | #include "network.h" |
||
31 | #include "os_support.h" |
||
32 | #include "rtpproto.h" |
||
33 | #include "rtsp.h" |
||
34 | #include "rdt.h" |
||
35 | #include "url.h" |
||
36 | |||
37 | static const struct RTSPStatusMessage { |
||
38 | enum RTSPStatusCode code; |
||
39 | const char *message; |
||
40 | } status_messages[] = { |
||
41 | { RTSP_STATUS_OK, "OK" }, |
||
42 | { RTSP_STATUS_METHOD, "Method Not Allowed" }, |
||
43 | { RTSP_STATUS_BANDWIDTH, "Not Enough Bandwidth" }, |
||
44 | { RTSP_STATUS_SESSION, "Session Not Found" }, |
||
45 | { RTSP_STATUS_STATE, "Method Not Valid in This State" }, |
||
46 | { RTSP_STATUS_AGGREGATE, "Aggregate operation not allowed" }, |
||
47 | { RTSP_STATUS_ONLY_AGGREGATE, "Only aggregate operation allowed" }, |
||
48 | { RTSP_STATUS_TRANSPORT, "Unsupported transport" }, |
||
49 | { RTSP_STATUS_INTERNAL, "Internal Server Error" }, |
||
50 | { RTSP_STATUS_SERVICE, "Service Unavailable" }, |
||
51 | { RTSP_STATUS_VERSION, "RTSP Version not supported" }, |
||
52 | { 0, "NULL" } |
||
53 | }; |
||
54 | |||
55 | static int rtsp_read_close(AVFormatContext *s) |
||
56 | { |
||
57 | RTSPState *rt = s->priv_data; |
||
58 | |||
59 | if (!(rt->rtsp_flags & RTSP_FLAG_LISTEN)) |
||
60 | ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL); |
||
61 | |||
62 | ff_rtsp_close_streams(s); |
||
63 | ff_rtsp_close_connections(s); |
||
64 | ff_network_close(); |
||
65 | rt->real_setup = NULL; |
||
66 | av_freep(&rt->real_setup_cache); |
||
67 | return 0; |
||
68 | } |
||
69 | |||
70 | static inline int read_line(AVFormatContext *s, char *rbuf, const int rbufsize, |
||
71 | int *rbuflen) |
||
72 | { |
||
73 | RTSPState *rt = s->priv_data; |
||
74 | int idx = 0; |
||
75 | int ret = 0; |
||
76 | *rbuflen = 0; |
||
77 | |||
78 | do { |
||
79 | ret = ffurl_read_complete(rt->rtsp_hd, rbuf + idx, 1); |
||
80 | if (ret <= 0) |
||
81 | return ret ? ret : AVERROR_EOF; |
||
82 | if (rbuf[idx] == '\r') { |
||
83 | /* Ignore */ |
||
84 | } else if (rbuf[idx] == '\n') { |
||
85 | rbuf[idx] = '\0'; |
||
86 | *rbuflen = idx; |
||
87 | return 0; |
||
88 | } else |
||
89 | idx++; |
||
90 | } while (idx < rbufsize); |
||
91 | av_log(s, AV_LOG_ERROR, "Message too long\n"); |
||
92 | return AVERROR(EIO); |
||
93 | } |
||
94 | |||
95 | static int rtsp_send_reply(AVFormatContext *s, enum RTSPStatusCode code, |
||
96 | const char *extracontent, uint16_t seq) |
||
97 | { |
||
98 | RTSPState *rt = s->priv_data; |
||
99 | char message[4096]; |
||
100 | int index = 0; |
||
101 | while (status_messages[index].code) { |
||
102 | if (status_messages[index].code == code) { |
||
103 | snprintf(message, sizeof(message), "RTSP/1.0 %d %s\r\n", |
||
104 | code, status_messages[index].message); |
||
105 | break; |
||
106 | } |
||
107 | index++; |
||
108 | } |
||
109 | if (!status_messages[index].code) |
||
110 | return AVERROR(EINVAL); |
||
111 | av_strlcatf(message, sizeof(message), "CSeq: %d\r\n", seq); |
||
112 | av_strlcatf(message, sizeof(message), "Server: %s\r\n", LIBAVFORMAT_IDENT); |
||
113 | if (extracontent) |
||
114 | av_strlcat(message, extracontent, sizeof(message)); |
||
115 | av_strlcat(message, "\r\n", sizeof(message)); |
||
116 | av_dlog(s, "Sending response:\n%s", message); |
||
117 | ffurl_write(rt->rtsp_hd, message, strlen(message)); |
||
118 | |||
119 | return 0; |
||
120 | } |
||
121 | |||
122 | static inline int check_sessionid(AVFormatContext *s, |
||
123 | RTSPMessageHeader *request) |
||
124 | { |
||
125 | RTSPState *rt = s->priv_data; |
||
126 | unsigned char *session_id = rt->session_id; |
||
127 | if (!session_id[0]) { |
||
128 | av_log(s, AV_LOG_WARNING, "There is no session-id at the moment\n"); |
||
129 | return 0; |
||
130 | } |
||
131 | if (strcmp(session_id, request->session_id)) { |
||
132 | av_log(s, AV_LOG_ERROR, "Unexpected session-id %s\n", |
||
133 | request->session_id); |
||
134 | rtsp_send_reply(s, RTSP_STATUS_SESSION, NULL, request->seq); |
||
135 | return AVERROR_STREAM_NOT_FOUND; |
||
136 | } |
||
137 | return 0; |
||
138 | } |
||
139 | |||
140 | static inline int rtsp_read_request(AVFormatContext *s, |
||
141 | RTSPMessageHeader *request, |
||
142 | const char *method) |
||
143 | { |
||
144 | RTSPState *rt = s->priv_data; |
||
145 | char rbuf[1024]; |
||
146 | int rbuflen, ret; |
||
147 | do { |
||
148 | ret = read_line(s, rbuf, sizeof(rbuf), &rbuflen); |
||
149 | if (ret) |
||
150 | return ret; |
||
151 | if (rbuflen > 1) { |
||
152 | av_dlog(s, "Parsing[%d]: %s\n", rbuflen, rbuf); |
||
153 | ff_rtsp_parse_line(request, rbuf, rt, method); |
||
154 | } |
||
155 | } while (rbuflen > 0); |
||
156 | if (request->seq != rt->seq + 1) { |
||
157 | av_log(s, AV_LOG_ERROR, "Unexpected Sequence number %d\n", |
||
158 | request->seq); |
||
159 | return AVERROR(EINVAL); |
||
160 | } |
||
161 | if (rt->session_id[0] && strcmp(method, "OPTIONS")) { |
||
162 | ret = check_sessionid(s, request); |
||
163 | if (ret) |
||
164 | return ret; |
||
165 | } |
||
166 | |||
167 | return 0; |
||
168 | } |
||
169 | |||
170 | static int rtsp_read_announce(AVFormatContext *s) |
||
171 | { |
||
172 | RTSPState *rt = s->priv_data; |
||
173 | RTSPMessageHeader request = { 0 }; |
||
174 | char sdp[4096]; |
||
175 | int ret; |
||
176 | |||
177 | ret = rtsp_read_request(s, &request, "ANNOUNCE"); |
||
178 | if (ret) |
||
179 | return ret; |
||
180 | rt->seq++; |
||
181 | if (strcmp(request.content_type, "application/sdp")) { |
||
182 | av_log(s, AV_LOG_ERROR, "Unexpected content type %s\n", |
||
183 | request.content_type); |
||
184 | rtsp_send_reply(s, RTSP_STATUS_SERVICE, NULL, request.seq); |
||
185 | return AVERROR_OPTION_NOT_FOUND; |
||
186 | } |
||
187 | if (request.content_length && request.content_length < sizeof(sdp) - 1) { |
||
188 | /* Read SDP */ |
||
189 | if (ffurl_read_complete(rt->rtsp_hd, sdp, request.content_length) |
||
190 | < request.content_length) { |
||
191 | av_log(s, AV_LOG_ERROR, |
||
192 | "Unable to get complete SDP Description in ANNOUNCE\n"); |
||
193 | rtsp_send_reply(s, RTSP_STATUS_INTERNAL, NULL, request.seq); |
||
194 | return AVERROR(EIO); |
||
195 | } |
||
196 | sdp[request.content_length] = '\0'; |
||
197 | av_log(s, AV_LOG_VERBOSE, "SDP: %s\n", sdp); |
||
198 | ret = ff_sdp_parse(s, sdp); |
||
199 | if (ret) |
||
200 | return ret; |
||
201 | rtsp_send_reply(s, RTSP_STATUS_OK, NULL, request.seq); |
||
202 | return 0; |
||
203 | } |
||
204 | av_log(s, AV_LOG_ERROR, |
||
205 | "Content-Length header value exceeds sdp allocated buffer (4KB)\n"); |
||
206 | rtsp_send_reply(s, RTSP_STATUS_INTERNAL, |
||
207 | "Content-Length exceeds buffer size", request.seq); |
||
208 | return AVERROR(EIO); |
||
209 | } |
||
210 | |||
211 | static int rtsp_read_options(AVFormatContext *s) |
||
212 | { |
||
213 | RTSPState *rt = s->priv_data; |
||
214 | RTSPMessageHeader request = { 0 }; |
||
215 | int ret = 0; |
||
216 | |||
217 | /* Parsing headers */ |
||
218 | ret = rtsp_read_request(s, &request, "OPTIONS"); |
||
219 | if (ret) |
||
220 | return ret; |
||
221 | rt->seq++; |
||
222 | /* Send Reply */ |
||
223 | rtsp_send_reply(s, RTSP_STATUS_OK, |
||
224 | "Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, RECORD\r\n", |
||
225 | request.seq); |
||
226 | return 0; |
||
227 | } |
||
228 | |||
229 | static int rtsp_read_setup(AVFormatContext *s, char* host, char *controlurl) |
||
230 | { |
||
231 | RTSPState *rt = s->priv_data; |
||
232 | RTSPMessageHeader request = { 0 }; |
||
233 | int ret = 0; |
||
234 | char url[1024]; |
||
235 | RTSPStream *rtsp_st; |
||
236 | char responseheaders[1024]; |
||
237 | int localport = -1; |
||
238 | int transportidx = 0; |
||
239 | int streamid = 0; |
||
240 | |||
241 | ret = rtsp_read_request(s, &request, "SETUP"); |
||
242 | if (ret) |
||
243 | return ret; |
||
244 | rt->seq++; |
||
245 | if (!request.nb_transports) { |
||
246 | av_log(s, AV_LOG_ERROR, "No transport defined in SETUP\n"); |
||
247 | return AVERROR_INVALIDDATA; |
||
248 | } |
||
249 | for (transportidx = 0; transportidx < request.nb_transports; |
||
250 | transportidx++) { |
||
251 | if (!request.transports[transportidx].mode_record || |
||
252 | (request.transports[transportidx].lower_transport != |
||
253 | RTSP_LOWER_TRANSPORT_UDP && |
||
254 | request.transports[transportidx].lower_transport != |
||
255 | RTSP_LOWER_TRANSPORT_TCP)) { |
||
256 | av_log(s, AV_LOG_ERROR, "mode=record/receive not set or transport" |
||
257 | " protocol not supported (yet)\n"); |
||
258 | return AVERROR_INVALIDDATA; |
||
259 | } |
||
260 | } |
||
261 | if (request.nb_transports > 1) |
||
262 | av_log(s, AV_LOG_WARNING, "More than one transport not supported, " |
||
263 | "using first of all\n"); |
||
264 | for (streamid = 0; streamid < rt->nb_rtsp_streams; streamid++) { |
||
265 | if (!strcmp(rt->rtsp_streams[streamid]->control_url, |
||
266 | controlurl)) |
||
267 | break; |
||
268 | } |
||
269 | if (streamid == rt->nb_rtsp_streams) { |
||
270 | av_log(s, AV_LOG_ERROR, "Unable to find requested track\n"); |
||
271 | return AVERROR_STREAM_NOT_FOUND; |
||
272 | } |
||
273 | rtsp_st = rt->rtsp_streams[streamid]; |
||
274 | localport = rt->rtp_port_min; |
||
275 | |||
276 | if (request.transports[0].lower_transport == RTSP_LOWER_TRANSPORT_TCP) { |
||
277 | rt->lower_transport = RTSP_LOWER_TRANSPORT_TCP; |
||
278 | if ((ret = ff_rtsp_open_transport_ctx(s, rtsp_st))) { |
||
279 | rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq); |
||
280 | return ret; |
||
281 | } |
||
282 | rtsp_st->interleaved_min = request.transports[0].interleaved_min; |
||
283 | rtsp_st->interleaved_max = request.transports[0].interleaved_max; |
||
284 | snprintf(responseheaders, sizeof(responseheaders), "Transport: " |
||
285 | "RTP/AVP/TCP;unicast;mode=receive;interleaved=%d-%d" |
||
286 | "\r\n", request.transports[0].interleaved_min, |
||
287 | request.transports[0].interleaved_max); |
||
288 | } else { |
||
289 | do { |
||
290 | ff_url_join(url, sizeof(url), "rtp", NULL, host, localport, NULL); |
||
291 | av_dlog(s, "Opening: %s", url); |
||
292 | ret = ffurl_open(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE, |
||
293 | &s->interrupt_callback, NULL); |
||
294 | if (ret) |
||
295 | localport += 2; |
||
296 | } while (ret || localport > rt->rtp_port_max); |
||
297 | if (localport > rt->rtp_port_max) { |
||
298 | rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq); |
||
299 | return ret; |
||
300 | } |
||
301 | |||
302 | av_dlog(s, "Listening on: %d", |
||
303 | ff_rtp_get_local_rtp_port(rtsp_st->rtp_handle)); |
||
304 | if ((ret = ff_rtsp_open_transport_ctx(s, rtsp_st))) { |
||
305 | rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq); |
||
306 | return ret; |
||
307 | } |
||
308 | |||
309 | localport = ff_rtp_get_local_rtp_port(rtsp_st->rtp_handle); |
||
310 | snprintf(responseheaders, sizeof(responseheaders), "Transport: " |
||
311 | "RTP/AVP/UDP;unicast;mode=receive;source=%s;" |
||
312 | "client_port=%d-%d;server_port=%d-%d\r\n", |
||
313 | host, request.transports[0].client_port_min, |
||
314 | request.transports[0].client_port_max, localport, |
||
315 | localport + 1); |
||
316 | } |
||
317 | |||
318 | /* Establish sessionid if not previously set */ |
||
319 | /* Put this in a function? */ |
||
320 | /* RFC 2326: session id must be at least 8 digits */ |
||
321 | while (strlen(rt->session_id) < 8) |
||
322 | av_strlcatf(rt->session_id, 512, "%u", av_get_random_seed()); |
||
323 | |||
324 | av_strlcatf(responseheaders, sizeof(responseheaders), "Session: %s\r\n", |
||
325 | rt->session_id); |
||
326 | /* Send Reply */ |
||
327 | rtsp_send_reply(s, RTSP_STATUS_OK, responseheaders, request.seq); |
||
328 | |||
329 | rt->state = RTSP_STATE_PAUSED; |
||
330 | return 0; |
||
331 | } |
||
332 | |||
333 | static int rtsp_read_record(AVFormatContext *s) |
||
334 | { |
||
335 | RTSPState *rt = s->priv_data; |
||
336 | RTSPMessageHeader request = { 0 }; |
||
337 | int ret = 0; |
||
338 | char responseheaders[1024]; |
||
339 | |||
340 | ret = rtsp_read_request(s, &request, "RECORD"); |
||
341 | if (ret) |
||
342 | return ret; |
||
343 | ret = check_sessionid(s, &request); |
||
344 | if (ret) |
||
345 | return ret; |
||
346 | rt->seq++; |
||
347 | snprintf(responseheaders, sizeof(responseheaders), "Session: %s\r\n", |
||
348 | rt->session_id); |
||
349 | rtsp_send_reply(s, RTSP_STATUS_OK, responseheaders, request.seq); |
||
350 | |||
351 | rt->state = RTSP_STATE_STREAMING; |
||
352 | return 0; |
||
353 | } |
||
354 | |||
355 | static inline int parse_command_line(AVFormatContext *s, const char *line, |
||
356 | int linelen, char *uri, int urisize, |
||
357 | char *method, int methodsize, |
||
358 | enum RTSPMethod *methodcode) |
||
359 | { |
||
360 | RTSPState *rt = s->priv_data; |
||
361 | const char *linept, *searchlinept; |
||
362 | linept = strchr(line, ' '); |
||
363 | if (linept - line > methodsize - 1) { |
||
364 | av_log(s, AV_LOG_ERROR, "Method string too long\n"); |
||
365 | return AVERROR(EIO); |
||
366 | } |
||
367 | memcpy(method, line, linept - line); |
||
368 | method[linept - line] = '\0'; |
||
369 | linept++; |
||
370 | if (!strcmp(method, "ANNOUNCE")) |
||
371 | *methodcode = ANNOUNCE; |
||
372 | else if (!strcmp(method, "OPTIONS")) |
||
373 | *methodcode = OPTIONS; |
||
374 | else if (!strcmp(method, "RECORD")) |
||
375 | *methodcode = RECORD; |
||
376 | else if (!strcmp(method, "SETUP")) |
||
377 | *methodcode = SETUP; |
||
378 | else if (!strcmp(method, "PAUSE")) |
||
379 | *methodcode = PAUSE; |
||
380 | else if (!strcmp(method, "TEARDOWN")) |
||
381 | *methodcode = TEARDOWN; |
||
382 | else |
||
383 | *methodcode = UNKNOWN; |
||
384 | /* Check method with the state */ |
||
385 | if (rt->state == RTSP_STATE_IDLE) { |
||
386 | if ((*methodcode != ANNOUNCE) && (*methodcode != OPTIONS)) { |
||
387 | av_log(s, AV_LOG_ERROR, "Unexpected command in Idle State %s\n", |
||
388 | line); |
||
389 | return AVERROR_PROTOCOL_NOT_FOUND; |
||
390 | } |
||
391 | } else if (rt->state == RTSP_STATE_PAUSED) { |
||
392 | if ((*methodcode != OPTIONS) && (*methodcode != RECORD) |
||
393 | && (*methodcode != SETUP)) { |
||
394 | av_log(s, AV_LOG_ERROR, "Unexpected command in Paused State %s\n", |
||
395 | line); |
||
396 | return AVERROR_PROTOCOL_NOT_FOUND; |
||
397 | } |
||
398 | } else if (rt->state == RTSP_STATE_STREAMING) { |
||
399 | if ((*methodcode != PAUSE) && (*methodcode != OPTIONS) |
||
400 | && (*methodcode != TEARDOWN)) { |
||
401 | av_log(s, AV_LOG_ERROR, "Unexpected command in Streaming State" |
||
402 | " %s\n", line); |
||
403 | return AVERROR_PROTOCOL_NOT_FOUND; |
||
404 | } |
||
405 | } else { |
||
406 | av_log(s, AV_LOG_ERROR, "Unexpected State [%d]\n", rt->state); |
||
407 | return AVERROR_BUG; |
||
408 | } |
||
409 | |||
410 | searchlinept = strchr(linept, ' '); |
||
411 | if (searchlinept == NULL) { |
||
412 | av_log(s, AV_LOG_ERROR, "Error parsing message URI\n"); |
||
413 | return AVERROR_INVALIDDATA; |
||
414 | } |
||
415 | if (searchlinept - linept > urisize - 1) { |
||
416 | av_log(s, AV_LOG_ERROR, "uri string length exceeded buffer size\n"); |
||
417 | return AVERROR(EIO); |
||
418 | } |
||
419 | memcpy(uri, linept, searchlinept - linept); |
||
420 | uri[searchlinept - linept] = '\0'; |
||
421 | if (strcmp(rt->control_uri, uri)) { |
||
422 | char host[128], path[512], auth[128]; |
||
423 | int port; |
||
424 | char ctl_host[128], ctl_path[512], ctl_auth[128]; |
||
425 | int ctl_port; |
||
426 | av_url_split(NULL, 0, auth, sizeof(auth), host, sizeof(host), &port, |
||
427 | path, sizeof(path), uri); |
||
428 | av_url_split(NULL, 0, ctl_auth, sizeof(ctl_auth), ctl_host, |
||
429 | sizeof(ctl_host), &ctl_port, ctl_path, sizeof(ctl_path), |
||
430 | rt->control_uri); |
||
431 | if (strcmp(host, ctl_host)) |
||
432 | av_log(s, AV_LOG_INFO, "Host %s differs from expected %s\n", |
||
433 | host, ctl_host); |
||
434 | if (strcmp(path, ctl_path) && *methodcode != SETUP) |
||
435 | av_log(s, AV_LOG_WARNING, "WARNING: Path %s differs from expected" |
||
436 | " %s\n", path, ctl_path); |
||
437 | if (*methodcode == ANNOUNCE) { |
||
438 | av_log(s, AV_LOG_INFO, |
||
439 | "Updating control URI to %s\n", uri); |
||
440 | av_strlcpy(rt->control_uri, uri, sizeof(rt->control_uri)); |
||
441 | } |
||
442 | } |
||
443 | |||
444 | linept = searchlinept + 1; |
||
445 | if (!av_strstart(linept, "RTSP/1.0", NULL)) { |
||
446 | av_log(s, AV_LOG_ERROR, "Error parsing protocol or version\n"); |
||
447 | return AVERROR_PROTOCOL_NOT_FOUND; |
||
448 | } |
||
449 | return 0; |
||
450 | } |
||
451 | |||
452 | int ff_rtsp_parse_streaming_commands(AVFormatContext *s) |
||
453 | { |
||
454 | RTSPState *rt = s->priv_data; |
||
455 | unsigned char rbuf[4096]; |
||
456 | unsigned char method[10]; |
||
457 | char uri[500]; |
||
458 | int ret; |
||
459 | int rbuflen = 0; |
||
460 | RTSPMessageHeader request = { 0 }; |
||
461 | enum RTSPMethod methodcode; |
||
462 | |||
463 | ret = read_line(s, rbuf, sizeof(rbuf), &rbuflen); |
||
464 | if (ret < 0) |
||
465 | return ret; |
||
466 | ret = parse_command_line(s, rbuf, rbuflen, uri, sizeof(uri), method, |
||
467 | sizeof(method), &methodcode); |
||
468 | if (ret) { |
||
469 | av_log(s, AV_LOG_ERROR, "RTSP: Unexpected Command\n"); |
||
470 | return ret; |
||
471 | } |
||
472 | |||
473 | ret = rtsp_read_request(s, &request, method); |
||
474 | if (ret) |
||
475 | return ret; |
||
476 | rt->seq++; |
||
477 | if (methodcode == PAUSE) { |
||
478 | rt->state = RTSP_STATE_PAUSED; |
||
479 | ret = rtsp_send_reply(s, RTSP_STATUS_OK, NULL , request.seq); |
||
480 | // TODO: Missing date header in response |
||
481 | } else if (methodcode == OPTIONS) { |
||
482 | ret = rtsp_send_reply(s, RTSP_STATUS_OK, |
||
483 | "Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, " |
||
484 | "RECORD\r\n", request.seq); |
||
485 | } else if (methodcode == TEARDOWN) { |
||
486 | rt->state = RTSP_STATE_IDLE; |
||
487 | ret = rtsp_send_reply(s, RTSP_STATUS_OK, NULL , request.seq); |
||
488 | return 0; |
||
489 | } |
||
490 | return ret; |
||
491 | } |
||
492 | |||
493 | static int rtsp_read_play(AVFormatContext *s) |
||
494 | { |
||
495 | RTSPState *rt = s->priv_data; |
||
496 | RTSPMessageHeader reply1, *reply = &reply1; |
||
497 | int i; |
||
498 | char cmd[1024]; |
||
499 | |||
500 | av_log(s, AV_LOG_DEBUG, "hello state=%d\n", rt->state); |
||
501 | rt->nb_byes = 0; |
||
502 | |||
503 | if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { |
||
504 | if (rt->transport == RTSP_TRANSPORT_RTP) { |
||
505 | for (i = 0; i < rt->nb_rtsp_streams; i++) { |
||
506 | RTSPStream *rtsp_st = rt->rtsp_streams[i]; |
||
507 | RTPDemuxContext *rtpctx = rtsp_st->transport_priv; |
||
508 | if (!rtpctx) |
||
509 | continue; |
||
510 | ff_rtp_reset_packet_queue(rtpctx); |
||
511 | rtpctx->last_rtcp_ntp_time = AV_NOPTS_VALUE; |
||
512 | rtpctx->first_rtcp_ntp_time = AV_NOPTS_VALUE; |
||
513 | rtpctx->base_timestamp = 0; |
||
514 | rtpctx->timestamp = 0; |
||
515 | rtpctx->unwrapped_timestamp = 0; |
||
516 | rtpctx->rtcp_ts_offset = 0; |
||
517 | } |
||
518 | } |
||
519 | if (rt->state == RTSP_STATE_PAUSED) { |
||
520 | cmd[0] = 0; |
||
521 | } else { |
||
522 | snprintf(cmd, sizeof(cmd), |
||
523 | "Range: npt=%"PRId64".%03"PRId64"-\r\n", |
||
524 | rt->seek_timestamp / AV_TIME_BASE, |
||
525 | rt->seek_timestamp / (AV_TIME_BASE / 1000) % 1000); |
||
526 | } |
||
527 | ff_rtsp_send_cmd(s, "PLAY", rt->control_uri, cmd, reply, NULL); |
||
528 | if (reply->status_code != RTSP_STATUS_OK) { |
||
529 | return -1; |
||
530 | } |
||
531 | if (rt->transport == RTSP_TRANSPORT_RTP && |
||
532 | reply->range_start != AV_NOPTS_VALUE) { |
||
533 | for (i = 0; i < rt->nb_rtsp_streams; i++) { |
||
534 | RTSPStream *rtsp_st = rt->rtsp_streams[i]; |
||
535 | RTPDemuxContext *rtpctx = rtsp_st->transport_priv; |
||
536 | AVStream *st = NULL; |
||
537 | if (!rtpctx || rtsp_st->stream_index < 0) |
||
538 | continue; |
||
539 | st = s->streams[rtsp_st->stream_index]; |
||
540 | rtpctx->range_start_offset = |
||
541 | av_rescale_q(reply->range_start, AV_TIME_BASE_Q, |
||
542 | st->time_base); |
||
543 | } |
||
544 | } |
||
545 | } |
||
546 | rt->state = RTSP_STATE_STREAMING; |
||
547 | return 0; |
||
548 | } |
||
549 | |||
550 | /* pause the stream */ |
||
551 | static int rtsp_read_pause(AVFormatContext *s) |
||
552 | { |
||
553 | RTSPState *rt = s->priv_data; |
||
554 | RTSPMessageHeader reply1, *reply = &reply1; |
||
555 | |||
556 | if (rt->state != RTSP_STATE_STREAMING) |
||
557 | return 0; |
||
558 | else if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { |
||
559 | ff_rtsp_send_cmd(s, "PAUSE", rt->control_uri, NULL, reply, NULL); |
||
560 | if (reply->status_code != RTSP_STATUS_OK) { |
||
561 | return -1; |
||
562 | } |
||
563 | } |
||
564 | rt->state = RTSP_STATE_PAUSED; |
||
565 | return 0; |
||
566 | } |
||
567 | |||
568 | int ff_rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply) |
||
569 | { |
||
570 | RTSPState *rt = s->priv_data; |
||
571 | char cmd[1024]; |
||
572 | unsigned char *content = NULL; |
||
573 | int ret; |
||
574 | |||
575 | /* describe the stream */ |
||
576 | snprintf(cmd, sizeof(cmd), |
||
577 | "Accept: application/sdp\r\n"); |
||
578 | if (rt->server_type == RTSP_SERVER_REAL) { |
||
579 | /** |
||
580 | * The Require: attribute is needed for proper streaming from |
||
581 | * Realmedia servers. |
||
582 | */ |
||
583 | av_strlcat(cmd, |
||
584 | "Require: com.real.retain-entity-for-setup\r\n", |
||
585 | sizeof(cmd)); |
||
586 | } |
||
587 | ff_rtsp_send_cmd(s, "DESCRIBE", rt->control_uri, cmd, reply, &content); |
||
588 | if (!content) |
||
589 | return AVERROR_INVALIDDATA; |
||
590 | if (reply->status_code != RTSP_STATUS_OK) { |
||
591 | av_freep(&content); |
||
592 | return AVERROR_INVALIDDATA; |
||
593 | } |
||
594 | |||
595 | av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", content); |
||
596 | /* now we got the SDP description, we parse it */ |
||
597 | ret = ff_sdp_parse(s, (const char *)content); |
||
598 | av_freep(&content); |
||
599 | if (ret < 0) |
||
600 | return ret; |
||
601 | |||
602 | return 0; |
||
603 | } |
||
604 | |||
605 | static int rtsp_listen(AVFormatContext *s) |
||
606 | { |
||
607 | RTSPState *rt = s->priv_data; |
||
608 | char host[128], path[512], auth[128]; |
||
609 | char uri[500]; |
||
610 | int port; |
||
611 | char tcpname[500]; |
||
612 | unsigned char rbuf[4096]; |
||
613 | unsigned char method[10]; |
||
614 | int rbuflen = 0; |
||
615 | int ret; |
||
616 | enum RTSPMethod methodcode; |
||
617 | |||
618 | /* extract hostname and port */ |
||
619 | av_url_split(NULL, 0, auth, sizeof(auth), host, sizeof(host), &port, |
||
620 | path, sizeof(path), s->filename); |
||
621 | |||
622 | /* ff_url_join. No authorization by now (NULL) */ |
||
623 | ff_url_join(rt->control_uri, sizeof(rt->control_uri), "rtsp", NULL, host, |
||
624 | port, "%s", path); |
||
625 | |||
626 | if (port < 0) |
||
627 | port = RTSP_DEFAULT_PORT; |
||
628 | |||
629 | /* Create TCP connection */ |
||
630 | ff_url_join(tcpname, sizeof(tcpname), "tcp", NULL, host, port, |
||
631 | "?listen&listen_timeout=%d", rt->initial_timeout * 1000); |
||
632 | |||
633 | if (ret = ffurl_open(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE, |
||
634 | &s->interrupt_callback, NULL)) { |
||
635 | av_log(s, AV_LOG_ERROR, "Unable to open RTSP for listening\n"); |
||
636 | return ret; |
||
637 | } |
||
638 | rt->state = RTSP_STATE_IDLE; |
||
639 | rt->rtsp_hd_out = rt->rtsp_hd; |
||
640 | for (;;) { /* Wait for incoming RTSP messages */ |
||
641 | ret = read_line(s, rbuf, sizeof(rbuf), &rbuflen); |
||
642 | if (ret < 0) |
||
643 | return ret; |
||
644 | ret = parse_command_line(s, rbuf, rbuflen, uri, sizeof(uri), method, |
||
645 | sizeof(method), &methodcode); |
||
646 | if (ret) { |
||
647 | av_log(s, AV_LOG_ERROR, "RTSP: Unexpected Command\n"); |
||
648 | return ret; |
||
649 | } |
||
650 | |||
651 | if (methodcode == ANNOUNCE) { |
||
652 | ret = rtsp_read_announce(s); |
||
653 | rt->state = RTSP_STATE_PAUSED; |
||
654 | } else if (methodcode == OPTIONS) { |
||
655 | ret = rtsp_read_options(s); |
||
656 | } else if (methodcode == RECORD) { |
||
657 | ret = rtsp_read_record(s); |
||
658 | if (!ret) |
||
659 | return 0; // We are ready for streaming |
||
660 | } else if (methodcode == SETUP) |
||
661 | ret = rtsp_read_setup(s, host, uri); |
||
662 | if (ret) { |
||
663 | ffurl_close(rt->rtsp_hd); |
||
664 | return AVERROR_INVALIDDATA; |
||
665 | } |
||
666 | } |
||
667 | return 0; |
||
668 | } |
||
669 | |||
670 | static int rtsp_probe(AVProbeData *p) |
||
671 | { |
||
672 | if (av_strstart(p->filename, "rtsp:", NULL)) |
||
673 | return AVPROBE_SCORE_MAX; |
||
674 | return 0; |
||
675 | } |
||
676 | |||
677 | static int rtsp_read_header(AVFormatContext *s) |
||
678 | { |
||
679 | RTSPState *rt = s->priv_data; |
||
680 | int ret; |
||
681 | |||
682 | if (rt->initial_timeout > 0) |
||
683 | rt->rtsp_flags |= RTSP_FLAG_LISTEN; |
||
684 | |||
685 | if (rt->rtsp_flags & RTSP_FLAG_LISTEN) { |
||
686 | ret = rtsp_listen(s); |
||
687 | if (ret) |
||
688 | return ret; |
||
689 | } else { |
||
690 | ret = ff_rtsp_connect(s); |
||
691 | if (ret) |
||
692 | return ret; |
||
693 | |||
694 | rt->real_setup_cache = !s->nb_streams ? NULL : |
||
695 | av_mallocz(2 * s->nb_streams * sizeof(*rt->real_setup_cache)); |
||
696 | if (!rt->real_setup_cache && s->nb_streams) |
||
697 | return AVERROR(ENOMEM); |
||
698 | rt->real_setup = rt->real_setup_cache + s->nb_streams; |
||
699 | |||
700 | if (rt->initial_pause) { |
||
701 | /* do not start immediately */ |
||
702 | } else { |
||
703 | if (rtsp_read_play(s) < 0) { |
||
704 | ff_rtsp_close_streams(s); |
||
705 | ff_rtsp_close_connections(s); |
||
706 | return AVERROR_INVALIDDATA; |
||
707 | } |
||
708 | } |
||
709 | } |
||
710 | |||
711 | return 0; |
||
712 | } |
||
713 | |||
714 | int ff_rtsp_tcp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, |
||
715 | uint8_t *buf, int buf_size) |
||
716 | { |
||
717 | RTSPState *rt = s->priv_data; |
||
718 | int id, len, i, ret; |
||
719 | RTSPStream *rtsp_st; |
||
720 | |||
721 | av_dlog(s, "tcp_read_packet:\n"); |
||
722 | redo: |
||
723 | for (;;) { |
||
724 | RTSPMessageHeader reply; |
||
725 | |||
726 | ret = ff_rtsp_read_reply(s, &reply, NULL, 1, NULL); |
||
727 | if (ret < 0) |
||
728 | return ret; |
||
729 | if (ret == 1) /* received '$' */ |
||
730 | break; |
||
731 | /* XXX: parse message */ |
||
732 | if (rt->state != RTSP_STATE_STREAMING) |
||
733 | return 0; |
||
734 | } |
||
735 | ret = ffurl_read_complete(rt->rtsp_hd, buf, 3); |
||
736 | if (ret != 3) |
||
737 | return -1; |
||
738 | id = buf[0]; |
||
739 | len = AV_RB16(buf + 1); |
||
740 | av_dlog(s, "id=%d len=%d\n", id, len); |
||
741 | if (len > buf_size || len < 8) |
||
742 | goto redo; |
||
743 | /* get the data */ |
||
744 | ret = ffurl_read_complete(rt->rtsp_hd, buf, len); |
||
745 | if (ret != len) |
||
746 | return -1; |
||
747 | if (rt->transport == RTSP_TRANSPORT_RDT && |
||
748 | ff_rdt_parse_header(buf, len, &id, NULL, NULL, NULL, NULL) < 0) |
||
749 | return -1; |
||
750 | |||
751 | /* find the matching stream */ |
||
752 | for (i = 0; i < rt->nb_rtsp_streams; i++) { |
||
753 | rtsp_st = rt->rtsp_streams[i]; |
||
754 | if (id >= rtsp_st->interleaved_min && |
||
755 | id <= rtsp_st->interleaved_max) |
||
756 | goto found; |
||
757 | } |
||
758 | goto redo; |
||
759 | found: |
||
760 | *prtsp_st = rtsp_st; |
||
761 | return len; |
||
762 | } |
||
763 | |||
764 | static int resetup_tcp(AVFormatContext *s) |
||
765 | { |
||
766 | RTSPState *rt = s->priv_data; |
||
767 | char host[1024]; |
||
768 | int port; |
||
769 | |||
770 | av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, NULL, 0, |
||
771 | s->filename); |
||
772 | ff_rtsp_undo_setup(s); |
||
773 | return ff_rtsp_make_setup_request(s, host, port, RTSP_LOWER_TRANSPORT_TCP, |
||
774 | rt->real_challenge); |
||
775 | } |
||
776 | |||
777 | static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) |
||
778 | { |
||
779 | RTSPState *rt = s->priv_data; |
||
780 | int ret; |
||
781 | RTSPMessageHeader reply1, *reply = &reply1; |
||
782 | char cmd[1024]; |
||
783 | |||
784 | retry: |
||
785 | if (rt->server_type == RTSP_SERVER_REAL) { |
||
786 | int i; |
||
787 | |||
788 | for (i = 0; i < s->nb_streams; i++) |
||
789 | rt->real_setup[i] = s->streams[i]->discard; |
||
790 | |||
791 | if (!rt->need_subscription) { |
||
792 | if (memcmp (rt->real_setup, rt->real_setup_cache, |
||
793 | sizeof(enum AVDiscard) * s->nb_streams)) { |
||
794 | snprintf(cmd, sizeof(cmd), |
||
795 | "Unsubscribe: %s\r\n", |
||
796 | rt->last_subscription); |
||
797 | ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri, |
||
798 | cmd, reply, NULL); |
||
799 | if (reply->status_code != RTSP_STATUS_OK) |
||
800 | return AVERROR_INVALIDDATA; |
||
801 | rt->need_subscription = 1; |
||
802 | } |
||
803 | } |
||
804 | |||
805 | if (rt->need_subscription) { |
||
806 | int r, rule_nr, first = 1; |
||
807 | |||
808 | memcpy(rt->real_setup_cache, rt->real_setup, |
||
809 | sizeof(enum AVDiscard) * s->nb_streams); |
||
810 | rt->last_subscription[0] = 0; |
||
811 | |||
812 | snprintf(cmd, sizeof(cmd), |
||
813 | "Subscribe: "); |
||
814 | for (i = 0; i < rt->nb_rtsp_streams; i++) { |
||
815 | rule_nr = 0; |
||
816 | for (r = 0; r < s->nb_streams; r++) { |
||
817 | if (s->streams[r]->id == i) { |
||
818 | if (s->streams[r]->discard != AVDISCARD_ALL) { |
||
819 | if (!first) |
||
820 | av_strlcat(rt->last_subscription, ",", |
||
821 | sizeof(rt->last_subscription)); |
||
822 | ff_rdt_subscribe_rule( |
||
823 | rt->last_subscription, |
||
824 | sizeof(rt->last_subscription), i, rule_nr); |
||
825 | first = 0; |
||
826 | } |
||
827 | rule_nr++; |
||
828 | } |
||
829 | } |
||
830 | } |
||
831 | av_strlcatf(cmd, sizeof(cmd), "%s\r\n", rt->last_subscription); |
||
832 | ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri, |
||
833 | cmd, reply, NULL); |
||
834 | if (reply->status_code != RTSP_STATUS_OK) |
||
835 | return AVERROR_INVALIDDATA; |
||
836 | rt->need_subscription = 0; |
||
837 | |||
838 | if (rt->state == RTSP_STATE_STREAMING) |
||
839 | rtsp_read_play (s); |
||
840 | } |
||
841 | } |
||
842 | |||
843 | ret = ff_rtsp_fetch_packet(s, pkt); |
||
844 | if (ret < 0) { |
||
845 | if (ret == AVERROR(ETIMEDOUT) && !rt->packets) { |
||
846 | if (rt->lower_transport == RTSP_LOWER_TRANSPORT_UDP && |
||
847 | rt->lower_transport_mask & (1 << RTSP_LOWER_TRANSPORT_TCP)) { |
||
848 | RTSPMessageHeader reply1, *reply = &reply1; |
||
849 | av_log(s, AV_LOG_WARNING, "UDP timeout, retrying with TCP\n"); |
||
850 | if (rtsp_read_pause(s) != 0) |
||
851 | return -1; |
||
852 | // TEARDOWN is required on Real-RTSP, but might make |
||
853 | // other servers close the connection. |
||
854 | if (rt->server_type == RTSP_SERVER_REAL) |
||
855 | ff_rtsp_send_cmd(s, "TEARDOWN", rt->control_uri, NULL, |
||
856 | reply, NULL); |
||
857 | rt->session_id[0] = '\0'; |
||
858 | if (resetup_tcp(s) == 0) { |
||
859 | rt->state = RTSP_STATE_IDLE; |
||
860 | rt->need_subscription = 1; |
||
861 | if (rtsp_read_play(s) != 0) |
||
862 | return -1; |
||
863 | goto retry; |
||
864 | } |
||
865 | } |
||
866 | } |
||
867 | return ret; |
||
868 | } |
||
869 | rt->packets++; |
||
870 | |||
871 | if (!(rt->rtsp_flags & RTSP_FLAG_LISTEN)) { |
||
872 | /* send dummy request to keep TCP connection alive */ |
||
873 | if ((av_gettime() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2 || |
||
874 | rt->auth_state.stale) { |
||
875 | if (rt->server_type == RTSP_SERVER_WMS || |
||
876 | (rt->server_type != RTSP_SERVER_REAL && |
||
877 | rt->get_parameter_supported)) { |
||
878 | ff_rtsp_send_cmd_async(s, "GET_PARAMETER", rt->control_uri, NULL); |
||
879 | } else { |
||
880 | ff_rtsp_send_cmd_async(s, "OPTIONS", "*", NULL); |
||
881 | } |
||
882 | /* The stale flag should be reset when creating the auth response in |
||
883 | * ff_rtsp_send_cmd_async, but reset it here just in case we never |
||
884 | * called the auth code (if we didn't have any credentials set). */ |
||
885 | rt->auth_state.stale = 0; |
||
886 | } |
||
887 | } |
||
888 | |||
889 | return 0; |
||
890 | } |
||
891 | |||
892 | static int rtsp_read_seek(AVFormatContext *s, int stream_index, |
||
893 | int64_t timestamp, int flags) |
||
894 | { |
||
895 | RTSPState *rt = s->priv_data; |
||
896 | |||
897 | rt->seek_timestamp = av_rescale_q(timestamp, |
||
898 | s->streams[stream_index]->time_base, |
||
899 | AV_TIME_BASE_Q); |
||
900 | switch(rt->state) { |
||
901 | default: |
||
902 | case RTSP_STATE_IDLE: |
||
903 | break; |
||
904 | case RTSP_STATE_STREAMING: |
||
905 | if (rtsp_read_pause(s) != 0) |
||
906 | return -1; |
||
907 | rt->state = RTSP_STATE_SEEKING; |
||
908 | if (rtsp_read_play(s) != 0) |
||
909 | return -1; |
||
910 | break; |
||
911 | case RTSP_STATE_PAUSED: |
||
912 | rt->state = RTSP_STATE_IDLE; |
||
913 | break; |
||
914 | } |
||
915 | return 0; |
||
916 | } |
||
917 | |||
918 | static const AVClass rtsp_demuxer_class = { |
||
919 | .class_name = "RTSP demuxer", |
||
920 | .item_name = av_default_item_name, |
||
921 | .option = ff_rtsp_options, |
||
922 | .version = LIBAVUTIL_VERSION_INT, |
||
923 | }; |
||
924 | |||
925 | AVInputFormat ff_rtsp_demuxer = { |
||
926 | .name = "rtsp", |
||
927 | .long_name = NULL_IF_CONFIG_SMALL("RTSP input"), |
||
928 | .priv_data_size = sizeof(RTSPState), |
||
929 | .read_probe = rtsp_probe, |
||
930 | .read_header = rtsp_read_header, |
||
931 | .read_packet = rtsp_read_packet, |
||
932 | .read_close = rtsp_read_close, |
||
933 | .read_seek = rtsp_read_seek, |
||
934 | .flags = AVFMT_NOFILE, |
||
935 | .read_play = rtsp_read_play, |
||
936 | .read_pause = rtsp_read_pause, |
||
937 | .priv_class = &rtsp_demuxer_class, |
||
938 | };><>>>>>=>>>>>>>>>>>>>>>>>>>=> |