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