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;
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];
146 do {
147 ret =
read_line(s, rbuf,
sizeof(rbuf), &rbuflen);
148 if (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)
164 }
165
166 return 0;
167 }
168
170 {
173 char sdp[4096];
175
177 if (ret)
185 }
187 /* Read SDP */
191 "Unable to get complete SDP Description in ANNOUNCE\n");
194 }
198 if (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 {
215
216 /* Parsing headers */
218 if (ret)
221 /* Send Reply */
223 "Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, RECORD\r\n",
225 return 0;
226 }
227
229 {
233 char url[1024];
235 char responseheaders[1024];
236 int localport = -1;
237 int transportidx = 0;
238 int streamid = 0;
239
241 if (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
280 }
283 snprintf(responseheaders,
sizeof(responseheaders),
"Transport: "
284 "RTP/AVP/TCP;unicast;mode=receive;interleaved=%d-%d"
287 } else {
288 do {
289 ff_url_join(url,
sizeof(url),
"rtp", NULL, host, localport, NULL);
290 av_dlog(s,
"Opening: %s", url);
293 if (ret)
294 localport += 2;
299 }
300
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 {
337 char responseheaders[1024];
338
340 if (ret)
343 if (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;
425 av_url_split(NULL, 0, auth,
sizeof(auth), host,
sizeof(host), &port,
426 path, sizeof(path), uri);
427 av_url_split(NULL, 0, ctl_auth,
sizeof(ctl_auth), ctl_host,
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];
458 int rbuflen = 0;
461
462 ret =
read_line(s, rbuf,
sizeof(rbuf), &rbuflen);
463 if (ret < 0)
466 sizeof(method), &methodcode);
467 if (ret) {
470 }
471
473 if (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 }
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;
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)
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;
616
617 /* extract hostname and port */
618 av_url_split(NULL, 0, auth,
sizeof(auth), host,
sizeof(host), &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 */
629 ff_url_join(tcpname,
sizeof(tcpname),
"tcp", NULL, host, port,
631
636 }
639 for (;;) { /* Wait for incoming RTSP messages */
640 ret =
read_line(s, rbuf,
sizeof(rbuf), &rbuflen);
641 if (ret < 0)
644 sizeof(method), &methodcode);
645 if (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 {
680
683
686 if (ret)
688 } else {
690 if (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)
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
769 av_url_split(NULL, 0, NULL, 0, host,
sizeof(host), &port, NULL, 0,
774 }
775
777 {
781 char cmd[1024];
782
783 retry:
785 int i;
786
789
794 "Unsubscribe: %s\r\n",
797 cmd, reply, NULL);
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 }
832 cmd, reply, NULL);
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.
855 reply, NULL);
861 return -1;
862 goto retry;
863 }
864 }
865 }
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 };