3 * Copyright (c) 2002 Fabrice Bellard.
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.
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.
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
22 #include <netinet/in.h>
23 #include <sys/socket.h>
24 #include <arpa/inet.h>
28 typedef struct RTSPState
{
29 URLContext
*rtsp_hd
; /* RTSP TCP connexion handle */
30 ByteIOContext rtsp_gb
;
31 int seq
; /* RTSP command sequence number */
33 enum RTSPProtocol protocol
;
34 char last_reply
[2048]; /* XXX: allocate ? */
37 typedef struct RTSPStream
{
39 int interleaved_min
, interleaved_max
; /* interleave ids, if TCP transport */
40 char control_url
[1024]; /* url for this stream */
43 /* suppress this hack */
44 int rtsp_abort_req
= 0;
46 /* XXX: currently, the only way to change the protocols consists in
47 changing this variable */
48 int rtsp_default_protocols
= (1 << RTSP_PROTOCOL_RTP_TCP
) | (1 << RTSP_PROTOCOL_RTP_UDP
) | (1 << RTSP_PROTOCOL_RTP_UDP_MULTICAST
);
50 FFRTSPCallback
*ff_rtsp_callback
= NULL
;
52 static int rtsp_probe(AVProbeData
*p
)
54 if (strstart(p
->filename
, "rtsp:", NULL
))
55 return AVPROBE_SCORE_MAX
;
59 static int redir_isspace(int c
)
61 return (c
== ' ' || c
== '\t' || c
== '\n' || c
== '\r');
64 static void skip_spaces(const char **pp
)
68 while (redir_isspace(*p
))
73 static void get_word_sep(char *buf
, int buf_size
, const char *sep
,
82 while (!strchr(sep
, *p
) && *p
!= '\0') {
83 if ((q
- buf
) < buf_size
- 1)
92 static void get_word(char *buf
, int buf_size
, const char **pp
)
100 while (!redir_isspace(*p
) && *p
!= '\0') {
101 if ((q
- buf
) < buf_size
- 1)
110 static void sdp_parse_line(AVFormatContext
*s
,
111 int letter
, const char *buf
)
113 char buf1
[64], st_type
[64];
115 int codec_type
, payload_type
;
120 printf("sdp: %c='%s'\n", letter
, buf
);
126 pstrcpy(s
->title
, sizeof(s
->title
), p
);
129 if (s
->nb_streams
== 0) {
130 pstrcpy(s
->comment
, sizeof(s
->comment
), p
);
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
;
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
);
150 rtsp_st
= av_mallocz(sizeof(RTSPStream
));
153 st
= av_new_stream(s
, s
->nb_streams
);
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
);
163 if (strstart(p
, "control:", &p
) && s
->nb_streams
> 0) {
165 /* get the control url */
166 st
= s
->streams
[s
->nb_streams
- 1];
167 rtsp_st
= st
->priv_data
;
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
);
176 pstrcpy(rtsp_st
->control_url
, sizeof(rtsp_st
->control_url
), p
);
183 int sdp_parse(AVFormatContext
*s
, const char *content
)
199 /* get the content */
201 while (*p
!= '\n' && *p
!= '\0') {
202 if ((q
- buf
) < sizeof(buf
) - 1)
207 sdp_parse_line(s
, letter
, buf
);
209 while (*p
!= '\n' && *p
!= '\0')
217 static void rtsp_parse_range(int *min_ptr
, int *max_ptr
, const char **pp
)
224 v
= strtol(p
, (char **)&p
, 10);
228 v
= strtol(p
, (char **)&p
, 10);
237 /* XXX: only one transport specification is parsed */
238 static void rtsp_parse_transport(RTSPHeader
*reply
, const char *p
)
240 char transport_protocol
[16];
242 char lower_transport
[16];
244 RTSPTransportField
*th
;
247 reply
->nb_transports
= 0;
254 th
= &reply
->transports
[reply
->nb_transports
];
256 get_word_sep(transport_protocol
, sizeof(transport_protocol
),
260 get_word_sep(profile
, sizeof(profile
), "/;,", &p
);
261 lower_transport
[0] = '\0';
263 get_word_sep(lower_transport
, sizeof(lower_transport
),
266 if (!strcmp(lower_transport
, "TCP"))
267 th
->protocol
= RTSP_PROTOCOL_RTP_TCP
;
269 th
->protocol
= RTSP_PROTOCOL_RTP_UDP
;
273 /* get each parameter */
274 while (*p
!= '\0' && *p
!= ',') {
275 get_word_sep(parameter
, sizeof(parameter
), "=;,", &p
);
276 if (!strcmp(parameter
, "port")) {
279 rtsp_parse_range(&th
->port_min
, &th
->port_max
, &p
);
281 } else if (!strcmp(parameter
, "client_port")) {
284 rtsp_parse_range(&th
->client_port_min
,
285 &th
->client_port_max
, &p
);
287 } else if (!strcmp(parameter
, "server_port")) {
290 rtsp_parse_range(&th
->server_port_min
,
291 &th
->server_port_max
, &p
);
293 } else if (!strcmp(parameter
, "interleaved")) {
296 rtsp_parse_range(&th
->interleaved_min
,
297 &th
->interleaved_max
, &p
);
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")) {
305 th
->ttl
= strtol(p
, (char **)&p
, 10);
307 } else if (!strcmp(parameter
, "destination")) {
308 struct in_addr ipaddr
;
312 get_word_sep(buf
, sizeof(buf
), ";,", &p
);
313 if (inet_aton(buf
, &ipaddr
))
314 th
->destination
= ntohl(ipaddr
.s_addr
);
317 while (*p
!= ';' && *p
!= '\0' && *p
!= ',')
325 reply
->nb_transports
++;
329 void rtsp_parse_line(RTSPHeader
*reply
, const char *buf
)
333 /* NOTE: we do case independent match for broken servers */
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);
347 static void rtsp_send_cmd(AVFormatContext
*s
,
348 const char *cmd
, RTSPHeader
*reply
,
349 unsigned char **content_ptr
)
351 RTSPState
*rt
= s
->priv_data
;
352 char buf
[4096], buf1
[1024], *q
;
355 int content_length
, line_count
;
356 unsigned char *content
= NULL
;
358 memset(reply
, 0, sizeof(RTSPHeader
));
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
);
368 pstrcat(buf
, sizeof(buf
), "\n");
370 printf("Sending:\n%s--\n", buf
);
372 url_write(rt
->rtsp_hd
, buf
, strlen(buf
));
374 /* parse reply (XXX: use buffers) */
376 rt
->last_reply
[0] = '\0';
380 if (url_read(rt
->rtsp_hd
, &ch
, 1) == 0)
385 if ((q
- buf
) < sizeof(buf
) - 1)
391 printf("line='%s'\n", buf
);
393 /* test if last line */
397 if (line_count
== 0) {
399 get_word(buf1
, sizeof(buf1
), &p
);
400 get_word(buf1
, sizeof(buf1
), &p
);
401 reply
->status_code
= atoi(buf1
);
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");
410 if (rt
->session_id
[0] == '\0' && reply
->session_id
[0] != '\0')
411 pstrcpy(rt
->session_id
, sizeof(rt
->session_id
), reply
->session_id
);
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';
421 *content_ptr
= content
;
424 /* useful for modules: set RTSP callback function */
426 void rtsp_set_callback(FFRTSPCallback
*rtsp_cb
)
428 ff_rtsp_callback
= rtsp_cb
;
432 static int rtsp_read_header(AVFormatContext
*s
,
433 AVFormatParameters
*ap
)
435 RTSPState
*rt
= s
->priv_data
;
436 char host
[1024], path
[1024], tcpname
[1024], cmd
[2048];
438 int port
, i
, ret
, err
;
439 RTSPHeader reply1
, *reply
= &reply1
;
440 unsigned char *content
= NULL
;
447 /* extract hostname and port */
449 host
, sizeof(host
), &port
, path
, sizeof(path
), s
->filename
);
451 port
= RTSP_DEFAULT_PORT
;
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)
457 rt
->rtsp_hd
= rtsp_hd
;
460 /* describe the stream */
461 snprintf(cmd
, sizeof(cmd
),
462 "DESCRIBE %s RTSP/1.0\n"
463 "Accept: application/sdp\n",
465 rtsp_send_cmd(s
, cmd
, reply
, &content
);
467 err
= AVERROR_INVALIDDATA
;
470 if (reply
->status_code
!= RTSP_STATUS_OK
) {
471 err
= AVERROR_INVALIDDATA
;
475 /* now we got the SDP description, we parse it */
476 ret
= sdp_parse(s
, (const char *)content
);
479 err
= AVERROR_INVALIDDATA
;
483 protocol_mask
= rtsp_default_protocols
;
485 /* for each stream, make the setup request */
486 /* XXX: we assume the same server is used for the control of each
488 for(i
=0;i
<s
->nb_streams
;i
++) {
490 char transport
[2048];
493 rtsp_st
= st
->priv_data
;
495 /* compute available transports */
499 if (protocol_mask
& (1 << RTSP_PROTOCOL_RTP_UDP
)) {
501 if (av_open_input_file(&rtsp_st
->ic
, "rtp://", fmt
, 0, NULL
) < 0) {
502 err
= AVERROR_INVALIDDATA
;
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",
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,
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");
530 snprintf(cmd
, sizeof(cmd
),
531 "SETUP %s RTSP/1.0\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
;
541 /* XXX: same protocol for all streams is required */
543 if (reply
->transports
[0].protocol
!= rt
->protocol
) {
544 err
= AVERROR_INVALIDDATA
;
548 rt
->protocol
= reply
->transports
[0].protocol
;
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
);
558 switch(reply
->transports
[0].protocol
) {
559 case RTSP_PROTOCOL_RTP_TCP
:
561 if (av_open_input_file(&rtsp_st
->ic
, "null", fmt
, 0, NULL
) < 0) {
562 err
= AVERROR_INVALIDDATA
;
565 rtsp_st
->interleaved_min
= reply
->transports
[0].interleaved_min
;
566 rtsp_st
->interleaved_max
= reply
->transports
[0].interleaved_max
;
569 case RTSP_PROTOCOL_RTP_UDP
:
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
;
582 case RTSP_PROTOCOL_RTP_UDP_MULTICAST
:
588 ttl
= reply
->transports
[0].ttl
;
591 snprintf(url
, sizeof(url
), "rtp://%s:%d?multicast=1&ttl=%d",
593 reply
->transports
[0].server_port_min
,
595 if (av_open_input_file(&rtsp_st
->ic
, url
, fmt
, 0, NULL
) < 0) {
596 err
= AVERROR_INVALIDDATA
;
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
;
614 snprintf(cmd
, sizeof(cmd
),
615 "PLAY %s RTSP/1.0\n",
617 rtsp_send_cmd(s
, cmd
, reply
, NULL
);
618 if (reply
->status_code
!= RTSP_STATUS_OK
) {
619 err
= AVERROR_INVALIDDATA
;
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) {
633 for(i
=0;i
<s
->nb_streams
;i
++) {
635 rtsp_st
= st
->priv_data
;
638 av_close_input_file(rtsp_st
->ic
);
643 url_close(rt
->rtsp_hd
);
647 static int tcp_read_packet(AVFormatContext
*s
,
650 RTSPState
*rt
= s
->priv_data
;
651 ByteIOContext
*rtsp_gb
= &rt
->rtsp_gb
;
652 int c
, id
, len
, i
, ret
;
655 char buf
[RTP_MAX_PACKET_LENGTH
];
659 c
= url_fgetc(rtsp_gb
);
665 id
= get_byte(rtsp_gb
);
666 len
= get_be16(rtsp_gb
);
667 if (len
> RTP_MAX_PACKET_LENGTH
|| len
< 12)
670 get_buffer(rtsp_gb
, buf
, len
);
672 /* find the matching stream */
673 for(i
= 0; i
< s
->nb_streams
; i
++) {
675 rtsp_st
= st
->priv_data
;
676 if (i
>= rtsp_st
->interleaved_min
&&
677 i
<= rtsp_st
->interleaved_max
)
682 ret
= rtp_parse_packet(rtsp_st
->ic
, pkt
, buf
, len
);
685 pkt
->stream_index
= i
;
689 /* NOTE: output one packet at a time. May need to add a small fifo */
690 static int udp_read_packet(AVFormatContext
*s
,
697 int fd1
, fd2
, fd_max
, n
, i
, ret
;
698 char buf
[RTP_MAX_PACKET_LENGTH
];
706 for(i
= 0; i
< s
->nb_streams
; i
++) {
708 rtsp_st
= st
->priv_data
;
710 /* currently, we cannot probe RTCP handle because of blocking restrictions */
711 rtp_get_file_handles(url_fileno(&ic
->pb
), &fd1
, &fd2
);
716 /* XXX: also add proper API to abort */
719 n
= select(fd_max
+ 1, &rfds
, NULL
, NULL
, &tv
);
721 for(i
= 0; i
< s
->nb_streams
; i
++) {
723 rtsp_st
= st
->priv_data
;
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
));
729 rtp_parse_packet(ic
, pkt
, buf
, ret
) == 0) {
730 pkt
->stream_index
= i
;
739 static int rtsp_read_packet(AVFormatContext
*s
,
742 RTSPState
*rt
= s
->priv_data
;
745 switch(rt
->protocol
) {
747 case RTSP_PROTOCOL_RTP_TCP
:
748 ret
= tcp_read_packet(s
, pkt
);
750 case RTSP_PROTOCOL_RTP_UDP
:
751 ret
= udp_read_packet(s
, pkt
);
757 static int rtsp_read_close(AVFormatContext
*s
)
759 RTSPState
*rt
= s
->priv_data
;
762 RTSPHeader reply1
, *reply
= &reply1
;
766 /* NOTE: it is valid to flush the buffer here */
767 if (rt
->protocol
== RTSP_PROTOCOL_RTP_TCP
) {
768 url_fclose(&rt
->rtsp_gb
);
771 snprintf(cmd
, sizeof(cmd
),
772 "TEARDOWN %s RTSP/1.0\n",
774 rtsp_send_cmd(s
, cmd
, reply
, NULL
);
776 if (ff_rtsp_callback
) {
777 ff_rtsp_callback(RTSP_ACTION_CLIENT_TEARDOWN
, rt
->session_id
,
781 for(i
=0;i
<s
->nb_streams
;i
++) {
783 rtsp_st
= st
->priv_data
;
786 av_close_input_file(rtsp_st
->ic
);
790 url_close(rt
->rtsp_hd
);
794 static AVInputFormat rtsp_demux
= {
805 /* dummy redirector format (used directly in av_open_input_file now) */
806 static int redir_probe(AVProbeData
*pd
)
810 while (redir_isspace(*p
))
812 if (strstart(p
, "http://", NULL
) ||
813 strstart(p
, "rtsp://", NULL
))
814 return AVPROBE_SCORE_MAX
;
818 /* called from utils.c */
819 int redir_open(AVFormatContext
**ic_ptr
, ByteIOContext
*f
)
823 AVFormatContext
*ic
= NULL
;
825 /* parse each URL and try to open it */
827 while (c
!= URL_EOF
) {
830 if (!redir_isspace(c
))
839 if (c
== URL_EOF
|| redir_isspace(c
))
841 if ((q
- buf
) < sizeof(buf
) - 1)
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)
858 AVInputFormat redir_demux
= {
870 av_register_input_format(&rtsp_demux
);
871 av_register_input_format(&redir_demux
);