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
28
35
51 { 0, "NULL" }
52 };
53
55 {
57
60
66 return 0;
67 }
68
70 int *rbuflen)
71 {
73 int idx = 0;
74 int ret = 0;
75 *rbuflen = 0;
76
77 do {
79 if (ret <= 0)
81 if (rbuf[idx] == '\r') {
82 /* Ignore */
83 } else if (rbuf[idx] == '\n') {
84 rbuf[idx] = '0円';
85 *rbuflen = idx;
86 return 0;
87 } else
88 idx++;
89 } while (idx < rbufsize);
92 }
93
95 const char *extracontent, uint16_t seq)
96 {
98 char message[4096];
102 snprintf(message,
sizeof(message),
"RTSP/1.0 %d %s\r\n",
104 break;
105 }
106 index++;
107 }
110 av_strlcatf(message,
sizeof(message),
"CSeq: %d\r\n", seq);
112 if (extracontent)
113 av_strlcat(message, extracontent,
sizeof(message));
115 av_dlog(s,
"Sending response:\n%s", message);
117
118 return 0;
119 }
120
123 {
126 if (!session_id[0]) {
128 return 0;
129 }
130 if (strcmp(session_id, request->
session_id)) {
135 }
136 return 0;
137 }
138
141 const char *method)
142 {
144 char rbuf[1024];
145 int rbuflen, ret;
146 do {
147 ret =
read_line(s, rbuf,
sizeof(rbuf), &rbuflen);
148 if (ret)
149 return ret;
150 if (rbuflen > 1) {
151 av_dlog(s,
"Parsing[%d]: %s\n", rbuflen, rbuf);
153 }
154 } while (rbuflen > 0);
155 if (request->
seq != rt->
seq + 1) {
159 }
160 if (rt->
session_id[0] && strcmp(method,
"OPTIONS")) {
162 if (ret)
163 return ret;
164 }
165
166 return 0;
167 }
168
170 {
173 char sdp[4096];
174 int ret;
175
177 if (ret)
178 return ret;
185 }
187 /* Read SDP */
191 "Unable to get complete SDP Description in ANNOUNCE\n");
194 }
198 if (ret)
199 return ret;
201 return 0;
202 }
204 "Content-Length header value exceeds sdp allocated buffer (4KB)\n");
206 "Content-Length exceeds buffer size", request.
seq);
208 }
209
211 {
214 int ret = 0;
215
216 /* Parsing headers */
218 if (ret)
219 return ret;
221 /* Send Reply */
223 "Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, RECORD\r\n",
225 return 0;
226 }
227
229 {
232 int ret = 0;
233 char url[1024];
235 char responseheaders[1024];
236 int localport = -1;
237 int transportidx = 0;
238 int streamid = 0;
239
241 if (ret)
242 return ret;
247 }
249 transportidx++) {
256 " protocol not supported (yet)\n");
258 }
259 }
262 "using first of all\n");
265 controlurl))
266 break;
267 }
271 }
274
279 return ret;
280 }
283 snprintf(responseheaders,
sizeof(responseheaders),
"Transport: "
284 "RTP/AVP/TCP;unicast;mode=receive;interleaved=%d-%d"
287 } else {
288 do {
290 av_dlog(s,
"Opening: %s", url);
293 if (ret)
294 localport += 2;
298 return ret;
299 }
300
305 return ret;
306 }
307
309 snprintf(responseheaders,
sizeof(responseheaders),
"Transport: "
310 "RTP/AVP/UDP;unicast;mode=receive;source=%s;"
311 "client_port=%d-%d;server_port=%d-%d\r\n",
314 localport + 1);
315 }
316
317 /* Establish sessionid if not previously set */
318 /* Put this in a function? */
319 /* RFC 2326: session id must be at least 8 digits */
322
323 av_strlcatf(responseheaders,
sizeof(responseheaders),
"Session: %s\r\n",
325 /* Send Reply */
327
329 return 0;
330 }
331
333 {
336 int ret = 0;
337 char responseheaders[1024];
338
340 if (ret)
341 return ret;
343 if (ret)
344 return ret;
346 snprintf(responseheaders,
sizeof(responseheaders),
"Session: %s\r\n",
349
351 return 0;
352 }
353
355 int linelen, char *uri, int urisize,
356 char *method, int methodsize,
358 {
360 const char *linept, *searchlinept;
361 linept = strchr(line, ' ');
362 if (linept - line > methodsize - 1) {
365 }
366 memcpy(method, line, linept - line);
367 method[linept - line] = '0円';
368 linept++;
369 if (!strcmp(method, "ANNOUNCE"))
371 else if (!strcmp(method, "OPTIONS"))
373 else if (!strcmp(method, "RECORD"))
375 else if (!strcmp(method, "SETUP"))
377 else if (!strcmp(method, "PAUSE"))
379 else if (!strcmp(method, "TEARDOWN"))
381 else
383 /* Check method with the state */
387 line);
389 }
392 && (*methodcode !=
SETUP)) {
394 line);
396 }
401 " %s\n", line);
403 }
404 } else {
407 }
408
409 searchlinept = strchr(linept, ' ');
410 if (searchlinept ==
NULL) {
413 }
414 if (searchlinept - linept > urisize - 1) {
417 }
418 memcpy(uri, linept, searchlinept - linept);
419 uri[searchlinept - linept] = '0円';
421 char host[128], path[512], auth[128];
422 int port;
423 char ctl_host[128], ctl_path[512], ctl_auth[128];
424 int ctl_port;
426 path, sizeof(path), uri);
428 sizeof(ctl_host), &ctl_port, ctl_path, sizeof(ctl_path),
430 if (strcmp(host, ctl_host))
432 host, ctl_host);
433 if (strcmp(path, ctl_path) && *methodcode !=
SETUP)
435 " %s\n", path, ctl_path);
438 "Updating control URI to %s\n", uri);
440 }
441 }
442
443 linept = searchlinept + 1;
447 }
448 return 0;
449 }
450
452 {
454 unsigned char rbuf[4096];
455 unsigned char method[10];
456 char uri[500];
457 int ret;
458 int rbuflen = 0;
461
462 ret =
read_line(s, rbuf,
sizeof(rbuf), &rbuflen);
463 if (ret < 0)
464 return ret;
466 sizeof(method), &methodcode);
467 if (ret) {
469 return ret;
470 }
471
473 if (ret)
474 return ret;
476 if (methodcode ==
PAUSE) {
479 // TODO: Missing date header in response
480 }
else if (methodcode ==
OPTIONS) {
482 "Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, "
483 "RECORD\r\n", request.
seq);
484 }
else if (methodcode ==
TEARDOWN) {
487 return 0;
488 }
489 return ret;
490 }
491
493 {
496 int i;
497 char cmd[1024];
498
501
507 if (!rtpctx)
508 continue;
516 }
517 }
519 cmd[0] = 0;
520 } else {
522 "Range: npt=%"PRId64".%03"PRId64"-\r\n",
525 }
528 return -1;
529 }
537 continue;
542 }
543 }
544 }
546 return 0;
547 }
548
549 /* pause the stream */
551 {
554
556 return 0;
560 return -1;
561 }
562 }
564 return 0;
565 }
566
568 {
570 char cmd[1024];
571 unsigned char *content =
NULL;
572 int ret;
573
574 /* describe the stream */
576 "Accept: application/sdp\r\n");
578 /**
579 * The Require: attribute is needed for proper streaming from
580 * Realmedia servers.
581 */
583 "Require: com.real.retain-entity-for-setup\r\n",
584 sizeof(cmd));
585 }
587 if (!content)
592 }
593
595 /* now we got the SDP description, we parse it */
598 if (ret < 0)
599 return ret;
600
601 return 0;
602 }
603
605 {
607 char host[128], path[512], auth[128];
608 char uri[500];
609 int port;
610 char tcpname[500];
611 unsigned char rbuf[4096];
612 unsigned char method[10];
613 int rbuflen = 0;
614 int ret;
616
617 /* extract hostname and port */
620
621 /* ff_url_join. No authorization by now (NULL) */
623 port, "%s", path);
624
625 if (port < 0)
627
628 /* Create TCP connection */
631
635 return ret;
636 }
639 for (;;) { /* Wait for incoming RTSP messages */
640 ret =
read_line(s, rbuf,
sizeof(rbuf), &rbuflen);
641 if (ret < 0)
642 return ret;
644 sizeof(method), &methodcode);
645 if (ret) {
647 return ret;
648 }
649
653 }
else if (methodcode ==
OPTIONS) {
655 }
else if (methodcode ==
RECORD) {
657 if (!ret)
658 return 0; // We are ready for streaming
659 }
else if (methodcode ==
SETUP)
661 if (ret) {
664 }
665 }
666 return 0;
667 }
668
670 {
673 return 0;
674 }
675
677 {
679 int ret;
680
683
686 if (ret)
687 return ret;
688 } else {
690 if (ret)
691 return ret;
692
698
700 /* do not start immediately */
701 } else {
706 }
707 }
708 }
709
710 return 0;
711 }
712
715 {
719
720 av_dlog(s,
"tcp_read_packet:\n");
721 redo:
722 for (;;) {
724
726 if (ret < 0)
727 return ret;
728 if (ret == 1) /* received '$' */
729 break;
730 /* XXX: parse message */
732 return 0;
733 }
735 if (ret != 3)
736 return -1;
737 id = buf[0];
739 av_dlog(s,
"id=%d len=%d\n",
id, len);
740 if (len > buf_size || len < 8)
741 goto redo;
742 /* get the data */
744 if (ret != len)
745 return -1;
748 return -1;
749
750 /* find the matching stream */
754 id <= rtsp_st->interleaved_max)
755 goto found;
756 }
757 goto redo;
758 found:
759 *prtsp_st = rtsp_st;
761 }
762
764 {
766 char host[1024];
767 int port;
768
774 }
775
777 {
779 int ret;
781 char cmd[1024];
782
783 retry:
785 int i;
786
789
794 "Unsubscribe: %s\r\n",
801 }
802 }
803
805 int r, rule_nr, first = 1;
806
810
812 "Subscribe: ");
814 rule_nr = 0;
818 if (!first)
824 first = 0;
825 }
826 rule_nr++;
827 }
828 }
829 }
836
839 }
840 }
841
843 if (ret < 0) {
850 return -1;
851 // TEARDOWN is required on Real-RTSP, but might make
852 // other servers close the connection.
861 return -1;
862 goto retry;
863 }
864 }
865 }
866 return ret;
867 }
869
871 /* send dummy request to keep TCP connection alive */
878 } else {
880 }
881 /* The stale flag should be reset when creating the auth response in
882 * ff_rtsp_send_cmd_async, but reset it here just in case we never
883 * called the auth code (if we didn't have any credentials set). */
885 }
886 }
887
888 return 0;
889 }
890
892 int64_t timestamp,
int flags)
893 {
895
900 default:
902 break;
905 return -1;
908 return -1;
909 break;
912 break;
913 }
914 return 0;
915 }
916
922 };
923
936 .priv_class = &rtsp_demuxer_class,
937 };