added rtsp support (not activated yet)
[libav.git] / libav / rtsp.c
CommitLineData
1617ad97
FB
1/*
2 * RTSP client
3 * Copyright (c) 2002 Fabrice Bellard.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include "avformat.h"
20
21#include <sys/time.h>
22#include <netinet/in.h>
23#include <sys/socket.h>
24#include <arpa/inet.h>
25
26//#define DEBUG
27
28typedef struct RTSPState {
29 URLContext *rtsp_hd; /* RTSP TCP connexion handle */
30 ByteIOContext rtsp_gb;
31 int seq; /* RTSP command sequence number */
32 char session_id[512];
33 enum RTSPProtocol protocol;
34 char last_reply[2048]; /* XXX: allocate ? */
35} RTSPState;
36
37typedef struct RTSPStream {
38 AVFormatContext *ic;
39 int interleaved_min, interleaved_max; /* interleave ids, if TCP transport */
40 char control_url[1024]; /* url for this stream */
41} RTSPStream;
42
43/* suppress this hack */
44int rtsp_abort_req = 0;
45
46/* XXX: currently, the only way to change the protocols consists in
47 changing this variable */
48int rtsp_default_protocols = (1 << RTSP_PROTOCOL_RTP_TCP) | (1 << RTSP_PROTOCOL_RTP_UDP) | (1 << RTSP_PROTOCOL_RTP_UDP_MULTICAST);
49
50FFRTSPCallback *ff_rtsp_callback = NULL;
51
52static int rtsp_probe(AVProbeData *p)
53{
54 if (strstart(p->filename, "rtsp:", NULL))
55 return AVPROBE_SCORE_MAX;
56 return 0;
57}
58
59static int redir_isspace(int c)
60{
61 return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
62}
63
64static void skip_spaces(const char **pp)
65{
66 const char *p;
67 p = *pp;
68 while (redir_isspace(*p))
69 p++;
70 *pp = p;
71}
72
73static void get_word_sep(char *buf, int buf_size, const char *sep,
74 const char **pp)
75{
76 const char *p;
77 char *q;
78
79 p = *pp;
80 skip_spaces(&p);
81 q = buf;
82 while (!strchr(sep, *p) && *p != '\0') {
83 if ((q - buf) < buf_size - 1)
84 *q++ = *p;
85 p++;
86 }
87 if (buf_size > 0)
88 *q = '\0';
89 *pp = p;
90}
91
92static void get_word(char *buf, int buf_size, const char **pp)
93{
94 const char *p;
95 char *q;
96
97 p = *pp;
98 skip_spaces(&p);
99 q = buf;
100 while (!redir_isspace(*p) && *p != '\0') {
101 if ((q - buf) < buf_size - 1)
102 *q++ = *p;
103 p++;
104 }
105 if (buf_size > 0)
106 *q = '\0';
107 *pp = p;
108}
109
110static void sdp_parse_line(AVFormatContext *s,
111 int letter, const char *buf)
112{
113 char buf1[64], st_type[64];
114 const char *p;
115 int codec_type, payload_type;
116 AVStream *st;
117 RTSPStream *rtsp_st;
118
119#ifdef DEBUG
120 printf("sdp: %c='%s'\n", letter, buf);
121#endif
122
123 p = buf;
124 switch(letter) {
125 case 's':
126 pstrcpy(s->title, sizeof(s->title), p);
127 break;
128 case 'i':
129 if (s->nb_streams == 0) {
130 pstrcpy(s->comment, sizeof(s->comment), p);
131 break;
132 }
133 break;
134 case 'm':
135 /* new stream */
136 get_word(st_type, sizeof(st_type), &p);
137 if (!strcmp(st_type, "audio")) {
138 codec_type = CODEC_TYPE_AUDIO;
139 } else if (!strcmp(st_type, "video")) {
140 codec_type = CODEC_TYPE_VIDEO;
141 } else {
142 return;
143 }
144 get_word(buf1, sizeof(buf1), &p); /* port */
145 get_word(buf1, sizeof(buf1), &p); /* protocol */
146 /* XXX: handle list of formats */
147 get_word(buf1, sizeof(buf1), &p); /* format list */
148 payload_type = atoi(buf1);
149
150 rtsp_st = av_mallocz(sizeof(RTSPStream));
151 if (!rtsp_st)
152 return;
153 st = av_new_stream(s, s->nb_streams);
154 if (!st)
155 return;
156 st->priv_data = rtsp_st;
157 /* put a default control url */
158 pstrcpy(rtsp_st->control_url, sizeof(rtsp_st->control_url), s->filename);
159 st->codec.codec_type = codec_type;
160 rtp_get_codec_info(&st->codec, payload_type);
161 break;
162 case 'a':
163 if (strstart(p, "control:", &p) && s->nb_streams > 0) {
164 char proto[32];
165 /* get the control url */
166 st = s->streams[s->nb_streams - 1];
167 rtsp_st = st->priv_data;
168
169 /* XXX: may need to add full url resolution */
170 url_split(proto, sizeof(proto), NULL, 0, NULL, NULL, 0, p);
171 if (proto[0] == '\0') {
172 /* relative control URL */
173 pstrcat(rtsp_st->control_url, sizeof(rtsp_st->control_url), "/");
174 pstrcat(rtsp_st->control_url, sizeof(rtsp_st->control_url), p);
175 } else {
176 pstrcpy(rtsp_st->control_url, sizeof(rtsp_st->control_url), p);
177 }
178 }
179 break;
180 }
181}
182
183int sdp_parse(AVFormatContext *s, const char *content)
184{
185 const char *p;
186 int letter;
187 char buf[1024], *q;
188
189 p = content;
190 for(;;) {
191 skip_spaces(&p);
192 letter = *p;
193 if (letter == '\0')
194 break;
195 p++;
196 if (*p != '=')
197 goto next_line;
198 p++;
199 /* get the content */
200 q = buf;
201 while (*p != '\n' && *p != '\0') {
202 if ((q - buf) < sizeof(buf) - 1)
203 *q++ = *p;
204 p++;
205 }
206 *q = '\0';
207 sdp_parse_line(s, letter, buf);
208 next_line:
209 while (*p != '\n' && *p != '\0')
210 p++;
211 if (*p == '\n')
212 p++;
213 }
214 return 0;
215}
216
217static void rtsp_parse_range(int *min_ptr, int *max_ptr, const char **pp)
218{
219 const char *p;
220 int v;
221
222 p = *pp;
223 skip_spaces(&p);
224 v = strtol(p, (char **)&p, 10);
225 if (*p == '-') {
226 p++;
227 *min_ptr = v;
228 v = strtol(p, (char **)&p, 10);
229 *max_ptr = v;
230 } else {
231 *min_ptr = v;
232 *max_ptr = v;
233 }
234 *pp = p;
235}
236
237/* XXX: only one transport specification is parsed */
238static void rtsp_parse_transport(RTSPHeader *reply, const char *p)
239{
240 char transport_protocol[16];
241 char profile[16];
242 char lower_transport[16];
243 char parameter[16];
244 RTSPTransportField *th;
245 char buf[256];
246
247 reply->nb_transports = 0;
248
249 for(;;) {
250 skip_spaces(&p);
251 if (*p == '\0')
252 break;
253
254 th = &reply->transports[reply->nb_transports];
255
256 get_word_sep(transport_protocol, sizeof(transport_protocol),
257 "/", &p);
258 if (*p == '/')
259 p++;
260 get_word_sep(profile, sizeof(profile), "/;,", &p);
261 lower_transport[0] = '\0';
262 if (*p == '/') {
263 get_word_sep(lower_transport, sizeof(lower_transport),
264 ";,", &p);
265 }
266 if (!strcmp(lower_transport, "TCP"))
267 th->protocol = RTSP_PROTOCOL_RTP_TCP;
268 else
269 th->protocol = RTSP_PROTOCOL_RTP_UDP;
270
271 if (*p == ';')
272 p++;
273 /* get each parameter */
274 while (*p != '\0' && *p != ',') {
275 get_word_sep(parameter, sizeof(parameter), "=;,", &p);
276 if (!strcmp(parameter, "port")) {
277 if (*p == '=') {
278 p++;
279 rtsp_parse_range(&th->port_min, &th->port_max, &p);
280 }
281 } else if (!strcmp(parameter, "client_port")) {
282 if (*p == '=') {
283 p++;
284 rtsp_parse_range(&th->client_port_min,
285 &th->client_port_max, &p);
286 }
287 } else if (!strcmp(parameter, "server_port")) {
288 if (*p == '=') {
289 p++;
290 rtsp_parse_range(&th->server_port_min,
291 &th->server_port_max, &p);
292 }
293 } else if (!strcmp(parameter, "interleaved")) {
294 if (*p == '=') {
295 p++;
296 rtsp_parse_range(&th->interleaved_min,
297 &th->interleaved_max, &p);
298 }
299 } else if (!strcmp(parameter, "multicast")) {
300 if (th->protocol == RTSP_PROTOCOL_RTP_UDP)
301 th->protocol = RTSP_PROTOCOL_RTP_UDP_MULTICAST;
302 } else if (!strcmp(parameter, "ttl")) {
303 if (*p == '=') {
304 p++;
305 th->ttl = strtol(p, (char **)&p, 10);
306 }
307 } else if (!strcmp(parameter, "destination")) {
308 struct in_addr ipaddr;
309
310 if (*p == '=') {
311 p++;
312 get_word_sep(buf, sizeof(buf), ";,", &p);
313 if (inet_aton(buf, &ipaddr))
314 th->destination = ntohl(ipaddr.s_addr);
315 }
316 }
317 while (*p != ';' && *p != '\0' && *p != ',')
318 p++;
319 if (*p == ';')
320 p++;
321 }
322 if (*p == ',')
323 p++;
324
325 reply->nb_transports++;
326 }
327}
328
329void rtsp_parse_line(RTSPHeader *reply, const char *buf)
330{
331 const char *p;
332
333 /* NOTE: we do case independent match for broken servers */
334 p = buf;
335 if (stristart(p, "Session:", &p)) {
336 get_word_sep(reply->session_id, sizeof(reply->session_id), ";", &p);
337 } else if (stristart(p, "Content-Length:", &p)) {
338 reply->content_length = strtol(p, NULL, 10);
339 } else if (stristart(p, "Transport:", &p)) {
340 rtsp_parse_transport(reply, p);
341 } else if (stristart(p, "CSeq:", &p)) {
342 reply->seq = strtol(p, NULL, 10);
343 }
344}
345
346
347static void rtsp_send_cmd(AVFormatContext *s,
348 const char *cmd, RTSPHeader *reply,
349 unsigned char **content_ptr)
350{
351 RTSPState *rt = s->priv_data;
352 char buf[4096], buf1[1024], *q;
353 unsigned char ch;
354 const char *p;
355 int content_length, line_count;
356 unsigned char *content = NULL;
357
358 memset(reply, 0, sizeof(RTSPHeader));
359
360 rt->seq++;
361 pstrcpy(buf, sizeof(buf), cmd);
362 snprintf(buf1, sizeof(buf1), "CSeq: %d\n", rt->seq);
363 pstrcat(buf, sizeof(buf), buf1);
364 if (rt->session_id[0] != '\0' && !strstr(cmd, "\nIf-Match:")) {
365 snprintf(buf1, sizeof(buf1), "Session: %s\n", rt->session_id);
366 pstrcat(buf, sizeof(buf), buf1);
367 }
368 pstrcat(buf, sizeof(buf), "\n");
369#ifdef DEBUG
370 printf("Sending:\n%s--\n", buf);
371#endif
372 url_write(rt->rtsp_hd, buf, strlen(buf));
373
374 /* parse reply (XXX: use buffers) */
375 line_count = 0;
376 rt->last_reply[0] = '\0';
377 for(;;) {
378 q = buf;
379 for(;;) {
380 if (url_read(rt->rtsp_hd, &ch, 1) == 0)
381 break;
382 if (ch == '\n')
383 break;
384 if (ch != '\r') {
385 if ((q - buf) < sizeof(buf) - 1)
386 *q++ = ch;
387 }
388 }
389 *q = '\0';
390#ifdef DEBUG
391 printf("line='%s'\n", buf);
392#endif
393 /* test if last line */
394 if (buf[0] == '\0')
395 break;
396 p = buf;
397 if (line_count == 0) {
398 /* get reply code */
399 get_word(buf1, sizeof(buf1), &p);
400 get_word(buf1, sizeof(buf1), &p);
401 reply->status_code = atoi(buf1);
402 } else {
403 rtsp_parse_line(reply, p);
404 pstrcat(rt->last_reply, sizeof(rt->last_reply), p);
405 pstrcat(rt->last_reply, sizeof(rt->last_reply), "\n");
406 }
407 line_count++;
408 }
409
410 if (rt->session_id[0] == '\0' && reply->session_id[0] != '\0')
411 pstrcpy(rt->session_id, sizeof(rt->session_id), reply->session_id);
412
413 content_length = reply->content_length;
414 if (content_length > 0) {
415 /* leave some room for a trailing '\0' (useful for simple parsing) */
416 content = av_malloc(content_length + 1);
417 url_read(rt->rtsp_hd, content, content_length);
418 content[content_length] = '\0';
419 }
420 if (content_ptr)
421 *content_ptr = content;
422}
423
424/* useful for modules: set RTSP callback function */
425
426void rtsp_set_callback(FFRTSPCallback *rtsp_cb)
427{
428 ff_rtsp_callback = rtsp_cb;
429}
430
431
432static int rtsp_read_header(AVFormatContext *s,
433 AVFormatParameters *ap)
434{
435 RTSPState *rt = s->priv_data;
436 char host[1024], path[1024], tcpname[1024], cmd[2048];
437 URLContext *rtsp_hd;
438 int port, i, ret, err;
439 RTSPHeader reply1, *reply = &reply1;
440 unsigned char *content = NULL;
441 AVStream *st;
442 RTSPStream *rtsp_st;
443 int protocol_mask;
444
445 rtsp_abort_req = 0;
446
447 /* extract hostname and port */
448 url_split(NULL, 0,
449 host, sizeof(host), &port, path, sizeof(path), s->filename);
450 if (port < 0)
451 port = RTSP_DEFAULT_PORT;
452
453 /* open the tcp connexion */
454 snprintf(tcpname, sizeof(tcpname), "tcp://%s:%d", host, port);
455 if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0)
456 return AVERROR_IO;
457 rt->rtsp_hd = rtsp_hd;
458 rt->seq = 0;
459
460 /* describe the stream */
461 snprintf(cmd, sizeof(cmd),
462 "DESCRIBE %s RTSP/1.0\n"
463 "Accept: application/sdp\n",
464 s->filename);
465 rtsp_send_cmd(s, cmd, reply, &content);
466 if (!content) {
467 err = AVERROR_INVALIDDATA;
468 goto fail;
469 }
470 if (reply->status_code != RTSP_STATUS_OK) {
471 err = AVERROR_INVALIDDATA;
472 goto fail;
473 }
474
475 /* now we got the SDP description, we parse it */
476 ret = sdp_parse(s, (const char *)content);
477 av_freep(&content);
478 if (ret < 0) {
479 err = AVERROR_INVALIDDATA;
480 goto fail;
481 }
482
483 protocol_mask = rtsp_default_protocols;
484
485 /* for each stream, make the setup request */
486 /* XXX: we assume the same server is used for the control of each
487 RTSP stream */
488 for(i=0;i<s->nb_streams;i++) {
489 AVInputFormat *fmt;
490 char transport[2048];
491
492 st = s->streams[i];
493 rtsp_st = st->priv_data;
494
495 /* compute available transports */
496 transport[0] = '\0';
497
498 /* RTP/UDP */
499 if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP)) {
500 fmt = &rtp_demux;
501 if (av_open_input_file(&rtsp_st->ic, "rtp://", fmt, 0, NULL) < 0) {
502 err = AVERROR_INVALIDDATA;
503 goto fail;
504 }
505
506 port = rtp_get_local_port(url_fileno(&rtsp_st->ic->pb));
507 if (transport[0] != '\0')
508 pstrcat(transport, sizeof(transport), ",");
509 snprintf(transport + strlen(transport), sizeof(transport) - strlen(transport) - 1,
510 "RTP/AVP/UDP;unicast;client_port=%d-%d",
511 port, port + 1);
512 }
513
514 /* RTP/TCP */
515 if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_TCP)) {
516 if (transport[0] != '\0')
517 pstrcat(transport, sizeof(transport), ",");
518 snprintf(transport + strlen(transport), sizeof(transport) - strlen(transport) - 1,
519 "RTP/AVP/TCP");
520 }
521
522 if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP_MULTICAST)) {
523 if (transport[0] != '\0')
524 pstrcat(transport, sizeof(transport), ",");
525 snprintf(transport + strlen(transport),
526 sizeof(transport) - strlen(transport) - 1,
527 "RTP/AVP/UDP;multicast");
528 }
529
530 snprintf(cmd, sizeof(cmd),
531 "SETUP %s RTSP/1.0\n"
532 "Transport: %s\n",
533 rtsp_st->control_url, transport);
534 rtsp_send_cmd(s, cmd, reply, NULL);
535 if (reply->status_code != RTSP_STATUS_OK ||
536 reply->nb_transports != 1) {
537 err = AVERROR_INVALIDDATA;
538 goto fail;
539 }
540
541 /* XXX: same protocol for all streams is required */
542 if (i > 0) {
543 if (reply->transports[0].protocol != rt->protocol) {
544 err = AVERROR_INVALIDDATA;
545 goto fail;
546 }
547 } else {
548 rt->protocol = reply->transports[0].protocol;
549 }
550
551 /* close RTP connection if not choosen */
552 if (reply->transports[0].protocol != RTSP_PROTOCOL_RTP_UDP &&
553 (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP))) {
554 av_close_input_file(rtsp_st->ic);
555 rtsp_st->ic = NULL;
556 }
557
558 switch(reply->transports[0].protocol) {
559 case RTSP_PROTOCOL_RTP_TCP:
560 fmt = &rtp_demux;
561 if (av_open_input_file(&rtsp_st->ic, "null", fmt, 0, NULL) < 0) {
562 err = AVERROR_INVALIDDATA;
563 goto fail;
564 }
565 rtsp_st->interleaved_min = reply->transports[0].interleaved_min;
566 rtsp_st->interleaved_max = reply->transports[0].interleaved_max;
567 break;
568
569 case RTSP_PROTOCOL_RTP_UDP:
570 {
571 char url[1024];
572
573 /* XXX: also use address if specified */
574 snprintf(url, sizeof(url), "rtp://%s:%d",
575 host, reply->transports[0].server_port_min);
576 if (rtp_set_remote_url(url_fileno(&rtsp_st->ic->pb), url) < 0) {
577 err = AVERROR_INVALIDDATA;
578 goto fail;
579 }
580 }
581 break;
582 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
583 {
584 char url[1024];
585 int ttl;
586
587 fmt = &rtp_demux;
588 ttl = reply->transports[0].ttl;
589 if (!ttl)
590 ttl = 16;
591 snprintf(url, sizeof(url), "rtp://%s:%d?multicast=1&ttl=%d",
592 host,
593 reply->transports[0].server_port_min,
594 ttl);
595 if (av_open_input_file(&rtsp_st->ic, url, fmt, 0, NULL) < 0) {
596 err = AVERROR_INVALIDDATA;
597 goto fail;
598 }
599 }
600 break;
601 }
602 }
603
604 /* use callback if available to extend setup */
605 if (ff_rtsp_callback) {
606 if (ff_rtsp_callback(RTSP_ACTION_CLIENT_SETUP, rt->session_id,
607 NULL, 0, rt->last_reply) < 0) {
608 err = AVERROR_INVALIDDATA;
609 goto fail;
610 }
611 }
612
613 /* start playing */
614 snprintf(cmd, sizeof(cmd),
615 "PLAY %s RTSP/1.0\n",
616 s->filename);
617 rtsp_send_cmd(s, cmd, reply, NULL);
618 if (reply->status_code != RTSP_STATUS_OK) {
619 err = AVERROR_INVALIDDATA;
620 goto fail;
621 }
622
623 /* open TCP with bufferized input */
624 if (rt->protocol == RTSP_PROTOCOL_RTP_TCP) {
625 if (url_fdopen(&rt->rtsp_gb, rt->rtsp_hd) < 0) {
626 err = AVERROR_NOMEM;
627 goto fail;
628 }
629 }
630
631 return 0;
632 fail:
633 for(i=0;i<s->nb_streams;i++) {
634 st = s->streams[i];
635 rtsp_st = st->priv_data;
636 if (rtsp_st) {
637 if (rtsp_st->ic)
638 av_close_input_file(rtsp_st->ic);
639 }
640 av_free(rtsp_st);
641 }
642 av_freep(&content);
643 url_close(rt->rtsp_hd);
644 return err;
645}
646
647static int tcp_read_packet(AVFormatContext *s,
648 AVPacket *pkt)
649{
650 RTSPState *rt = s->priv_data;
651 ByteIOContext *rtsp_gb = &rt->rtsp_gb;
652 int c, id, len, i, ret;
653 AVStream *st;
654 RTSPStream *rtsp_st;
655 char buf[RTP_MAX_PACKET_LENGTH];
656
657 redo:
658 for(;;) {
659 c = url_fgetc(rtsp_gb);
660 if (c == URL_EOF)
661 return AVERROR_IO;
662 if (c == '$')
663 break;
664 }
665 id = get_byte(rtsp_gb);
666 len = get_be16(rtsp_gb);
667 if (len > RTP_MAX_PACKET_LENGTH || len < 12)
668 goto redo;
669 /* get the data */
670 get_buffer(rtsp_gb, buf, len);
671
672 /* find the matching stream */
673 for(i = 0; i < s->nb_streams; i++) {
674 st = s->streams[i];
675 rtsp_st = st->priv_data;
676 if (i >= rtsp_st->interleaved_min &&
677 i <= rtsp_st->interleaved_max)
678 goto found;
679 }
680 goto redo;
681 found:
682 ret = rtp_parse_packet(rtsp_st->ic, pkt, buf, len);
683 if (ret < 0)
684 goto redo;
685 pkt->stream_index = i;
686 return ret;
687}
688
689/* NOTE: output one packet at a time. May need to add a small fifo */
690static int udp_read_packet(AVFormatContext *s,
691 AVPacket *pkt)
692{
693 AVFormatContext *ic;
694 AVStream *st;
695 RTSPStream *rtsp_st;
696 fd_set rfds;
697 int fd1, fd2, fd_max, n, i, ret;
698 char buf[RTP_MAX_PACKET_LENGTH];
699 struct timeval tv;
700
701 for(;;) {
702 if (rtsp_abort_req)
703 return -EIO;
704 FD_ZERO(&rfds);
705 fd_max = -1;
706 for(i = 0; i < s->nb_streams; i++) {
707 st = s->streams[i];
708 rtsp_st = st->priv_data;
709 ic = rtsp_st->ic;
710 /* currently, we cannot probe RTCP handle because of blocking restrictions */
711 rtp_get_file_handles(url_fileno(&ic->pb), &fd1, &fd2);
712 if (fd1 > fd_max)
713 fd_max = fd1;
714 FD_SET(fd1, &rfds);
715 }
716 /* XXX: also add proper API to abort */
717 tv.tv_sec = 0;
718 tv.tv_usec = 500000;
719 n = select(fd_max + 1, &rfds, NULL, NULL, &tv);
720 if (n > 0) {
721 for(i = 0; i < s->nb_streams; i++) {
722 st = s->streams[i];
723 rtsp_st = st->priv_data;
724 ic = rtsp_st->ic;
725 rtp_get_file_handles(url_fileno(&ic->pb), &fd1, &fd2);
726 if (FD_ISSET(fd1, &rfds)) {
727 ret = url_read(url_fileno(&ic->pb), buf, sizeof(buf));
728 if (ret >= 0 &&
729 rtp_parse_packet(ic, pkt, buf, ret) == 0) {
730 pkt->stream_index = i;
731 return ret;
732 }
733 }
734 }
735 }
736 }
737}
738
739static int rtsp_read_packet(AVFormatContext *s,
740 AVPacket *pkt)
741{
742 RTSPState *rt = s->priv_data;
743 int ret;
744
745 switch(rt->protocol) {
746 default:
747 case RTSP_PROTOCOL_RTP_TCP:
748 ret = tcp_read_packet(s, pkt);
749 break;
750 case RTSP_PROTOCOL_RTP_UDP:
751 ret = udp_read_packet(s, pkt);
752 break;
753 }
754 return ret;
755}
756
757static int rtsp_read_close(AVFormatContext *s)
758{
759 RTSPState *rt = s->priv_data;
760 AVStream *st;
761 RTSPStream *rtsp_st;
762 RTSPHeader reply1, *reply = &reply1;
763 int i;
764 char cmd[1024];
765
766 /* NOTE: it is valid to flush the buffer here */
767 if (rt->protocol == RTSP_PROTOCOL_RTP_TCP) {
768 url_fclose(&rt->rtsp_gb);
769 }
770
771 snprintf(cmd, sizeof(cmd),
772 "TEARDOWN %s RTSP/1.0\n",
773 s->filename);
774 rtsp_send_cmd(s, cmd, reply, NULL);
775
776 if (ff_rtsp_callback) {
777 ff_rtsp_callback(RTSP_ACTION_CLIENT_TEARDOWN, rt->session_id,
778 NULL, 0, NULL);
779 }
780
781 for(i=0;i<s->nb_streams;i++) {
782 st = s->streams[i];
783 rtsp_st = st->priv_data;
784 if (rtsp_st) {
785 if (rtsp_st->ic)
786 av_close_input_file(rtsp_st->ic);
787 }
788 av_free(rtsp_st);
789 }
790 url_close(rt->rtsp_hd);
791 return 0;
792}
793
794static AVInputFormat rtsp_demux = {
795 "rtsp",
796 "RTSP input format",
797 sizeof(RTSPState),
798 rtsp_probe,
799 rtsp_read_header,
800 rtsp_read_packet,
801 rtsp_read_close,
802 flags: AVFMT_NOFILE,
803};
804
805/* dummy redirector format (used directly in av_open_input_file now) */
806static int redir_probe(AVProbeData *pd)
807{
808 const char *p;
809 p = pd->buf;
810 while (redir_isspace(*p))
811 p++;
812 if (strstart(p, "http://", NULL) ||
813 strstart(p, "rtsp://", NULL))
814 return AVPROBE_SCORE_MAX;
815 return 0;
816}
817
818/* called from utils.c */
819int redir_open(AVFormatContext **ic_ptr, ByteIOContext *f)
820{
821 char buf[4096], *q;
822 int c;
823 AVFormatContext *ic = NULL;
824
825 /* parse each URL and try to open it */
826 c = url_fgetc(f);
827 while (c != URL_EOF) {
828 /* skip spaces */
829 for(;;) {
830 if (!redir_isspace(c))
831 break;
832 c = url_fgetc(f);
833 }
834 if (c == URL_EOF)
835 break;
836 /* record url */
837 q = buf;
838 for(;;) {
839 if (c == URL_EOF || redir_isspace(c))
840 break;
841 if ((q - buf) < sizeof(buf) - 1)
842 *q++ = c;
843 c = url_fgetc(f);
844 }
845 *q = '\0';
846 //printf("URL='%s'\n", buf);
847 /* try to open the media file */
848 if (av_open_input_file(&ic, buf, NULL, 0, NULL) == 0)
849 break;
850 }
851 *ic_ptr = ic;
852 if (!ic)
853 return AVERROR_IO;
854 else
855 return 0;
856}
857
858AVInputFormat redir_demux = {
859 "redir",
860 "Redirector format",
861 0,
862 redir_probe,
863 NULL,
864 NULL,
865 NULL,
866};
867
868int rtsp_init(void)
869{
870 av_register_input_format(&rtsp_demux);
871 av_register_input_format(&redir_demux);
872 return 0;
873}