/src/ffmpeg/libavformat/rtspdec.c
Line | Count | Source |
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 "config_components.h" |
23 | | |
24 | | #include "libavutil/avstring.h" |
25 | | #include "libavutil/avassert.h" |
26 | | #include "libavutil/intreadwrite.h" |
27 | | #include "libavutil/mathematics.h" |
28 | | #include "libavutil/mem.h" |
29 | | #include "libavutil/random_seed.h" |
30 | | #include "libavutil/time.h" |
31 | | #include "avformat.h" |
32 | | #include "demux.h" |
33 | | |
34 | | #include "internal.h" |
35 | | #include "network.h" |
36 | | #include "os_support.h" |
37 | | #include "rtpproto.h" |
38 | | #include "rtsp.h" |
39 | | #include "rdt.h" |
40 | | #include "tls.h" |
41 | | #include "url.h" |
42 | | #include "version.h" |
43 | | |
44 | | static const struct RTSPStatusMessage { |
45 | | enum RTSPStatusCode code; |
46 | | const char *message; |
47 | | } status_messages[] = { |
48 | | { RTSP_STATUS_OK, "OK" }, |
49 | | { RTSP_STATUS_METHOD, "Method Not Allowed" }, |
50 | | { RTSP_STATUS_BANDWIDTH, "Not Enough Bandwidth" }, |
51 | | { RTSP_STATUS_SESSION, "Session Not Found" }, |
52 | | { RTSP_STATUS_STATE, "Method Not Valid in This State" }, |
53 | | { RTSP_STATUS_AGGREGATE, "Aggregate operation not allowed" }, |
54 | | { RTSP_STATUS_ONLY_AGGREGATE, "Only aggregate operation allowed" }, |
55 | | { RTSP_STATUS_TRANSPORT, "Unsupported transport" }, |
56 | | { RTSP_STATUS_INTERNAL, "Internal Server Error" }, |
57 | | { RTSP_STATUS_SERVICE, "Service Unavailable" }, |
58 | | { RTSP_STATUS_VERSION, "RTSP Version not supported" }, |
59 | | { 0, "NULL" } |
60 | | }; |
61 | | |
62 | | static int rtsp_read_close(AVFormatContext *s) |
63 | 0 | { |
64 | 0 | RTSPState *rt = s->priv_data; |
65 | |
|
66 | 0 | if (!(rt->rtsp_flags & RTSP_FLAG_LISTEN)) |
67 | 0 | ff_rtsp_send_cmd_async(s, "TEARDOWN", rt->control_uri, NULL); |
68 | |
|
69 | 0 | ff_rtsp_close_streams(s); |
70 | 0 | ff_rtsp_close_connections(s); |
71 | 0 | ff_network_close(); |
72 | 0 | rt->real_setup = NULL; |
73 | 0 | av_freep(&rt->real_setup_cache); |
74 | 0 | return 0; |
75 | 0 | } |
76 | | |
77 | | static inline int read_line(AVFormatContext *s, char *rbuf, const int rbufsize, |
78 | | int *rbuflen) |
79 | 0 | { |
80 | 0 | RTSPState *rt = s->priv_data; |
81 | 0 | int idx = 0; |
82 | 0 | int ret = 0; |
83 | 0 | *rbuflen = 0; |
84 | |
|
85 | 0 | do { |
86 | 0 | ret = ffurl_read_complete(rt->rtsp_hd, rbuf + idx, 1); |
87 | 0 | if (ret <= 0) |
88 | 0 | return ret ? ret : AVERROR_EOF; |
89 | 0 | if (rbuf[idx] == '\r') { |
90 | | /* Ignore */ |
91 | 0 | } else if (rbuf[idx] == '\n') { |
92 | 0 | rbuf[idx] = '\0'; |
93 | 0 | *rbuflen = idx; |
94 | 0 | return 0; |
95 | 0 | } else |
96 | 0 | idx++; |
97 | 0 | } while (idx < rbufsize); |
98 | 0 | av_log(s, AV_LOG_ERROR, "Message too long\n"); |
99 | 0 | return AVERROR(EIO); |
100 | 0 | } |
101 | | |
102 | | static int rtsp_send_reply(AVFormatContext *s, enum RTSPStatusCode code, |
103 | | const char *extracontent, uint16_t seq) |
104 | 0 | { |
105 | 0 | RTSPState *rt = s->priv_data; |
106 | 0 | char message[MAX_URL_SIZE]; |
107 | 0 | int index = 0; |
108 | 0 | while (status_messages[index].code) { |
109 | 0 | if (status_messages[index].code == code) { |
110 | 0 | snprintf(message, sizeof(message), "RTSP/1.0 %d %s\r\n", |
111 | 0 | code, status_messages[index].message); |
112 | 0 | break; |
113 | 0 | } |
114 | 0 | index++; |
115 | 0 | } |
116 | 0 | if (!status_messages[index].code) |
117 | 0 | return AVERROR(EINVAL); |
118 | 0 | av_strlcatf(message, sizeof(message), "CSeq: %d\r\n", seq); |
119 | 0 | av_strlcatf(message, sizeof(message), "Server: %s\r\n", LIBAVFORMAT_IDENT); |
120 | 0 | if (extracontent) |
121 | 0 | av_strlcat(message, extracontent, sizeof(message)); |
122 | 0 | av_strlcat(message, "\r\n", sizeof(message)); |
123 | 0 | av_log(s, AV_LOG_TRACE, "Sending response:\n%s", message); |
124 | 0 | ffurl_write(rt->rtsp_hd_out, message, strlen(message)); |
125 | |
|
126 | 0 | return 0; |
127 | 0 | } |
128 | | |
129 | | static inline int check_sessionid(AVFormatContext *s, |
130 | | RTSPMessageHeader *request) |
131 | 0 | { |
132 | 0 | RTSPState *rt = s->priv_data; |
133 | 0 | unsigned char *session_id = rt->session_id; |
134 | 0 | if (!session_id[0]) { |
135 | 0 | av_log(s, AV_LOG_WARNING, "There is no session-id at the moment\n"); |
136 | 0 | return 0; |
137 | 0 | } |
138 | 0 | if (strcmp(session_id, request->session_id)) { |
139 | 0 | av_log(s, AV_LOG_ERROR, "Unexpected session-id %s\n", |
140 | 0 | request->session_id); |
141 | 0 | rtsp_send_reply(s, RTSP_STATUS_SESSION, NULL, request->seq); |
142 | 0 | return AVERROR_STREAM_NOT_FOUND; |
143 | 0 | } |
144 | 0 | return 0; |
145 | 0 | } |
146 | | |
147 | | static inline int rtsp_read_request(AVFormatContext *s, |
148 | | RTSPMessageHeader *request, |
149 | | const char *method) |
150 | 0 | { |
151 | 0 | RTSPState *rt = s->priv_data; |
152 | 0 | char rbuf[MAX_URL_SIZE]; |
153 | 0 | int rbuflen, ret; |
154 | 0 | do { |
155 | 0 | ret = read_line(s, rbuf, sizeof(rbuf), &rbuflen); |
156 | 0 | if (ret) |
157 | 0 | return ret; |
158 | 0 | if (rbuflen > 1) { |
159 | 0 | av_log(s, AV_LOG_TRACE, "Parsing[%d]: %s\n", rbuflen, rbuf); |
160 | 0 | ff_rtsp_parse_line(s, request, rbuf, rt, method); |
161 | 0 | } |
162 | 0 | } while (rbuflen > 0); |
163 | 0 | if (request->seq != rt->seq + 1) { |
164 | 0 | av_log(s, AV_LOG_ERROR, "Unexpected Sequence number %d\n", |
165 | 0 | request->seq); |
166 | 0 | return AVERROR(EINVAL); |
167 | 0 | } |
168 | 0 | if (rt->session_id[0] && strcmp(method, "OPTIONS")) { |
169 | 0 | ret = check_sessionid(s, request); |
170 | 0 | if (ret) |
171 | 0 | return ret; |
172 | 0 | } |
173 | | |
174 | 0 | return 0; |
175 | 0 | } |
176 | | |
177 | | static int rtsp_read_announce(AVFormatContext *s) |
178 | 0 | { |
179 | 0 | RTSPState *rt = s->priv_data; |
180 | 0 | RTSPMessageHeader request = { 0 }; |
181 | 0 | char *sdp; |
182 | 0 | int ret; |
183 | |
|
184 | 0 | ret = rtsp_read_request(s, &request, "ANNOUNCE"); |
185 | 0 | if (ret) |
186 | 0 | return ret; |
187 | 0 | rt->seq++; |
188 | 0 | if (strcmp(request.content_type, "application/sdp")) { |
189 | 0 | av_log(s, AV_LOG_ERROR, "Unexpected content type %s\n", |
190 | 0 | request.content_type); |
191 | 0 | rtsp_send_reply(s, RTSP_STATUS_SERVICE, NULL, request.seq); |
192 | 0 | return AVERROR_OPTION_NOT_FOUND; |
193 | 0 | } |
194 | 0 | if (request.content_length > 0 && request.content_length <= SDP_MAX_SIZE) { |
195 | 0 | sdp = av_malloc(request.content_length + 1); |
196 | 0 | if (!sdp) |
197 | 0 | return AVERROR(ENOMEM); |
198 | | |
199 | | /* Read SDP */ |
200 | 0 | if (ffurl_read_complete(rt->rtsp_hd, sdp, request.content_length) |
201 | 0 | < request.content_length) { |
202 | 0 | av_log(s, AV_LOG_ERROR, |
203 | 0 | "Unable to get complete SDP Description in ANNOUNCE\n"); |
204 | 0 | rtsp_send_reply(s, RTSP_STATUS_INTERNAL, NULL, request.seq); |
205 | 0 | av_free(sdp); |
206 | 0 | return AVERROR(EIO); |
207 | 0 | } |
208 | 0 | sdp[request.content_length] = '\0'; |
209 | 0 | av_log(s, AV_LOG_VERBOSE, "SDP: %s\n", sdp); |
210 | 0 | ret = ff_sdp_parse(s, sdp); |
211 | 0 | av_free(sdp); |
212 | 0 | if (ret) |
213 | 0 | return ret; |
214 | 0 | rtsp_send_reply(s, RTSP_STATUS_OK, NULL, request.seq); |
215 | 0 | return 0; |
216 | 0 | } |
217 | 0 | av_log(s, AV_LOG_ERROR, |
218 | 0 | "Invalid ANNOUNCE Content-Length %d\n", request.content_length); |
219 | 0 | rtsp_send_reply(s, RTSP_STATUS_INTERNAL, |
220 | 0 | "Invalid Content-Length", request.seq); |
221 | 0 | return AVERROR_INVALIDDATA; |
222 | 0 | } |
223 | | |
224 | | static int rtsp_read_options(AVFormatContext *s) |
225 | 0 | { |
226 | 0 | RTSPState *rt = s->priv_data; |
227 | 0 | RTSPMessageHeader request = { 0 }; |
228 | 0 | int ret = 0; |
229 | | |
230 | | /* Parsing headers */ |
231 | 0 | ret = rtsp_read_request(s, &request, "OPTIONS"); |
232 | 0 | if (ret) |
233 | 0 | return ret; |
234 | 0 | rt->seq++; |
235 | | /* Send Reply */ |
236 | 0 | rtsp_send_reply(s, RTSP_STATUS_OK, |
237 | 0 | "Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, RECORD\r\n", |
238 | 0 | request.seq); |
239 | 0 | return 0; |
240 | 0 | } |
241 | | |
242 | | static int rtsp_read_setup(AVFormatContext *s, char* host, char *controlurl) |
243 | 0 | { |
244 | 0 | RTSPState *rt = s->priv_data; |
245 | 0 | RTSPMessageHeader request = { 0 }; |
246 | 0 | int ret = 0; |
247 | 0 | char url[MAX_URL_SIZE]; |
248 | 0 | RTSPStream *rtsp_st; |
249 | 0 | char responseheaders[MAX_URL_SIZE]; |
250 | 0 | int localport = -1; |
251 | 0 | int transportidx = 0; |
252 | 0 | int streamid = 0; |
253 | |
|
254 | 0 | ret = rtsp_read_request(s, &request, "SETUP"); |
255 | 0 | if (ret) |
256 | 0 | return ret; |
257 | 0 | rt->seq++; |
258 | 0 | if (!request.nb_transports) { |
259 | 0 | av_log(s, AV_LOG_ERROR, "No transport defined in SETUP\n"); |
260 | 0 | return AVERROR_INVALIDDATA; |
261 | 0 | } |
262 | 0 | for (transportidx = 0; transportidx < request.nb_transports; |
263 | 0 | transportidx++) { |
264 | 0 | if (!request.transports[transportidx].mode_record || |
265 | 0 | (request.transports[transportidx].lower_transport != |
266 | 0 | RTSP_LOWER_TRANSPORT_UDP && |
267 | 0 | request.transports[transportidx].lower_transport != |
268 | 0 | RTSP_LOWER_TRANSPORT_TCP)) { |
269 | 0 | av_log(s, AV_LOG_ERROR, "mode=record/receive not set or transport" |
270 | 0 | " protocol not supported (yet)\n"); |
271 | 0 | return AVERROR_INVALIDDATA; |
272 | 0 | } |
273 | 0 | } |
274 | 0 | if (request.nb_transports > 1) |
275 | 0 | av_log(s, AV_LOG_WARNING, "More than one transport not supported, " |
276 | 0 | "using first of all\n"); |
277 | 0 | for (streamid = 0; streamid < rt->nb_rtsp_streams; streamid++) { |
278 | 0 | if (!strcmp(rt->rtsp_streams[streamid]->control_url, |
279 | 0 | controlurl)) |
280 | 0 | break; |
281 | 0 | } |
282 | 0 | if (streamid == rt->nb_rtsp_streams) { |
283 | 0 | av_log(s, AV_LOG_ERROR, "Unable to find requested track\n"); |
284 | 0 | return AVERROR_STREAM_NOT_FOUND; |
285 | 0 | } |
286 | 0 | rtsp_st = rt->rtsp_streams[streamid]; |
287 | 0 | localport = rt->rtp_port_min; |
288 | | |
289 | | /* check if the stream has already been setup */ |
290 | 0 | if (rtsp_st->transport_priv) { |
291 | 0 | if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RDT) |
292 | 0 | ff_rdt_parse_close(rtsp_st->transport_priv); |
293 | 0 | else if (CONFIG_RTPDEC && rt->transport == RTSP_TRANSPORT_RTP) |
294 | 0 | ff_rtp_parse_close(rtsp_st->transport_priv); |
295 | 0 | rtsp_st->transport_priv = NULL; |
296 | 0 | } |
297 | 0 | if (rtsp_st->rtp_handle) |
298 | 0 | ffurl_closep(&rtsp_st->rtp_handle); |
299 | |
|
300 | 0 | if (request.transports[0].lower_transport == RTSP_LOWER_TRANSPORT_TCP) { |
301 | 0 | rt->lower_transport = RTSP_LOWER_TRANSPORT_TCP; |
302 | 0 | if ((ret = ff_rtsp_open_transport_ctx(s, rtsp_st))) { |
303 | 0 | rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq); |
304 | 0 | return ret; |
305 | 0 | } |
306 | 0 | rtsp_st->interleaved_min = request.transports[0].interleaved_min; |
307 | 0 | rtsp_st->interleaved_max = request.transports[0].interleaved_max; |
308 | 0 | snprintf(responseheaders, sizeof(responseheaders), "Transport: " |
309 | 0 | "RTP/AVP/TCP;unicast;mode=record;interleaved=%d-%d" |
310 | 0 | "\r\n", request.transports[0].interleaved_min, |
311 | 0 | request.transports[0].interleaved_max); |
312 | 0 | } else { |
313 | 0 | do { |
314 | 0 | AVDictionary *opts = NULL; |
315 | 0 | av_dict_set_int(&opts, "buffer_size", rt->buffer_size, 0); |
316 | 0 | ff_url_join(url, sizeof(url), "rtp", NULL, host, localport, NULL); |
317 | 0 | av_log(s, AV_LOG_TRACE, "Opening: %s\n", url); |
318 | 0 | ret = ffurl_open_whitelist(&rtsp_st->rtp_handle, url, AVIO_FLAG_READ_WRITE, |
319 | 0 | &s->interrupt_callback, &opts, |
320 | 0 | s->protocol_whitelist, s->protocol_blacklist, NULL); |
321 | 0 | av_dict_free(&opts); |
322 | 0 | if (ret) |
323 | 0 | localport += 2; |
324 | 0 | } while (ret || localport > rt->rtp_port_max); |
325 | 0 | if (localport > rt->rtp_port_max) { |
326 | 0 | rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq); |
327 | 0 | return ret; |
328 | 0 | } |
329 | | |
330 | 0 | av_log(s, AV_LOG_TRACE, "Listening on: %d\n", |
331 | 0 | ff_rtp_get_local_rtp_port(rtsp_st->rtp_handle)); |
332 | 0 | if ((ret = ff_rtsp_open_transport_ctx(s, rtsp_st))) { |
333 | 0 | rtsp_send_reply(s, RTSP_STATUS_TRANSPORT, NULL, request.seq); |
334 | 0 | return ret; |
335 | 0 | } |
336 | | |
337 | 0 | localport = ff_rtp_get_local_rtp_port(rtsp_st->rtp_handle); |
338 | 0 | snprintf(responseheaders, sizeof(responseheaders), "Transport: " |
339 | 0 | "RTP/AVP/UDP;unicast;mode=record;source=%s;" |
340 | 0 | "client_port=%d-%d;server_port=%d-%d\r\n", |
341 | 0 | host, request.transports[0].client_port_min, |
342 | 0 | request.transports[0].client_port_max, localport, |
343 | 0 | localport + 1); |
344 | 0 | } |
345 | | |
346 | | /* Establish sessionid if not previously set */ |
347 | | /* Put this in a function? */ |
348 | | /* RFC 2326: session id must be at least 8 digits */ |
349 | 0 | while (strlen(rt->session_id) < 8) |
350 | 0 | av_strlcatf(rt->session_id, 512, "%u", av_get_random_seed()); |
351 | |
|
352 | 0 | av_strlcatf(responseheaders, sizeof(responseheaders), "Session: %s\r\n", |
353 | 0 | rt->session_id); |
354 | | /* Send Reply */ |
355 | 0 | rtsp_send_reply(s, RTSP_STATUS_OK, responseheaders, request.seq); |
356 | |
|
357 | 0 | rt->state = RTSP_STATE_PAUSED; |
358 | 0 | return 0; |
359 | 0 | } |
360 | | |
361 | | static int rtsp_read_record(AVFormatContext *s) |
362 | 0 | { |
363 | 0 | RTSPState *rt = s->priv_data; |
364 | 0 | RTSPMessageHeader request = { 0 }; |
365 | 0 | int ret = 0; |
366 | 0 | char responseheaders[MAX_URL_SIZE]; |
367 | |
|
368 | 0 | ret = rtsp_read_request(s, &request, "RECORD"); |
369 | 0 | if (ret) |
370 | 0 | return ret; |
371 | 0 | ret = check_sessionid(s, &request); |
372 | 0 | if (ret) |
373 | 0 | return ret; |
374 | 0 | rt->seq++; |
375 | 0 | snprintf(responseheaders, sizeof(responseheaders), "Session: %s\r\n", |
376 | 0 | rt->session_id); |
377 | 0 | rtsp_send_reply(s, RTSP_STATUS_OK, responseheaders, request.seq); |
378 | |
|
379 | 0 | rt->state = RTSP_STATE_STREAMING; |
380 | 0 | return 0; |
381 | 0 | } |
382 | | |
383 | | static inline int parse_command_line(AVFormatContext *s, const char *line, |
384 | | int linelen, char *uri, int urisize, |
385 | | char *method, int methodsize, |
386 | | enum RTSPMethod *methodcode) |
387 | 0 | { |
388 | 0 | RTSPState *rt = s->priv_data; |
389 | 0 | const char *linept, *searchlinept; |
390 | 0 | linept = strchr(line, ' '); |
391 | |
|
392 | 0 | if (!linept) { |
393 | 0 | av_log(s, AV_LOG_ERROR, "Error parsing method string\n"); |
394 | 0 | return AVERROR_INVALIDDATA; |
395 | 0 | } |
396 | | |
397 | 0 | if (linept - line > methodsize - 1) { |
398 | 0 | av_log(s, AV_LOG_ERROR, "Method string too long\n"); |
399 | 0 | return AVERROR(EIO); |
400 | 0 | } |
401 | 0 | memcpy(method, line, linept - line); |
402 | 0 | method[linept - line] = '\0'; |
403 | 0 | linept++; |
404 | 0 | if (!strcmp(method, "ANNOUNCE")) |
405 | 0 | *methodcode = ANNOUNCE; |
406 | 0 | else if (!strcmp(method, "OPTIONS")) |
407 | 0 | *methodcode = OPTIONS; |
408 | 0 | else if (!strcmp(method, "RECORD")) |
409 | 0 | *methodcode = RECORD; |
410 | 0 | else if (!strcmp(method, "SETUP")) |
411 | 0 | *methodcode = SETUP; |
412 | 0 | else if (!strcmp(method, "PAUSE")) |
413 | 0 | *methodcode = PAUSE; |
414 | 0 | else if (!strcmp(method, "TEARDOWN")) |
415 | 0 | *methodcode = TEARDOWN; |
416 | 0 | else |
417 | 0 | *methodcode = UNKNOWN; |
418 | | /* Check method with the state */ |
419 | 0 | if (rt->state == RTSP_STATE_IDLE) { |
420 | 0 | if ((*methodcode != ANNOUNCE) && (*methodcode != OPTIONS)) { |
421 | 0 | av_log(s, AV_LOG_ERROR, "Unexpected command in Idle State %s\n", |
422 | 0 | line); |
423 | 0 | return AVERROR_PROTOCOL_NOT_FOUND; |
424 | 0 | } |
425 | 0 | } else if (rt->state == RTSP_STATE_PAUSED) { |
426 | 0 | if ((*methodcode != OPTIONS) && (*methodcode != RECORD) |
427 | 0 | && (*methodcode != SETUP)) { |
428 | 0 | av_log(s, AV_LOG_ERROR, "Unexpected command in Paused State %s\n", |
429 | 0 | line); |
430 | 0 | return AVERROR_PROTOCOL_NOT_FOUND; |
431 | 0 | } |
432 | 0 | } else if (rt->state == RTSP_STATE_STREAMING) { |
433 | 0 | if ((*methodcode != PAUSE) && (*methodcode != OPTIONS) |
434 | 0 | && (*methodcode != TEARDOWN)) { |
435 | 0 | av_log(s, AV_LOG_ERROR, "Unexpected command in Streaming State" |
436 | 0 | " %s\n", line); |
437 | 0 | return AVERROR_PROTOCOL_NOT_FOUND; |
438 | 0 | } |
439 | 0 | } else { |
440 | 0 | av_log(s, AV_LOG_ERROR, "Unexpected State [%d]\n", rt->state); |
441 | 0 | return AVERROR_BUG; |
442 | 0 | } |
443 | | |
444 | 0 | searchlinept = strchr(linept, ' '); |
445 | 0 | if (!searchlinept) { |
446 | 0 | av_log(s, AV_LOG_ERROR, "Error parsing message URI\n"); |
447 | 0 | return AVERROR_INVALIDDATA; |
448 | 0 | } |
449 | 0 | if (searchlinept - linept > urisize - 1) { |
450 | 0 | av_log(s, AV_LOG_ERROR, "uri string length exceeded buffer size\n"); |
451 | 0 | return AVERROR(EIO); |
452 | 0 | } |
453 | 0 | memcpy(uri, linept, searchlinept - linept); |
454 | 0 | uri[searchlinept - linept] = '\0'; |
455 | 0 | if (strcmp(rt->control_uri, uri)) { |
456 | 0 | char host[128], path[512], auth[128]; |
457 | 0 | int port; |
458 | 0 | char ctl_host[128], ctl_path[512], ctl_auth[128]; |
459 | 0 | int ctl_port; |
460 | 0 | av_url_split(NULL, 0, auth, sizeof(auth), host, sizeof(host), &port, |
461 | 0 | path, sizeof(path), uri); |
462 | 0 | av_url_split(NULL, 0, ctl_auth, sizeof(ctl_auth), ctl_host, |
463 | 0 | sizeof(ctl_host), &ctl_port, ctl_path, sizeof(ctl_path), |
464 | 0 | rt->control_uri); |
465 | 0 | if (strcmp(host, ctl_host)) |
466 | 0 | av_log(s, AV_LOG_INFO, "Host %s differs from expected %s\n", |
467 | 0 | host, ctl_host); |
468 | 0 | if (strcmp(path, ctl_path) && *methodcode != SETUP) |
469 | 0 | av_log(s, AV_LOG_WARNING, "WARNING: Path %s differs from expected" |
470 | 0 | " %s\n", path, ctl_path); |
471 | 0 | if (*methodcode == ANNOUNCE) { |
472 | 0 | av_log(s, AV_LOG_INFO, |
473 | 0 | "Updating control URI to %s\n", uri); |
474 | 0 | av_strlcpy(rt->control_uri, uri, sizeof(rt->control_uri)); |
475 | 0 | } |
476 | 0 | } |
477 | |
|
478 | 0 | linept = searchlinept + 1; |
479 | 0 | if (!av_strstart(linept, "RTSP/1.0", NULL)) { |
480 | 0 | av_log(s, AV_LOG_ERROR, "Error parsing protocol or version\n"); |
481 | 0 | return AVERROR_PROTOCOL_NOT_FOUND; |
482 | 0 | } |
483 | 0 | return 0; |
484 | 0 | } |
485 | | |
486 | | int ff_rtsp_parse_streaming_commands(AVFormatContext *s) |
487 | 0 | { |
488 | 0 | RTSPState *rt = s->priv_data; |
489 | 0 | unsigned char rbuf[MAX_URL_SIZE]; |
490 | 0 | unsigned char method[10]; |
491 | 0 | char uri[500]; |
492 | 0 | int ret; |
493 | 0 | int rbuflen = 0; |
494 | 0 | RTSPMessageHeader request = { 0 }; |
495 | 0 | enum RTSPMethod methodcode; |
496 | |
|
497 | 0 | ret = read_line(s, rbuf, sizeof(rbuf), &rbuflen); |
498 | 0 | if (ret < 0) |
499 | 0 | return ret; |
500 | 0 | av_log(s, AV_LOG_TRACE, "Parsing[%d]: %s\n", rbuflen, rbuf); |
501 | 0 | ret = parse_command_line(s, rbuf, rbuflen, uri, sizeof(uri), method, |
502 | 0 | sizeof(method), &methodcode); |
503 | 0 | if (ret) { |
504 | 0 | av_log(s, AV_LOG_ERROR, "RTSP: Unexpected Command\n"); |
505 | 0 | return ret; |
506 | 0 | } |
507 | | |
508 | 0 | ret = rtsp_read_request(s, &request, method); |
509 | 0 | if (ret) |
510 | 0 | return ret; |
511 | 0 | rt->seq++; |
512 | 0 | if (methodcode == PAUSE) { |
513 | 0 | rt->state = RTSP_STATE_PAUSED; |
514 | 0 | ret = rtsp_send_reply(s, RTSP_STATUS_OK, NULL , request.seq); |
515 | | // TODO: Missing date header in response |
516 | 0 | } else if (methodcode == OPTIONS) { |
517 | 0 | ret = rtsp_send_reply(s, RTSP_STATUS_OK, |
518 | 0 | "Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, " |
519 | 0 | "RECORD\r\n", request.seq); |
520 | 0 | } else if (methodcode == TEARDOWN) { |
521 | 0 | rt->state = RTSP_STATE_IDLE; |
522 | 0 | ret = rtsp_send_reply(s, RTSP_STATUS_OK, NULL , request.seq); |
523 | 0 | } |
524 | 0 | return ret; |
525 | 0 | } |
526 | | |
527 | | static int rtsp_read_play(AVFormatContext *s) |
528 | 0 | { |
529 | 0 | RTSPState *rt = s->priv_data; |
530 | 0 | RTSPMessageHeader reply1, *reply = &reply1; |
531 | 0 | int i; |
532 | 0 | char cmd[MAX_URL_SIZE]; |
533 | |
|
534 | 0 | av_log(s, AV_LOG_DEBUG, "hello state=%d\n", rt->state); |
535 | 0 | rt->nb_byes = 0; |
536 | |
|
537 | 0 | if (rt->lower_transport == RTSP_LOWER_TRANSPORT_UDP) { |
538 | 0 | for (i = 0; i < rt->nb_rtsp_streams; i++) { |
539 | 0 | RTSPStream *rtsp_st = rt->rtsp_streams[i]; |
540 | | /* Try to initialize the connection state in a |
541 | | * potential NAT router by sending dummy packets. |
542 | | * RTP/RTCP dummy packets are used for RDT, too. |
543 | | */ |
544 | 0 | if (rtsp_st->rtp_handle && |
545 | 0 | !(rt->server_type == RTSP_SERVER_WMS && i > 1)) |
546 | 0 | ff_rtp_send_punch_packets(rtsp_st->rtp_handle); |
547 | 0 | } |
548 | 0 | } |
549 | 0 | if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { |
550 | 0 | if (rt->transport == RTSP_TRANSPORT_RTP) { |
551 | 0 | for (i = 0; i < rt->nb_rtsp_streams; i++) { |
552 | 0 | RTSPStream *rtsp_st = rt->rtsp_streams[i]; |
553 | 0 | RTPDemuxContext *rtpctx = rtsp_st->transport_priv; |
554 | 0 | if (!rtpctx) |
555 | 0 | continue; |
556 | 0 | ff_rtp_reset_packet_queue(rtpctx); |
557 | 0 | rtpctx->last_sr.ntp_timestamp = AV_NOPTS_VALUE; |
558 | 0 | rtpctx->first_rtcp_ntp_time = AV_NOPTS_VALUE; |
559 | 0 | rtpctx->base_timestamp = 0; |
560 | 0 | rtpctx->timestamp = 0; |
561 | 0 | rtpctx->unwrapped_timestamp = 0; |
562 | 0 | rtpctx->rtcp_ts_offset = 0; |
563 | 0 | } |
564 | 0 | } |
565 | 0 | if (rt->state == RTSP_STATE_PAUSED) { |
566 | 0 | cmd[0] = 0; |
567 | 0 | } else { |
568 | 0 | snprintf(cmd, sizeof(cmd), |
569 | 0 | "Range: npt=%"PRId64".%03"PRId64"-\r\n", |
570 | 0 | rt->seek_timestamp / AV_TIME_BASE, |
571 | 0 | rt->seek_timestamp / (AV_TIME_BASE / 1000) % 1000); |
572 | 0 | } |
573 | 0 | ff_rtsp_send_cmd(s, "PLAY", rt->control_uri, cmd, reply, NULL); |
574 | 0 | if (reply->status_code != RTSP_STATUS_OK) { |
575 | 0 | return ff_rtsp_averror(reply->status_code, -1); |
576 | 0 | } |
577 | 0 | if (rt->transport == RTSP_TRANSPORT_RTP && |
578 | 0 | reply->range_start != AV_NOPTS_VALUE) { |
579 | 0 | for (i = 0; i < rt->nb_rtsp_streams; i++) { |
580 | 0 | RTSPStream *rtsp_st = rt->rtsp_streams[i]; |
581 | 0 | RTPDemuxContext *rtpctx = rtsp_st->transport_priv; |
582 | 0 | AVStream *st = NULL; |
583 | 0 | if (!rtpctx || rtsp_st->stream_index < 0) |
584 | 0 | continue; |
585 | | |
586 | 0 | st = s->streams[rtsp_st->stream_index]; |
587 | 0 | rtpctx->range_start_offset = |
588 | 0 | av_rescale_q(reply->range_start, AV_TIME_BASE_Q, |
589 | 0 | st->time_base); |
590 | 0 | } |
591 | 0 | } |
592 | 0 | } |
593 | 0 | rt->state = RTSP_STATE_STREAMING; |
594 | 0 | return 0; |
595 | 0 | } |
596 | | |
597 | | /* pause the stream */ |
598 | | static int rtsp_read_pause(AVFormatContext *s) |
599 | 0 | { |
600 | 0 | RTSPState *rt = s->priv_data; |
601 | 0 | RTSPMessageHeader reply1, *reply = &reply1; |
602 | |
|
603 | 0 | if (rt->state != RTSP_STATE_STREAMING) |
604 | 0 | return 0; |
605 | 0 | else if (!(rt->server_type == RTSP_SERVER_REAL && rt->need_subscription)) { |
606 | 0 | ff_rtsp_send_cmd(s, "PAUSE", rt->control_uri, NULL, reply, NULL); |
607 | 0 | if (reply->status_code != RTSP_STATUS_OK) { |
608 | 0 | return ff_rtsp_averror(reply->status_code, -1); |
609 | 0 | } |
610 | 0 | } |
611 | 0 | rt->state = RTSP_STATE_PAUSED; |
612 | 0 | return 0; |
613 | 0 | } |
614 | | |
615 | | static int rtsp_read_set_state(AVFormatContext *s, |
616 | | enum FFInputFormatStreamState state) |
617 | 0 | { |
618 | 0 | switch (state) { |
619 | 0 | case FF_INFMT_STATE_PLAY: |
620 | 0 | return rtsp_read_play(s); |
621 | 0 | case FF_INFMT_STATE_PAUSE: |
622 | 0 | return rtsp_read_pause(s); |
623 | 0 | default: |
624 | 0 | return AVERROR(ENOTSUP); |
625 | 0 | } |
626 | 0 | } |
627 | | |
628 | | static char *dict_to_headers(AVDictionary *headers) |
629 | 0 | { |
630 | 0 | char *buf; |
631 | 0 | AVBPrint bprint; |
632 | 0 | av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED); |
633 | |
|
634 | 0 | const AVDictionaryEntry *header = NULL; |
635 | 0 | while ((header = av_dict_iterate(headers, header))) { |
636 | 0 | av_bprintf(&bprint, "%s: %s\r\n", header->key, header->value); |
637 | 0 | } |
638 | |
|
639 | 0 | av_bprint_finalize(&bprint, &buf); |
640 | 0 | return buf; |
641 | 0 | } |
642 | | |
643 | | static int rtsp_submit_command(struct AVFormatContext *s, enum AVFormatCommandID id, void *data) |
644 | 0 | { |
645 | 0 | if (id != AVFORMAT_COMMAND_RTSP_SET_PARAMETER) |
646 | 0 | return AVERROR(ENOTSUP); |
647 | 0 | if (!data) |
648 | 0 | return AVERROR(EINVAL); |
649 | | |
650 | 0 | RTSPState *rt = s->priv_data; |
651 | 0 | AVRTSPCommandRequest *req = data; |
652 | |
|
653 | 0 | if (rt->state == RTSP_STATE_IDLE) |
654 | | // Not ready to send a SET_PARAMETERS command yet |
655 | 0 | return AVERROR(EAGAIN); |
656 | | |
657 | 0 | av_log(s, AV_LOG_DEBUG, "Sending SET_PARAMETER command to %s\n", rt->control_uri); |
658 | 0 | char *headers = dict_to_headers(req->headers); |
659 | |
|
660 | 0 | int ret = ff_rtsp_send_cmd_with_content_async_stored(s, "SET_PARAMETER", |
661 | 0 | rt->control_uri, headers, req->body, req->body_len); |
662 | 0 | av_free(headers); |
663 | |
|
664 | 0 | if (ret != 0) |
665 | 0 | av_log(s, AV_LOG_ERROR, "Failure sending SET_PARAMETER command: %s\n", av_err2str(ret)); |
666 | |
|
667 | 0 | return ret; |
668 | 0 | } |
669 | | |
670 | | static int rtsp_read_command_reply(AVFormatContext *s, enum AVFormatCommandID id, void **data_out) |
671 | 0 | { |
672 | 0 | if (id != AVFORMAT_COMMAND_RTSP_SET_PARAMETER) |
673 | 0 | return AVERROR(ENOTSUP); |
674 | 0 | if (!data_out) |
675 | 0 | return AVERROR(EINVAL); |
676 | | |
677 | 0 | unsigned char *body; |
678 | 0 | RTSPMessageHeader *reply; |
679 | 0 | int ret = ff_rtsp_read_reply_async_stored(s, &reply, &body); |
680 | 0 | if (ret < 0) |
681 | 0 | return ret; |
682 | | |
683 | 0 | AVRTSPResponse *res = av_malloc(sizeof(*res)); |
684 | 0 | if (!res) { |
685 | 0 | av_free(body); |
686 | 0 | av_free(reply); |
687 | 0 | return AVERROR(ENOMEM); |
688 | 0 | } |
689 | | |
690 | 0 | res->status_code = reply->status_code; |
691 | 0 | res->body_len = reply->content_length; |
692 | 0 | res->body = body; |
693 | |
|
694 | 0 | res->reason = av_strdup(reply->reason); |
695 | 0 | if (!res->reason) { |
696 | 0 | av_free(res->body); |
697 | 0 | av_free(res); |
698 | 0 | av_free(reply); |
699 | 0 | return AVERROR(ENOMEM); |
700 | 0 | } |
701 | | |
702 | 0 | av_free(reply); |
703 | 0 | *data_out = res; |
704 | 0 | return 0; |
705 | 0 | } |
706 | | |
707 | | static int rtsp_handle_command(struct AVFormatContext *s, |
708 | | enum FFInputFormatCommandOption opt, |
709 | | enum AVFormatCommandID id, void *data) |
710 | 0 | { |
711 | 0 | switch (opt) { |
712 | 0 | case FF_INFMT_COMMAND_SUBMIT: |
713 | 0 | return rtsp_submit_command(s, id, data); |
714 | 0 | case FF_INFMT_COMMAND_GET_REPLY: |
715 | 0 | return rtsp_read_command_reply(s, id, data); |
716 | 0 | default: |
717 | 0 | av_unreachable("Invalid command option"); |
718 | 0 | } |
719 | 0 | } |
720 | | |
721 | | int ff_rtsp_setup_input_streams(AVFormatContext *s, RTSPMessageHeader *reply) |
722 | 0 | { |
723 | 0 | RTSPState *rt = s->priv_data; |
724 | 0 | char cmd[MAX_URL_SIZE]; |
725 | 0 | unsigned char *content = NULL; |
726 | 0 | int ret; |
727 | | |
728 | | /* describe the stream */ |
729 | 0 | snprintf(cmd, sizeof(cmd), |
730 | 0 | "Accept: application/sdp\r\n"); |
731 | 0 | if (rt->server_type == RTSP_SERVER_REAL) { |
732 | | /** |
733 | | * The Require: attribute is needed for proper streaming from |
734 | | * Realmedia servers. |
735 | | */ |
736 | 0 | av_strlcat(cmd, |
737 | 0 | "Require: com.real.retain-entity-for-setup\r\n", |
738 | 0 | sizeof(cmd)); |
739 | 0 | } |
740 | 0 | ff_rtsp_send_cmd(s, "DESCRIBE", rt->control_uri, cmd, reply, &content); |
741 | 0 | if (reply->status_code != RTSP_STATUS_OK) { |
742 | 0 | av_freep(&content); |
743 | 0 | return ff_rtsp_averror(reply->status_code, AVERROR_INVALIDDATA); |
744 | 0 | } |
745 | 0 | if (!content) |
746 | 0 | return AVERROR_INVALIDDATA; |
747 | | |
748 | 0 | av_log(s, AV_LOG_VERBOSE, "SDP:\n%s\n", content); |
749 | | /* now we got the SDP description, we parse it */ |
750 | 0 | ret = ff_sdp_parse(s, (const char *)content); |
751 | 0 | av_freep(&content); |
752 | 0 | if (ret < 0) |
753 | 0 | return ret; |
754 | | |
755 | 0 | return 0; |
756 | 0 | } |
757 | | |
758 | | static int rtsp_listen(AVFormatContext *s) |
759 | 0 | { |
760 | 0 | RTSPState *rt = s->priv_data; |
761 | 0 | char proto[128], host[128], path[512], auth[128]; |
762 | 0 | char uri[500]; |
763 | 0 | int port; |
764 | 0 | int default_port = RTSP_DEFAULT_PORT; |
765 | 0 | char tcpname[500]; |
766 | 0 | const char *lower_proto = "tcp"; |
767 | 0 | unsigned char rbuf[MAX_URL_SIZE]; |
768 | 0 | unsigned char method[10]; |
769 | 0 | int rbuflen = 0; |
770 | 0 | int ret; |
771 | 0 | enum RTSPMethod methodcode; |
772 | |
|
773 | 0 | if ((ret = ff_network_init()) < 0) |
774 | 0 | return ret; |
775 | | |
776 | | /* extract hostname and port */ |
777 | 0 | av_url_split(proto, sizeof(proto), auth, sizeof(auth), host, sizeof(host), |
778 | 0 | &port, path, sizeof(path), s->url); |
779 | | |
780 | | /* ff_url_join. No authorization by now (NULL) */ |
781 | 0 | ff_url_join(rt->control_uri, sizeof(rt->control_uri), proto, NULL, host, |
782 | 0 | port, "%s", path); |
783 | |
|
784 | 0 | if (!strcmp(proto, "rtsps")) { |
785 | 0 | lower_proto = "tls"; |
786 | 0 | default_port = RTSPS_DEFAULT_PORT; |
787 | 0 | } |
788 | |
|
789 | 0 | if (port < 0) |
790 | 0 | port = default_port; |
791 | | |
792 | | /* Create TCP connection */ |
793 | 0 | ff_url_join(tcpname, sizeof(tcpname), lower_proto, NULL, host, port, |
794 | 0 | "?listen&listen_timeout=%d", rt->initial_timeout * 1000); |
795 | |
|
796 | 0 | if (ret = ffurl_open_whitelist(&rt->rtsp_hd, tcpname, AVIO_FLAG_READ_WRITE, |
797 | 0 | &s->interrupt_callback, NULL, |
798 | 0 | s->protocol_whitelist, s->protocol_blacklist, NULL)) { |
799 | 0 | av_log(s, AV_LOG_ERROR, "Unable to open RTSP for listening\n"); |
800 | 0 | goto fail; |
801 | 0 | } |
802 | 0 | rt->state = RTSP_STATE_IDLE; |
803 | 0 | rt->rtsp_hd_out = rt->rtsp_hd; |
804 | 0 | for (;;) { /* Wait for incoming RTSP messages */ |
805 | 0 | ret = read_line(s, rbuf, sizeof(rbuf), &rbuflen); |
806 | 0 | if (ret < 0) |
807 | 0 | goto fail; |
808 | 0 | av_log(s, AV_LOG_TRACE, "Parsing[%d]: %s\n", rbuflen, rbuf); |
809 | 0 | ret = parse_command_line(s, rbuf, rbuflen, uri, sizeof(uri), method, |
810 | 0 | sizeof(method), &methodcode); |
811 | 0 | if (ret) { |
812 | 0 | av_log(s, AV_LOG_ERROR, "RTSP: Unexpected Command\n"); |
813 | 0 | goto fail; |
814 | 0 | } |
815 | | |
816 | 0 | if (methodcode == ANNOUNCE) { |
817 | 0 | ret = rtsp_read_announce(s); |
818 | 0 | rt->state = RTSP_STATE_PAUSED; |
819 | 0 | } else if (methodcode == OPTIONS) { |
820 | 0 | ret = rtsp_read_options(s); |
821 | 0 | } else if (methodcode == RECORD) { |
822 | 0 | ret = rtsp_read_record(s); |
823 | 0 | if (!ret) |
824 | 0 | return 0; // We are ready for streaming |
825 | 0 | } else if (methodcode == SETUP) |
826 | 0 | ret = rtsp_read_setup(s, host, uri); |
827 | 0 | if (ret) { |
828 | 0 | ret = AVERROR_INVALIDDATA; |
829 | 0 | goto fail; |
830 | 0 | } |
831 | 0 | } |
832 | 0 | fail: |
833 | 0 | ff_rtsp_close_streams(s); |
834 | 0 | ff_rtsp_close_connections(s); |
835 | 0 | ff_network_close(); |
836 | 0 | return ret; |
837 | 0 | } |
838 | | |
839 | | static int rtsp_probe(const AVProbeData *p) |
840 | 0 | { |
841 | 0 | if ( |
842 | | #if CONFIG_TLS_PROTOCOL |
843 | | av_strstart(p->filename, "rtsps:", NULL) || |
844 | | #endif |
845 | 0 | av_strstart(p->filename, "satip:", NULL) || |
846 | 0 | av_strstart(p->filename, "rtsp:", NULL)) |
847 | 0 | return AVPROBE_SCORE_MAX; |
848 | 0 | return 0; |
849 | 0 | } |
850 | | |
851 | | static int rtsp_read_header(AVFormatContext *s) |
852 | 0 | { |
853 | 0 | RTSPState *rt = s->priv_data; |
854 | 0 | int ret; |
855 | |
|
856 | 0 | if (rt->initial_timeout > 0) |
857 | 0 | rt->rtsp_flags |= RTSP_FLAG_LISTEN; |
858 | |
|
859 | 0 | if (rt->rtsp_flags & RTSP_FLAG_LISTEN) { |
860 | 0 | ret = rtsp_listen(s); |
861 | 0 | if (ret) |
862 | 0 | return ret; |
863 | 0 | } else { |
864 | 0 | ret = ff_rtsp_connect(s); |
865 | 0 | if (ret) |
866 | 0 | return ret; |
867 | | |
868 | 0 | rt->real_setup_cache = !s->nb_streams ? NULL : |
869 | 0 | av_calloc(s->nb_streams, 2 * sizeof(*rt->real_setup_cache)); |
870 | 0 | if (!rt->real_setup_cache && s->nb_streams) { |
871 | 0 | ret = AVERROR(ENOMEM); |
872 | 0 | goto fail; |
873 | 0 | } |
874 | 0 | rt->real_setup = rt->real_setup_cache + s->nb_streams; |
875 | |
|
876 | 0 | if (rt->initial_pause) { |
877 | | /* do not start immediately */ |
878 | 0 | } else { |
879 | 0 | ret = rtsp_read_play(s); |
880 | 0 | if (ret < 0) |
881 | 0 | goto fail; |
882 | 0 | } |
883 | 0 | } |
884 | | |
885 | 0 | return 0; |
886 | | |
887 | 0 | fail: |
888 | 0 | rtsp_read_close(s); |
889 | 0 | return ret; |
890 | 0 | } |
891 | | |
892 | | int ff_rtsp_tcp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st, |
893 | | uint8_t *buf, int buf_size) |
894 | 0 | { |
895 | 0 | RTSPState *rt = s->priv_data; |
896 | 0 | int id, len, i, ret; |
897 | 0 | RTSPStream *rtsp_st; |
898 | |
|
899 | 0 | av_log(s, AV_LOG_TRACE, "tcp_read_packet:\n"); |
900 | 0 | redo: |
901 | 0 | for (;;) { |
902 | 0 | RTSPMessageHeader reply; |
903 | |
|
904 | 0 | ret = ff_rtsp_read_reply(s, &reply, NULL, 1, NULL); |
905 | 0 | if (ret < 0) |
906 | 0 | return ret; |
907 | 0 | if (ret == 1) /* received '$' */ |
908 | 0 | break; |
909 | | /* XXX: parse message */ |
910 | 0 | if (rt->state != RTSP_STATE_STREAMING) |
911 | 0 | return 0; |
912 | 0 | } |
913 | 0 | ret = ffurl_read_complete(rt->rtsp_hd, buf, 3); |
914 | 0 | if (ret != 3) |
915 | 0 | return AVERROR(EIO); |
916 | 0 | rt->pending_packet = 0; |
917 | 0 | id = buf[0]; |
918 | 0 | len = AV_RB16(buf + 1); |
919 | 0 | av_log(s, AV_LOG_TRACE, "id=%d len=%d\n", id, len); |
920 | 0 | if (len > buf_size || len < 8) |
921 | 0 | goto redo; |
922 | | /* get the data */ |
923 | 0 | ret = ffurl_read_complete(rt->rtsp_hd, buf, len); |
924 | 0 | if (ret != len) |
925 | 0 | return AVERROR(EIO); |
926 | 0 | if (rt->transport == RTSP_TRANSPORT_RDT && |
927 | 0 | (ret = ff_rdt_parse_header(buf, len, &id, NULL, NULL, NULL, NULL)) < 0) |
928 | 0 | return ret; |
929 | | |
930 | | /* find the matching stream */ |
931 | 0 | for (i = 0; i < rt->nb_rtsp_streams; i++) { |
932 | 0 | rtsp_st = rt->rtsp_streams[i]; |
933 | 0 | if (id >= rtsp_st->interleaved_min && |
934 | 0 | id <= rtsp_st->interleaved_max) |
935 | 0 | goto found; |
936 | 0 | } |
937 | 0 | goto redo; |
938 | 0 | found: |
939 | 0 | *prtsp_st = rtsp_st; |
940 | 0 | return len; |
941 | 0 | } |
942 | | |
943 | | static int resetup_tcp(AVFormatContext *s) |
944 | 0 | { |
945 | 0 | RTSPState *rt = s->priv_data; |
946 | 0 | char host[1024]; |
947 | 0 | int port; |
948 | |
|
949 | 0 | av_url_split(NULL, 0, NULL, 0, host, sizeof(host), &port, NULL, 0, |
950 | 0 | s->url); |
951 | 0 | ff_rtsp_undo_setup(s, 0); |
952 | 0 | return ff_rtsp_make_setup_request(s, host, port, RTSP_LOWER_TRANSPORT_TCP, |
953 | 0 | rt->real_challenge); |
954 | 0 | } |
955 | | |
956 | | static int rtsp_read_packet(AVFormatContext *s, AVPacket *pkt) |
957 | 0 | { |
958 | 0 | RTSPState *rt = s->priv_data; |
959 | 0 | int ret; |
960 | 0 | RTSPMessageHeader reply1, *reply = &reply1; |
961 | 0 | char cmd[MAX_URL_SIZE]; |
962 | |
|
963 | 0 | retry: |
964 | 0 | if (rt->server_type == RTSP_SERVER_REAL) { |
965 | 0 | int i; |
966 | |
|
967 | 0 | for (i = 0; i < s->nb_streams; i++) |
968 | 0 | rt->real_setup[i] = s->streams[i]->discard; |
969 | |
|
970 | 0 | if (!rt->need_subscription) { |
971 | 0 | if (memcmp (rt->real_setup, rt->real_setup_cache, |
972 | 0 | sizeof(enum AVDiscard) * s->nb_streams)) { |
973 | 0 | snprintf(cmd, sizeof(cmd), |
974 | 0 | "Unsubscribe: %s\r\n", |
975 | 0 | rt->last_subscription); |
976 | 0 | ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri, |
977 | 0 | cmd, reply, NULL); |
978 | 0 | if (reply->status_code != RTSP_STATUS_OK) |
979 | 0 | return ff_rtsp_averror(reply->status_code, AVERROR_INVALIDDATA); |
980 | 0 | rt->need_subscription = 1; |
981 | 0 | } |
982 | 0 | } |
983 | | |
984 | 0 | if (rt->need_subscription) { |
985 | 0 | int r, rule_nr, first = 1; |
986 | |
|
987 | 0 | memcpy(rt->real_setup_cache, rt->real_setup, |
988 | 0 | sizeof(enum AVDiscard) * s->nb_streams); |
989 | 0 | rt->last_subscription[0] = 0; |
990 | |
|
991 | 0 | snprintf(cmd, sizeof(cmd), |
992 | 0 | "Subscribe: "); |
993 | 0 | for (i = 0; i < rt->nb_rtsp_streams; i++) { |
994 | 0 | rule_nr = 0; |
995 | 0 | for (r = 0; r < s->nb_streams; r++) { |
996 | 0 | if (s->streams[r]->id == i) { |
997 | 0 | if (s->streams[r]->discard != AVDISCARD_ALL) { |
998 | 0 | if (!first) |
999 | 0 | av_strlcat(rt->last_subscription, ",", |
1000 | 0 | sizeof(rt->last_subscription)); |
1001 | 0 | ff_rdt_subscribe_rule( |
1002 | 0 | rt->last_subscription, |
1003 | 0 | sizeof(rt->last_subscription), i, rule_nr); |
1004 | 0 | first = 0; |
1005 | 0 | } |
1006 | 0 | rule_nr++; |
1007 | 0 | } |
1008 | 0 | } |
1009 | 0 | } |
1010 | 0 | av_strlcatf(cmd, sizeof(cmd), "%s\r\n", rt->last_subscription); |
1011 | 0 | ff_rtsp_send_cmd(s, "SET_PARAMETER", rt->control_uri, |
1012 | 0 | cmd, reply, NULL); |
1013 | 0 | if (reply->status_code != RTSP_STATUS_OK) |
1014 | 0 | return ff_rtsp_averror(reply->status_code, AVERROR_INVALIDDATA); |
1015 | 0 | rt->need_subscription = 0; |
1016 | |
|
1017 | 0 | if (rt->state == RTSP_STATE_STREAMING) |
1018 | 0 | rtsp_read_play (s); |
1019 | 0 | } |
1020 | 0 | } |
1021 | | |
1022 | 0 | ret = ff_rtsp_fetch_packet(s, pkt); |
1023 | 0 | if (ret < 0) { |
1024 | 0 | if (ret == AVERROR(ETIMEDOUT) && !rt->packets) { |
1025 | 0 | if (rt->lower_transport == RTSP_LOWER_TRANSPORT_UDP && |
1026 | 0 | rt->lower_transport_mask & (1 << RTSP_LOWER_TRANSPORT_TCP)) { |
1027 | 0 | RTSPMessageHeader reply1, *reply = &reply1; |
1028 | 0 | av_log(s, AV_LOG_WARNING, "UDP timeout, retrying with TCP\n"); |
1029 | 0 | if (rtsp_read_pause(s) != 0) |
1030 | 0 | return -1; |
1031 | | // TEARDOWN is required on Real-RTSP, but might make |
1032 | | // other servers close the connection. |
1033 | 0 | if (rt->server_type == RTSP_SERVER_REAL) |
1034 | 0 | ff_rtsp_send_cmd(s, "TEARDOWN", rt->control_uri, NULL, |
1035 | 0 | reply, NULL); |
1036 | 0 | rt->session_id[0] = '\0'; |
1037 | 0 | if (resetup_tcp(s) == 0) { |
1038 | 0 | rt->state = RTSP_STATE_IDLE; |
1039 | 0 | rt->need_subscription = 1; |
1040 | 0 | if (rtsp_read_play(s) != 0) |
1041 | 0 | return -1; |
1042 | 0 | goto retry; |
1043 | 0 | } |
1044 | 0 | } |
1045 | 0 | } |
1046 | 0 | return ret; |
1047 | 0 | } |
1048 | 0 | rt->packets++; |
1049 | |
|
1050 | 0 | if (!(rt->rtsp_flags & RTSP_FLAG_LISTEN)) { |
1051 | | /* send dummy request to keep TCP connection alive */ |
1052 | 0 | if ((av_gettime_relative() - rt->last_cmd_time) / 1000000 >= rt->timeout / 2 || |
1053 | 0 | rt->auth_state.stale) { |
1054 | 0 | if (rt->server_type == RTSP_SERVER_WMS || |
1055 | 0 | (rt->server_type != RTSP_SERVER_REAL && |
1056 | 0 | rt->get_parameter_supported)) { |
1057 | 0 | ff_rtsp_send_cmd_async(s, "GET_PARAMETER", rt->control_uri, NULL); |
1058 | 0 | } else { |
1059 | 0 | ff_rtsp_send_cmd_async(s, "OPTIONS", rt->control_uri, NULL); |
1060 | 0 | } |
1061 | | /* The stale flag should be reset when creating the auth response in |
1062 | | * ff_rtsp_send_cmd_async, but reset it here just in case we never |
1063 | | * called the auth code (if we didn't have any credentials set). */ |
1064 | 0 | rt->auth_state.stale = 0; |
1065 | 0 | } |
1066 | 0 | } |
1067 | |
|
1068 | 0 | return 0; |
1069 | 0 | } |
1070 | | |
1071 | | static int rtsp_read_seek(AVFormatContext *s, int stream_index, |
1072 | | int64_t timestamp, int flags) |
1073 | 0 | { |
1074 | 0 | RTSPState *rt = s->priv_data; |
1075 | 0 | int ret; |
1076 | |
|
1077 | 0 | rt->seek_timestamp = av_rescale_q(timestamp, |
1078 | 0 | s->streams[stream_index]->time_base, |
1079 | 0 | AV_TIME_BASE_Q); |
1080 | 0 | switch(rt->state) { |
1081 | 0 | default: |
1082 | 0 | case RTSP_STATE_IDLE: |
1083 | 0 | break; |
1084 | 0 | case RTSP_STATE_STREAMING: |
1085 | 0 | if ((ret = rtsp_read_pause(s)) != 0) |
1086 | 0 | return ret; |
1087 | 0 | rt->state = RTSP_STATE_SEEKING; |
1088 | 0 | if ((ret = rtsp_read_play(s)) != 0) |
1089 | 0 | return ret; |
1090 | 0 | break; |
1091 | 0 | case RTSP_STATE_PAUSED: |
1092 | 0 | rt->state = RTSP_STATE_IDLE; |
1093 | 0 | break; |
1094 | 0 | } |
1095 | 0 | return 0; |
1096 | 0 | } |
1097 | | |
1098 | | static const AVClass rtsp_demuxer_class = { |
1099 | | .class_name = "RTSP demuxer", |
1100 | | .item_name = av_default_item_name, |
1101 | | .option = ff_rtsp_options, |
1102 | | .version = LIBAVUTIL_VERSION_INT, |
1103 | | }; |
1104 | | |
1105 | | const FFInputFormat ff_rtsp_demuxer = { |
1106 | | .p.name = "rtsp", |
1107 | | .p.long_name = NULL_IF_CONFIG_SMALL("RTSP input"), |
1108 | | .p.flags = AVFMT_NOFILE, |
1109 | | .p.priv_class = &rtsp_demuxer_class, |
1110 | | .priv_data_size = sizeof(RTSPState), |
1111 | | .read_probe = rtsp_probe, |
1112 | | .read_header = rtsp_read_header, |
1113 | | .read_packet = rtsp_read_packet, |
1114 | | .read_close = rtsp_read_close, |
1115 | | .read_seek = rtsp_read_seek, |
1116 | | .read_set_state = rtsp_read_set_state, |
1117 | | .handle_command = rtsp_handle_command, |
1118 | | }; |