3 * Copyright (c) 2002 Fabrice Bellard.
5 * This file is part of FFmpeg.
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.
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.
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
22 /* needed by inet_aton() */
25 #include "libavutil/avstring.h"
29 #ifdef HAVE_SYS_SELECT_H
30 #include <sys/select.h>
36 #include "rtp_internal.h"
40 //#define DEBUG_RTP_TCP
42 enum RTSPClientState
{
49 RTSP_SERVER_RTP
, /*< Standard-compliant RTP-server */
50 RTSP_SERVER_REAL
, /*< Realmedia-style server */
54 typedef struct RTSPState
{
55 URLContext
*rtsp_hd
; /* RTSP TCP connexion handle */
57 struct RTSPStream
**rtsp_streams
;
59 enum RTSPClientState state
;
60 int64_t seek_timestamp
;
62 /* XXX: currently we use unbuffered input */
63 // ByteIOContext rtsp_gb;
64 int seq
; /* RTSP command sequence number */
66 enum RTSPProtocol protocol
;
67 enum RTSPServerType server_type
;
68 char last_reply
[2048]; /* XXX: allocate ? */
69 RTPDemuxContext
*cur_rtp
;
70 int need_subscription
;
73 typedef struct RTSPStream
{
74 URLContext
*rtp_handle
; /* RTP stream handle */
75 RTPDemuxContext
*rtp_ctx
; /* RTP parse context */
77 int stream_index
; /* corresponding stream index, if any. -1 if none (MPEG2TS case) */
78 int interleaved_min
, interleaved_max
; /* interleave ids, if TCP transport */
79 char control_url
[1024]; /* url for this stream (from SDP) */
81 int sdp_port
; /* port (from SDP content - not used in RTSP) */
82 struct in_addr sdp_ip
; /* IP address (from SDP content - not used in RTSP) */
83 int sdp_ttl
; /* IP TTL (from SDP content - not used in RTSP) */
84 int sdp_payload_type
; /* payload type - only used in SDP */
85 rtp_payload_data_t rtp_payload_data
; /* rtp payload parsing infos from SDP */
87 RTPDynamicProtocolHandler
*dynamic_handler
; ///< Only valid if it's a dynamic protocol. (This is the handler structure)
88 void *dynamic_protocol_context
; ///< Only valid if it's a dynamic protocol. (This is any private data associated with the dynamic protocol)
91 static int rtsp_read_play(AVFormatContext
*s
);
93 /* XXX: currently, the only way to change the protocols consists in
94 changing this variable */
96 #if LIBAVFORMAT_VERSION_INT < (53 << 16)
97 int rtsp_default_protocols
= (1 << RTSP_PROTOCOL_RTP_UDP
);
100 static int rtsp_probe(AVProbeData
*p
)
102 if (av_strstart(p
->filename
, "rtsp:", NULL
))
103 return AVPROBE_SCORE_MAX
;
107 static int redir_isspace(int c
)
109 return c
== ' ' || c
== '\t' || c
== '\n' || c
== '\r';
112 static void skip_spaces(const char **pp
)
116 while (redir_isspace(*p
))
121 static void get_word_sep(char *buf
, int buf_size
, const char *sep
,
132 while (!strchr(sep
, *p
) && *p
!= '\0') {
133 if ((q
- buf
) < buf_size
- 1)
142 static void get_word(char *buf
, int buf_size
, const char **pp
)
150 while (!redir_isspace(*p
) && *p
!= '\0') {
151 if ((q
- buf
) < buf_size
- 1)
160 /* parse the rtpmap description: <codec_name>/<clock_rate>[/<other
162 static int sdp_parse_rtpmap(AVCodecContext
*codec
, RTSPStream
*rtsp_st
, int payload_type
, const char *p
)
169 /* Loop into AVRtpDynamicPayloadTypes[] and AVRtpPayloadTypes[] and
170 see if we can handle this kind of payload */
171 get_word_sep(buf
, sizeof(buf
), "/", &p
);
172 if (payload_type
>= RTP_PT_PRIVATE
) {
173 RTPDynamicProtocolHandler
*handler
= RTPFirstDynamicPayloadHandler
;
175 if (!strcmp(buf
, handler
->enc_name
) && (codec
->codec_type
== handler
->codec_type
)) {
176 codec
->codec_id
= handler
->codec_id
;
177 rtsp_st
->dynamic_handler
= handler
;
179 rtsp_st
->dynamic_protocol_context
= handler
->open();
183 handler
= handler
->next
;
186 /* We are in a standard case ( from http://www.iana.org/assignments/rtp-parameters) */
187 /* search into AVRtpPayloadTypes[] */
188 codec
->codec_id
= ff_rtp_codec_id(buf
, codec
->codec_type
);
191 c
= avcodec_find_decoder(codec
->codec_id
);
195 c_name
= (char *)NULL
;
198 get_word_sep(buf
, sizeof(buf
), "/", &p
);
200 switch (codec
->codec_type
) {
201 case CODEC_TYPE_AUDIO
:
202 av_log(codec
, AV_LOG_DEBUG
, " audio codec set to : %s\n", c_name
);
203 codec
->sample_rate
= RTSP_DEFAULT_AUDIO_SAMPLERATE
;
204 codec
->channels
= RTSP_DEFAULT_NB_AUDIO_CHANNELS
;
206 codec
->sample_rate
= i
;
207 get_word_sep(buf
, sizeof(buf
), "/", &p
);
211 // TODO: there is a bug here; if it is a mono stream, and less than 22000Hz, faad upconverts to stereo and twice the
212 // frequency. No problem, but the sample rate is being set here by the sdp line. Upcoming patch forthcoming. (rdm)
214 av_log(codec
, AV_LOG_DEBUG
, " audio samplerate set to : %i\n", codec
->sample_rate
);
215 av_log(codec
, AV_LOG_DEBUG
, " audio channels set to : %i\n", codec
->channels
);
217 case CODEC_TYPE_VIDEO
:
218 av_log(codec
, AV_LOG_DEBUG
, " video codec set to : %s\n", c_name
);
229 /* return the length and optionnaly the data */
230 static int hex_to_data(uint8_t *data
, const char *p
)
240 c
= toupper((unsigned char)*p
++);
241 if (c
>= '0' && c
<= '9')
243 else if (c
>= 'A' && c
<= 'F')
258 static void sdp_parse_fmtp_config(AVCodecContext
*codec
, char *attr
, char *value
)
260 switch (codec
->codec_id
) {
263 if (!strcmp(attr
, "config")) {
264 /* decode the hexa encoded parameter */
265 int len
= hex_to_data(NULL
, value
);
266 codec
->extradata
= av_mallocz(len
+ FF_INPUT_BUFFER_PADDING_SIZE
);
267 if (!codec
->extradata
)
269 codec
->extradata_size
= len
;
270 hex_to_data(codec
->extradata
, value
);
279 typedef struct attrname_map
286 /* All known fmtp parmeters and the corresping RTPAttrTypeEnum */
287 #define ATTR_NAME_TYPE_INT 0
288 #define ATTR_NAME_TYPE_STR 1
289 static attrname_map_t attr_names
[]=
291 {"SizeLength", ATTR_NAME_TYPE_INT
, offsetof(rtp_payload_data_t
, sizelength
)},
292 {"IndexLength", ATTR_NAME_TYPE_INT
, offsetof(rtp_payload_data_t
, indexlength
)},
293 {"IndexDeltaLength", ATTR_NAME_TYPE_INT
, offsetof(rtp_payload_data_t
, indexdeltalength
)},
294 {"profile-level-id", ATTR_NAME_TYPE_INT
, offsetof(rtp_payload_data_t
, profile_level_id
)},
295 {"StreamType", ATTR_NAME_TYPE_INT
, offsetof(rtp_payload_data_t
, streamtype
)},
296 {"mode", ATTR_NAME_TYPE_STR
, offsetof(rtp_payload_data_t
, mode
)},
300 /** parse the attribute line from the fmtp a line of an sdp resonse. This is broken out as a function
301 * because it is used in rtp_h264.c, which is forthcoming.
303 int rtsp_next_attr_and_value(const char **p
, char *attr
, int attr_size
, char *value
, int value_size
)
308 get_word_sep(attr
, attr_size
, "=", p
);
311 get_word_sep(value
, value_size
, ";", p
);
319 /* parse a SDP line and save stream attributes */
320 static void sdp_parse_fmtp(AVStream
*st
, const char *p
)
326 RTSPStream
*rtsp_st
= st
->priv_data
;
327 AVCodecContext
*codec
= st
->codec
;
328 rtp_payload_data_t
*rtp_payload_data
= &rtsp_st
->rtp_payload_data
;
330 /* loop on each attribute */
331 while(rtsp_next_attr_and_value(&p
, attr
, sizeof(attr
), value
, sizeof(value
)))
333 /* grab the codec extra_data from the config parameter of the fmtp line */
334 sdp_parse_fmtp_config(codec
, attr
, value
);
335 /* Looking for a known attribute */
336 for (i
= 0; attr_names
[i
].str
; ++i
) {
337 if (!strcasecmp(attr
, attr_names
[i
].str
)) {
338 if (attr_names
[i
].type
== ATTR_NAME_TYPE_INT
)
339 *(int *)((char *)rtp_payload_data
+ attr_names
[i
].offset
) = atoi(value
);
340 else if (attr_names
[i
].type
== ATTR_NAME_TYPE_STR
)
341 *(char **)((char *)rtp_payload_data
+ attr_names
[i
].offset
) = av_strdup(value
);
347 /** Parse a string \p in the form of Range:npt=xx-xx, and determine the start
349 * Used for seeking in the rtp stream.
351 static void rtsp_parse_range_npt(const char *p
, int64_t *start
, int64_t *end
)
356 if (!av_stristart(p
, "npt=", &p
))
359 *start
= AV_NOPTS_VALUE
;
360 *end
= AV_NOPTS_VALUE
;
362 get_word_sep(buf
, sizeof(buf
), "-", &p
);
363 *start
= parse_date(buf
, 1);
366 get_word_sep(buf
, sizeof(buf
), "-", &p
);
367 *end
= parse_date(buf
, 1);
369 // av_log(NULL, AV_LOG_DEBUG, "Range Start: %lld\n", *start);
370 // av_log(NULL, AV_LOG_DEBUG, "Range End: %lld\n", *end);
373 typedef struct SDPParseState
{
375 struct in_addr default_ip
;
379 static void sdp_parse_line(AVFormatContext
*s
, SDPParseState
*s1
,
380 int letter
, const char *buf
)
382 RTSPState
*rt
= s
->priv_data
;
383 char buf1
[64], st_type
[64];
385 int codec_type
, payload_type
, i
;
388 struct in_addr sdp_ip
;
392 printf("sdp: %c='%s'\n", letter
, buf
);
398 get_word(buf1
, sizeof(buf1
), &p
);
399 if (strcmp(buf1
, "IN") != 0)
401 get_word(buf1
, sizeof(buf1
), &p
);
402 if (strcmp(buf1
, "IP4") != 0)
404 get_word_sep(buf1
, sizeof(buf1
), "/", &p
);
405 if (inet_aton(buf1
, &sdp_ip
) == 0)
410 get_word_sep(buf1
, sizeof(buf1
), "/", &p
);
413 if (s
->nb_streams
== 0) {
414 s1
->default_ip
= sdp_ip
;
415 s1
->default_ttl
= ttl
;
417 st
= s
->streams
[s
->nb_streams
- 1];
418 rtsp_st
= st
->priv_data
;
419 rtsp_st
->sdp_ip
= sdp_ip
;
420 rtsp_st
->sdp_ttl
= ttl
;
424 av_strlcpy(s
->title
, p
, sizeof(s
->title
));
427 if (s
->nb_streams
== 0) {
428 av_strlcpy(s
->comment
, p
, sizeof(s
->comment
));
434 get_word(st_type
, sizeof(st_type
), &p
);
435 if (!strcmp(st_type
, "audio")) {
436 codec_type
= CODEC_TYPE_AUDIO
;
437 } else if (!strcmp(st_type
, "video")) {
438 codec_type
= CODEC_TYPE_VIDEO
;
442 rtsp_st
= av_mallocz(sizeof(RTSPStream
));
445 rtsp_st
->stream_index
= -1;
446 dynarray_add(&rt
->rtsp_streams
, &rt
->nb_rtsp_streams
, rtsp_st
);
448 rtsp_st
->sdp_ip
= s1
->default_ip
;
449 rtsp_st
->sdp_ttl
= s1
->default_ttl
;
451 get_word(buf1
, sizeof(buf1
), &p
); /* port */
452 rtsp_st
->sdp_port
= atoi(buf1
);
454 get_word(buf1
, sizeof(buf1
), &p
); /* protocol (ignored) */
456 /* XXX: handle list of formats */
457 get_word(buf1
, sizeof(buf1
), &p
); /* format list */
458 rtsp_st
->sdp_payload_type
= atoi(buf1
);
460 if (!strcmp(ff_rtp_enc_name(rtsp_st
->sdp_payload_type
), "MP2T")) {
461 /* no corresponding stream */
463 st
= av_new_stream(s
, 0);
466 st
->priv_data
= rtsp_st
;
467 rtsp_st
->stream_index
= st
->index
;
468 st
->codec
->codec_type
= codec_type
;
469 if (rtsp_st
->sdp_payload_type
< RTP_PT_PRIVATE
) {
470 /* if standard payload type, we can find the codec right now */
471 rtp_get_codec_info(st
->codec
, rtsp_st
->sdp_payload_type
);
474 /* put a default control url */
475 av_strlcpy(rtsp_st
->control_url
, s
->filename
, sizeof(rtsp_st
->control_url
));
478 if (av_strstart(p
, "control:", &p
) && s
->nb_streams
> 0) {
480 /* get the control url */
481 st
= s
->streams
[s
->nb_streams
- 1];
482 rtsp_st
= st
->priv_data
;
484 /* XXX: may need to add full url resolution */
485 url_split(proto
, sizeof(proto
), NULL
, 0, NULL
, 0, NULL
, NULL
, 0, p
);
486 if (proto
[0] == '\0') {
487 /* relative control URL */
488 av_strlcat(rtsp_st
->control_url
, "/", sizeof(rtsp_st
->control_url
));
489 av_strlcat(rtsp_st
->control_url
, p
, sizeof(rtsp_st
->control_url
));
491 av_strlcpy(rtsp_st
->control_url
, p
, sizeof(rtsp_st
->control_url
));
493 } else if (av_strstart(p
, "rtpmap:", &p
)) {
494 /* NOTE: rtpmap is only supported AFTER the 'm=' tag */
495 get_word(buf1
, sizeof(buf1
), &p
);
496 payload_type
= atoi(buf1
);
497 for(i
= 0; i
< s
->nb_streams
;i
++) {
499 rtsp_st
= st
->priv_data
;
500 if (rtsp_st
->sdp_payload_type
== payload_type
) {
501 sdp_parse_rtpmap(st
->codec
, rtsp_st
, payload_type
, p
);
504 } else if (av_strstart(p
, "fmtp:", &p
)) {
505 /* NOTE: fmtp is only supported AFTER the 'a=rtpmap:xxx' tag */
506 get_word(buf1
, sizeof(buf1
), &p
);
507 payload_type
= atoi(buf1
);
508 for(i
= 0; i
< s
->nb_streams
;i
++) {
510 rtsp_st
= st
->priv_data
;
511 if (rtsp_st
->sdp_payload_type
== payload_type
) {
512 if(rtsp_st
->dynamic_handler
&& rtsp_st
->dynamic_handler
->parse_sdp_a_line
) {
513 if(!rtsp_st
->dynamic_handler
->parse_sdp_a_line(st
, rtsp_st
->dynamic_protocol_context
, buf
)) {
514 sdp_parse_fmtp(st
, p
);
517 sdp_parse_fmtp(st
, p
);
521 } else if(av_strstart(p
, "framesize:", &p
)) {
522 // let dynamic protocol handlers have a stab at the line.
523 get_word(buf1
, sizeof(buf1
), &p
);
524 payload_type
= atoi(buf1
);
525 for(i
= 0; i
< s
->nb_streams
;i
++) {
527 rtsp_st
= st
->priv_data
;
528 if (rtsp_st
->sdp_payload_type
== payload_type
) {
529 if(rtsp_st
->dynamic_handler
&& rtsp_st
->dynamic_handler
->parse_sdp_a_line
) {
530 rtsp_st
->dynamic_handler
->parse_sdp_a_line(st
, rtsp_st
->dynamic_protocol_context
, buf
);
534 } else if(av_strstart(p
, "range:", &p
)) {
537 // this is so that seeking on a streamed file can work.
538 rtsp_parse_range_npt(p
, &start
, &end
);
539 s
->start_time
= start
;
540 s
->duration
= (end
==AV_NOPTS_VALUE
)?AV_NOPTS_VALUE
:end
-start
; // AV_NOPTS_VALUE means live broadcast (and can't seek)
541 } else if (s
->nb_streams
> 0) {
542 rtsp_st
= s
->streams
[s
->nb_streams
- 1]->priv_data
;
543 if (rtsp_st
->dynamic_handler
&&
544 rtsp_st
->dynamic_handler
->parse_sdp_a_line
)
545 rtsp_st
->dynamic_handler
->parse_sdp_a_line(s
->streams
[s
->nb_streams
- 1],
546 rtsp_st
->dynamic_protocol_context
, buf
);
552 static int sdp_parse(AVFormatContext
*s
, const char *content
)
557 SDPParseState sdp_parse_state
, *s1
= &sdp_parse_state
;
559 memset(s1
, 0, sizeof(SDPParseState
));
570 /* get the content */
572 while (*p
!= '\n' && *p
!= '\r' && *p
!= '\0') {
573 if ((q
- buf
) < sizeof(buf
) - 1)
578 sdp_parse_line(s
, s1
, letter
, buf
);
580 while (*p
!= '\n' && *p
!= '\0')
588 static void rtsp_parse_range(int *min_ptr
, int *max_ptr
, const char **pp
)
595 v
= strtol(p
, (char **)&p
, 10);
599 v
= strtol(p
, (char **)&p
, 10);
608 /* XXX: only one transport specification is parsed */
609 static void rtsp_parse_transport(RTSPHeader
*reply
, const char *p
)
611 char transport_protocol
[16];
613 char lower_transport
[16];
615 RTSPTransportField
*th
;
618 reply
->nb_transports
= 0;
625 th
= &reply
->transports
[reply
->nb_transports
];
627 get_word_sep(transport_protocol
, sizeof(transport_protocol
),
631 if (!strcasecmp (transport_protocol
, "rtp")) {
632 get_word_sep(profile
, sizeof(profile
), "/;,", &p
);
633 lower_transport
[0] = '\0';
634 /* rtp/avp/<protocol> */
637 get_word_sep(lower_transport
, sizeof(lower_transport
),
640 } else if (!strcasecmp (transport_protocol
, "x-pn-tng")) {
641 /* x-pn-tng/<protocol> */
642 get_word_sep(lower_transport
, sizeof(lower_transport
), "/;,", &p
);
645 if (!strcasecmp(lower_transport
, "TCP"))
646 th
->protocol
= RTSP_PROTOCOL_RTP_TCP
;
648 th
->protocol
= RTSP_PROTOCOL_RTP_UDP
;
652 /* get each parameter */
653 while (*p
!= '\0' && *p
!= ',') {
654 get_word_sep(parameter
, sizeof(parameter
), "=;,", &p
);
655 if (!strcmp(parameter
, "port")) {
658 rtsp_parse_range(&th
->port_min
, &th
->port_max
, &p
);
660 } else if (!strcmp(parameter
, "client_port")) {
663 rtsp_parse_range(&th
->client_port_min
,
664 &th
->client_port_max
, &p
);
666 } else if (!strcmp(parameter
, "server_port")) {
669 rtsp_parse_range(&th
->server_port_min
,
670 &th
->server_port_max
, &p
);
672 } else if (!strcmp(parameter
, "interleaved")) {
675 rtsp_parse_range(&th
->interleaved_min
,
676 &th
->interleaved_max
, &p
);
678 } else if (!strcmp(parameter
, "multicast")) {
679 if (th
->protocol
== RTSP_PROTOCOL_RTP_UDP
)
680 th
->protocol
= RTSP_PROTOCOL_RTP_UDP_MULTICAST
;
681 } else if (!strcmp(parameter
, "ttl")) {
684 th
->ttl
= strtol(p
, (char **)&p
, 10);
686 } else if (!strcmp(parameter
, "destination")) {
687 struct in_addr ipaddr
;
691 get_word_sep(buf
, sizeof(buf
), ";,", &p
);
692 if (inet_aton(buf
, &ipaddr
))
693 th
->destination
= ntohl(ipaddr
.s_addr
);
696 while (*p
!= ';' && *p
!= '\0' && *p
!= ',')
704 reply
->nb_transports
++;
708 void rtsp_parse_line(RTSPHeader
*reply
, const char *buf
)
712 /* NOTE: we do case independent match for broken servers */
714 if (av_stristart(p
, "Session:", &p
)) {
715 get_word_sep(reply
->session_id
, sizeof(reply
->session_id
), ";", &p
);
716 } else if (av_stristart(p
, "Content-Length:", &p
)) {
717 reply
->content_length
= strtol(p
, NULL
, 10);
718 } else if (av_stristart(p
, "Transport:", &p
)) {
719 rtsp_parse_transport(reply
, p
);
720 } else if (av_stristart(p
, "CSeq:", &p
)) {
721 reply
->seq
= strtol(p
, NULL
, 10);
722 } else if (av_stristart(p
, "Range:", &p
)) {
723 rtsp_parse_range_npt(p
, &reply
->range_start
, &reply
->range_end
);
724 } else if (av_stristart(p
, "RealChallenge1:", &p
)) {
726 av_strlcpy(reply
->real_challenge
, p
, sizeof(reply
->real_challenge
));
730 static int url_readbuf(URLContext
*h
, unsigned char *buf
, int size
)
736 ret
= url_read(h
, buf
+len
, size
-len
);
744 /* skip a RTP/TCP interleaved packet */
745 static void rtsp_skip_packet(AVFormatContext
*s
)
747 RTSPState
*rt
= s
->priv_data
;
751 ret
= url_readbuf(rt
->rtsp_hd
, buf
, 3);
754 len
= AV_RB16(buf
+ 1);
756 printf("skipping RTP packet len=%d\n", len
);
761 if (len1
> sizeof(buf
))
763 ret
= url_readbuf(rt
->rtsp_hd
, buf
, len1
);
770 static void rtsp_send_cmd(AVFormatContext
*s
,
771 const char *cmd
, RTSPHeader
*reply
,
772 unsigned char **content_ptr
)
774 RTSPState
*rt
= s
->priv_data
;
775 char buf
[4096], buf1
[1024], *q
;
778 int content_length
, line_count
;
779 unsigned char *content
= NULL
;
781 memset(reply
, 0, sizeof(RTSPHeader
));
784 av_strlcpy(buf
, cmd
, sizeof(buf
));
785 snprintf(buf1
, sizeof(buf1
), "CSeq: %d\r\n", rt
->seq
);
786 av_strlcat(buf
, buf1
, sizeof(buf
));
787 if (rt
->session_id
[0] != '\0' && !strstr(cmd
, "\nIf-Match:")) {
788 snprintf(buf1
, sizeof(buf1
), "Session: %s\r\n", rt
->session_id
);
789 av_strlcat(buf
, buf1
, sizeof(buf
));
791 av_strlcat(buf
, "\r\n", sizeof(buf
));
793 printf("Sending:\n%s--\n", buf
);
795 url_write(rt
->rtsp_hd
, buf
, strlen(buf
));
797 /* parse reply (XXX: use buffers) */
799 rt
->last_reply
[0] = '\0';
803 if (url_readbuf(rt
->rtsp_hd
, &ch
, 1) != 1)
808 /* XXX: only parse it if first char on line ? */
810 } else if (ch
!= '\r') {
811 if ((q
- buf
) < sizeof(buf
) - 1)
817 printf("line='%s'\n", buf
);
819 /* test if last line */
823 if (line_count
== 0) {
825 get_word(buf1
, sizeof(buf1
), &p
);
826 get_word(buf1
, sizeof(buf1
), &p
);
827 reply
->status_code
= atoi(buf1
);
829 rtsp_parse_line(reply
, p
);
830 av_strlcat(rt
->last_reply
, p
, sizeof(rt
->last_reply
));
831 av_strlcat(rt
->last_reply
, "\n", sizeof(rt
->last_reply
));
836 if (rt
->session_id
[0] == '\0' && reply
->session_id
[0] != '\0')
837 av_strlcpy(rt
->session_id
, reply
->session_id
, sizeof(rt
->session_id
));
839 content_length
= reply
->content_length
;
840 if (content_length
> 0) {
841 /* leave some room for a trailing '\0' (useful for simple parsing) */
842 content
= av_malloc(content_length
+ 1);
843 (void)url_readbuf(rt
->rtsp_hd
, content
, content_length
);
844 content
[content_length
] = '\0';
847 *content_ptr
= content
;
853 /* close and free RTSP streams */
854 static void rtsp_close_streams(RTSPState
*rt
)
859 for(i
=0;i
<rt
->nb_rtsp_streams
;i
++) {
860 rtsp_st
= rt
->rtsp_streams
[i
];
862 if (rtsp_st
->rtp_ctx
)
863 rtp_parse_close(rtsp_st
->rtp_ctx
);
864 if (rtsp_st
->rtp_handle
)
865 url_close(rtsp_st
->rtp_handle
);
866 if (rtsp_st
->dynamic_handler
&& rtsp_st
->dynamic_protocol_context
)
867 rtsp_st
->dynamic_handler
->close(rtsp_st
->dynamic_protocol_context
);
870 av_free(rt
->rtsp_streams
);
874 rtsp_open_transport_ctx(AVFormatContext
*s
, RTSPStream
*rtsp_st
)
878 /* open the RTP context */
879 if (rtsp_st
->stream_index
>= 0)
880 st
= s
->streams
[rtsp_st
->stream_index
];
882 s
->ctx_flags
|= AVFMTCTX_NOHEADER
;
883 rtsp_st
->rtp_ctx
= rtp_parse_open(s
, st
, rtsp_st
->rtp_handle
, rtsp_st
->sdp_payload_type
, &rtsp_st
->rtp_payload_data
);
885 if (!rtsp_st
->rtp_ctx
) {
886 return AVERROR(ENOMEM
);
888 if(rtsp_st
->dynamic_handler
) {
889 rtsp_st
->rtp_ctx
->dynamic_protocol_context
= rtsp_st
->dynamic_protocol_context
;
890 rtsp_st
->rtp_ctx
->parse_packet
= rtsp_st
->dynamic_handler
->parse_packet
;
898 * @returns 0 on success, <0 on error, 1 if protocol is unavailable.
901 make_setup_request (AVFormatContext
*s
, const char *host
, int port
,
902 int protocol
, const char *real_challenge
)
904 RTSPState
*rt
= s
->priv_data
;
907 RTSPHeader reply1
, *reply
= &reply1
;
909 const char *trans_pref
;
911 if (rt
->server_type
== RTSP_SERVER_REAL
)
912 trans_pref
= "x-pn-tng";
914 trans_pref
= "RTP/AVP";
916 /* for each stream, make the setup request */
917 /* XXX: we assume the same server is used for the control of each
920 for(j
= RTSP_RTP_PORT_MIN
, i
= 0; i
< rt
->nb_rtsp_streams
; ++i
) {
921 char transport
[2048];
923 rtsp_st
= rt
->rtsp_streams
[i
];
926 if (protocol
== RTSP_PROTOCOL_RTP_UDP
) {
929 /* first try in specified port range */
930 if (RTSP_RTP_PORT_MIN
!= 0) {
931 while(j
<= RTSP_RTP_PORT_MAX
) {
932 snprintf(buf
, sizeof(buf
), "rtp://%s?localport=%d", host
, j
);
933 j
+= 2; /* we will use two port by rtp stream (rtp and rtcp) */
934 if (url_open(&rtsp_st
->rtp_handle
, buf
, URL_RDWR
) == 0) {
940 /* then try on any port
941 ** if (url_open(&rtsp_st->rtp_handle, "rtp://", URL_RDONLY) < 0) {
942 ** err = AVERROR_INVALIDDATA;
948 port
= rtp_get_local_port(rtsp_st
->rtp_handle
);
949 snprintf(transport
, sizeof(transport
) - 1,
950 "%s/UDP;unicast;client_port=%d",
952 if (rt
->server_type
== RTSP_SERVER_RTP
)
953 av_strlcatf(transport
, sizeof(transport
), "-%d", port
+ 1);
957 else if (protocol
== RTSP_PROTOCOL_RTP_TCP
) {
958 snprintf(transport
, sizeof(transport
) - 1,
959 "%s/TCP", trans_pref
);
962 else if (protocol
== RTSP_PROTOCOL_RTP_UDP_MULTICAST
) {
963 snprintf(transport
, sizeof(transport
) - 1,
964 "%s/UDP;multicast", trans_pref
);
966 if (rt
->server_type
== RTSP_SERVER_REAL
)
967 av_strlcat(transport
, ";mode=play", sizeof(transport
));
968 snprintf(cmd
, sizeof(cmd
),
969 "SETUP %s RTSP/1.0\r\n"
971 rtsp_st
->control_url
, transport
);
972 if (i
== 0 && rt
->server_type
== RTSP_SERVER_REAL
) {
973 char real_res
[41], real_csum
[9];
974 ff_rdt_calc_response_and_checksum(real_res
, real_csum
,
976 av_strlcatf(cmd
, sizeof(cmd
),
978 "RealChallenge2: %s, sd=%s\r\n",
979 rt
->session_id
, real_res
, real_csum
);
981 rtsp_send_cmd(s
, cmd
, reply
, NULL
);
982 if (reply
->status_code
== 461 /* Unsupported protocol */ && i
== 0) {
985 } else if (reply
->status_code
!= RTSP_STATUS_OK
||
986 reply
->nb_transports
!= 1) {
987 err
= AVERROR_INVALIDDATA
;
991 /* XXX: same protocol for all streams is required */
993 if (reply
->transports
[0].protocol
!= rt
->protocol
) {
994 err
= AVERROR_INVALIDDATA
;
998 rt
->protocol
= reply
->transports
[0].protocol
;
1001 /* close RTP connection if not choosen */
1002 if (reply
->transports
[0].protocol
!= RTSP_PROTOCOL_RTP_UDP
&&
1003 (protocol
== RTSP_PROTOCOL_RTP_UDP
)) {
1004 url_close(rtsp_st
->rtp_handle
);
1005 rtsp_st
->rtp_handle
= NULL
;
1008 switch(reply
->transports
[0].protocol
) {
1009 case RTSP_PROTOCOL_RTP_TCP
:
1010 rtsp_st
->interleaved_min
= reply
->transports
[0].interleaved_min
;
1011 rtsp_st
->interleaved_max
= reply
->transports
[0].interleaved_max
;
1014 case RTSP_PROTOCOL_RTP_UDP
:
1018 /* XXX: also use address if specified */
1019 snprintf(url
, sizeof(url
), "rtp://%s:%d",
1020 host
, reply
->transports
[0].server_port_min
);
1021 if (rtp_set_remote_url(rtsp_st
->rtp_handle
, url
) < 0) {
1022 err
= AVERROR_INVALIDDATA
;
1027 case RTSP_PROTOCOL_RTP_UDP_MULTICAST
:
1032 in
.s_addr
= htonl(reply
->transports
[0].destination
);
1033 snprintf(url
, sizeof(url
), "rtp://%s:%d?ttl=%d",
1035 reply
->transports
[0].port_min
,
1036 reply
->transports
[0].ttl
);
1037 if (url_open(&rtsp_st
->rtp_handle
, url
, URL_RDWR
) < 0) {
1038 err
= AVERROR_INVALIDDATA
;
1045 if ((err
= rtsp_open_transport_ctx(s
, rtsp_st
)))
1049 if (rt
->server_type
== RTSP_SERVER_REAL
)
1050 rt
->need_subscription
= 1;
1055 for (i
=0; i
<rt
->nb_rtsp_streams
; i
++) {
1056 if (rt
->rtsp_streams
[i
]->rtp_handle
) {
1057 url_close(rt
->rtsp_streams
[i
]->rtp_handle
);
1058 rt
->rtsp_streams
[i
]->rtp_handle
= NULL
;
1064 static int rtsp_read_header(AVFormatContext
*s
,
1065 AVFormatParameters
*ap
)
1067 RTSPState
*rt
= s
->priv_data
;
1068 char host
[1024], path
[1024], tcpname
[1024], cmd
[2048], *option_list
, *option
;
1069 URLContext
*rtsp_hd
;
1071 RTSPHeader reply1
, *reply
= &reply1
;
1072 unsigned char *content
= NULL
;
1073 int protocol_mask
= 0;
1074 char real_challenge
[64];
1076 /* extract hostname and port */
1077 url_split(NULL
, 0, NULL
, 0,
1078 host
, sizeof(host
), &port
, path
, sizeof(path
), s
->filename
);
1080 port
= RTSP_DEFAULT_PORT
;
1082 /* search for options */
1083 option_list
= strchr(path
, '?');
1085 /* remove the options from the path */
1087 while(option_list
) {
1088 /* move the option pointer */
1089 option
= option_list
;
1090 option_list
= strchr(option_list
, '&');
1092 *(option_list
++) = 0;
1093 /* handle the options */
1094 if (strcmp(option
, "udp") == 0)
1095 protocol_mask
= (1<< RTSP_PROTOCOL_RTP_UDP
);
1096 else if (strcmp(option
, "multicast") == 0)
1097 protocol_mask
= (1<< RTSP_PROTOCOL_RTP_UDP_MULTICAST
);
1098 else if (strcmp(option
, "tcp") == 0)
1099 protocol_mask
= (1<< RTSP_PROTOCOL_RTP_TCP
);
1104 protocol_mask
= (1 << RTSP_PROTOCOL_RTP_LAST
) - 1;
1106 /* open the tcp connexion */
1107 snprintf(tcpname
, sizeof(tcpname
), "tcp://%s:%d", host
, port
);
1108 if (url_open(&rtsp_hd
, tcpname
, URL_RDWR
) < 0)
1109 return AVERROR(EIO
);
1110 rt
->rtsp_hd
= rtsp_hd
;
1113 /* request options supported by the server; this also detects server type */
1114 for (rt
->server_type
= RTSP_SERVER_RTP
;;) {
1115 snprintf(cmd
, sizeof(cmd
),
1116 "OPTIONS %s RTSP/1.0\r\n", s
->filename
);
1117 if (rt
->server_type
== RTSP_SERVER_REAL
)
1120 * The following entries are required for proper
1121 * streaming from a Realmedia server. They are
1122 * interdependent in some way although we currently
1123 * don't quite understand how. Values were copied
1124 * from mplayer SVN r23589.
1125 * @param CompanyID is a 16-byte ID in base64
1126 * @param ClientChallenge is a 16-byte ID in hex
1128 "ClientChallenge: 9e26d33f2984236010ef6253fb1887f7\r\n"
1129 "PlayerStarttime: [28/03/2003:22:50:23 00:00]\r\n"
1130 "CompanyID: KnKV4M4I/B2FjJ1TToLycw==\r\n"
1131 "GUID: 00000000-0000-0000-0000-000000000000\r\n",
1133 rtsp_send_cmd(s
, cmd
, reply
, NULL
);
1134 if (reply
->status_code
!= RTSP_STATUS_OK
) {
1135 err
= AVERROR_INVALIDDATA
;
1139 /* detect server type if not standard-compliant RTP */
1140 if (rt
->server_type
!= RTSP_SERVER_REAL
&& reply
->real_challenge
[0]) {
1141 rt
->server_type
= RTSP_SERVER_REAL
;
1143 } else if (rt
->server_type
== RTSP_SERVER_REAL
) {
1144 strcpy(real_challenge
, reply
->real_challenge
);
1149 /* describe the stream */
1150 snprintf(cmd
, sizeof(cmd
),
1151 "DESCRIBE %s RTSP/1.0\r\n"
1152 "Accept: application/sdp\r\n",
1154 if (rt
->server_type
== RTSP_SERVER_REAL
) {
1156 * The Require: attribute is needed for proper streaming from
1157 * Realmedia servers.
1160 "Require: com.real.retain-entity-for-setup\r\n",
1163 rtsp_send_cmd(s
, cmd
, reply
, &content
);
1165 err
= AVERROR_INVALIDDATA
;
1168 if (reply
->status_code
!= RTSP_STATUS_OK
) {
1169 err
= AVERROR_INVALIDDATA
;
1173 /* now we got the SDP description, we parse it */
1174 ret
= sdp_parse(s
, (const char *)content
);
1177 err
= AVERROR_INVALIDDATA
;
1182 int protocol
= ff_log2_tab
[protocol_mask
& ~(protocol_mask
- 1)];
1184 err
= make_setup_request(s
, host
, port
, protocol
,
1185 rt
->server_type
== RTSP_SERVER_REAL
?
1186 real_challenge
: NULL
);
1189 protocol_mask
&= ~(1 << protocol
);
1190 if (protocol_mask
== 0 && err
== 1) {
1191 err
= AVERROR(FF_NETERROR(EPROTONOSUPPORT
));
1196 rt
->state
= RTSP_STATE_IDLE
;
1197 rt
->seek_timestamp
= 0; /* default is to start stream at position
1199 if (ap
->initial_pause
) {
1200 /* do not start immediately */
1202 if (rtsp_read_play(s
) < 0) {
1203 err
= AVERROR_INVALIDDATA
;
1209 rtsp_close_streams(rt
);
1211 url_close(rt
->rtsp_hd
);
1215 static int tcp_read_packet(AVFormatContext
*s
, RTSPStream
**prtsp_st
,
1216 uint8_t *buf
, int buf_size
)
1218 RTSPState
*rt
= s
->priv_data
;
1219 int id
, len
, i
, ret
;
1220 RTSPStream
*rtsp_st
;
1222 #ifdef DEBUG_RTP_TCP
1223 printf("tcp_read_packet:\n");
1227 ret
= url_readbuf(rt
->rtsp_hd
, buf
, 1);
1228 #ifdef DEBUG_RTP_TCP
1229 printf("ret=%d c=%02x [%c]\n", ret
, buf
[0], buf
[0]);
1236 ret
= url_readbuf(rt
->rtsp_hd
, buf
, 3);
1240 len
= AV_RB16(buf
+ 1);
1241 #ifdef DEBUG_RTP_TCP
1242 printf("id=%d len=%d\n", id
, len
);
1244 if (len
> buf_size
|| len
< 12)
1247 ret
= url_readbuf(rt
->rtsp_hd
, buf
, len
);
1251 /* find the matching stream */
1252 for(i
= 0; i
< rt
->nb_rtsp_streams
; i
++) {
1253 rtsp_st
= rt
->rtsp_streams
[i
];
1254 if (id
>= rtsp_st
->interleaved_min
&&
1255 id
<= rtsp_st
->interleaved_max
)
1260 *prtsp_st
= rtsp_st
;
1264 static int udp_read_packet(AVFormatContext
*s
, RTSPStream
**prtsp_st
,
1265 uint8_t *buf
, int buf_size
)
1267 RTSPState
*rt
= s
->priv_data
;
1268 RTSPStream
*rtsp_st
;
1270 int fd1
, fd2
, fd_max
, n
, i
, ret
;
1274 if (url_interrupt_cb())
1275 return AVERROR(EINTR
);
1278 for(i
= 0; i
< rt
->nb_rtsp_streams
; i
++) {
1279 rtsp_st
= rt
->rtsp_streams
[i
];
1280 /* currently, we cannot probe RTCP handle because of blocking restrictions */
1281 rtp_get_file_handles(rtsp_st
->rtp_handle
, &fd1
, &fd2
);
1287 tv
.tv_usec
= 100 * 1000;
1288 n
= select(fd_max
+ 1, &rfds
, NULL
, NULL
, &tv
);
1290 for(i
= 0; i
< rt
->nb_rtsp_streams
; i
++) {
1291 rtsp_st
= rt
->rtsp_streams
[i
];
1292 rtp_get_file_handles(rtsp_st
->rtp_handle
, &fd1
, &fd2
);
1293 if (FD_ISSET(fd1
, &rfds
)) {
1294 ret
= url_read(rtsp_st
->rtp_handle
, buf
, buf_size
);
1296 *prtsp_st
= rtsp_st
;
1305 static int rtsp_read_packet(AVFormatContext
*s
,
1308 RTSPState
*rt
= s
->priv_data
;
1309 RTSPStream
*rtsp_st
;
1311 uint8_t buf
[RTP_MAX_PACKET_LENGTH
];
1313 if (rt
->server_type
== RTSP_SERVER_REAL
&& rt
->need_subscription
) {
1315 RTSPHeader reply1
, *reply
= &reply1
;
1318 snprintf(cmd
, sizeof(cmd
),
1319 "SET_PARAMETER %s RTSP/1.0\r\n"
1322 for (i
= 0; i
< rt
->nb_rtsp_streams
; i
++) {
1323 if (i
!= 0) av_strlcat(cmd
, ",", sizeof(cmd
));
1324 ff_rdt_subscribe_rule(
1325 rt
->rtsp_streams
[i
]->rtp_ctx
,
1326 cmd
, sizeof(cmd
), i
, 0);
1328 av_strlcat(cmd
, "\r\n", sizeof(cmd
));
1329 rtsp_send_cmd(s
, cmd
, reply
, NULL
);
1330 if (reply
->status_code
!= RTSP_STATUS_OK
)
1331 return AVERROR_INVALIDDATA
;
1332 rt
->need_subscription
= 0;
1334 if (rt
->state
== RTSP_STATE_PLAYING
)
1338 /* get next frames from the same RTP packet */
1340 if (rt
->server_type
== RTSP_SERVER_REAL
)
1341 ret
= ff_rdt_parse_packet(rt
->cur_rtp
, pkt
, NULL
, 0);
1343 ret
= rtp_parse_packet(rt
->cur_rtp
, pkt
, NULL
, 0);
1347 } else if (ret
== 1) {
1354 /* read next RTP packet */
1356 switch(rt
->protocol
) {
1358 case RTSP_PROTOCOL_RTP_TCP
:
1359 len
= tcp_read_packet(s
, &rtsp_st
, buf
, sizeof(buf
));
1361 case RTSP_PROTOCOL_RTP_UDP
:
1362 case RTSP_PROTOCOL_RTP_UDP_MULTICAST
:
1363 len
= udp_read_packet(s
, &rtsp_st
, buf
, sizeof(buf
));
1364 if (len
>=0 && rtsp_st
->rtp_ctx
)
1365 rtp_check_and_send_back_rr(rtsp_st
->rtp_ctx
, len
);
1370 if (rt
->server_type
== RTSP_SERVER_REAL
)
1371 ret
= ff_rdt_parse_packet(rtsp_st
->rtp_ctx
, pkt
, buf
, len
);
1373 ret
= rtp_parse_packet(rtsp_st
->rtp_ctx
, pkt
, buf
, len
);
1377 /* more packets may follow, so we save the RTP context */
1378 rt
->cur_rtp
= rtsp_st
->rtp_ctx
;
1383 static int rtsp_read_play(AVFormatContext
*s
)
1385 RTSPState
*rt
= s
->priv_data
;
1386 RTSPHeader reply1
, *reply
= &reply1
;
1389 av_log(s
, AV_LOG_DEBUG
, "hello state=%d\n", rt
->state
);
1391 if (!(rt
->server_type
== RTSP_SERVER_REAL
&& rt
->need_subscription
)) {
1392 if (rt
->state
== RTSP_STATE_PAUSED
) {
1393 snprintf(cmd
, sizeof(cmd
),
1394 "PLAY %s RTSP/1.0\r\n",
1397 snprintf(cmd
, sizeof(cmd
),
1398 "PLAY %s RTSP/1.0\r\n"
1399 "Range: npt=%0.3f-\r\n",
1401 (double)rt
->seek_timestamp
/ AV_TIME_BASE
);
1403 rtsp_send_cmd(s
, cmd
, reply
, NULL
);
1404 if (reply
->status_code
!= RTSP_STATUS_OK
) {
1408 rt
->state
= RTSP_STATE_PLAYING
;
1412 /* pause the stream */
1413 static int rtsp_read_pause(AVFormatContext
*s
)
1415 RTSPState
*rt
= s
->priv_data
;
1416 RTSPHeader reply1
, *reply
= &reply1
;
1421 if (rt
->state
!= RTSP_STATE_PLAYING
)
1423 else if (!(rt
->server_type
== RTSP_SERVER_REAL
&& rt
->need_subscription
)) {
1424 snprintf(cmd
, sizeof(cmd
),
1425 "PAUSE %s RTSP/1.0\r\n",
1427 rtsp_send_cmd(s
, cmd
, reply
, NULL
);
1428 if (reply
->status_code
!= RTSP_STATUS_OK
) {
1432 rt
->state
= RTSP_STATE_PAUSED
;
1436 static int rtsp_read_seek(AVFormatContext
*s
, int stream_index
,
1437 int64_t timestamp
, int flags
)
1439 RTSPState
*rt
= s
->priv_data
;
1441 rt
->seek_timestamp
= av_rescale_q(timestamp
, s
->streams
[stream_index
]->time_base
, AV_TIME_BASE_Q
);
1444 case RTSP_STATE_IDLE
:
1446 case RTSP_STATE_PLAYING
:
1447 if (rtsp_read_play(s
) != 0)
1450 case RTSP_STATE_PAUSED
:
1451 rt
->state
= RTSP_STATE_IDLE
;
1457 static int rtsp_read_close(AVFormatContext
*s
)
1459 RTSPState
*rt
= s
->priv_data
;
1460 RTSPHeader reply1
, *reply
= &reply1
;
1464 /* NOTE: it is valid to flush the buffer here */
1465 if (rt
->protocol
== RTSP_PROTOCOL_RTP_TCP
) {
1466 url_fclose(&rt
->rtsp_gb
);
1469 snprintf(cmd
, sizeof(cmd
),
1470 "TEARDOWN %s RTSP/1.0\r\n",
1472 rtsp_send_cmd(s
, cmd
, reply
, NULL
);
1474 rtsp_close_streams(rt
);
1475 url_close(rt
->rtsp_hd
);
1479 #ifdef CONFIG_RTSP_DEMUXER
1480 AVInputFormat rtsp_demuxer
= {
1482 NULL_IF_CONFIG_SMALL("RTSP input format"),
1489 .flags
= AVFMT_NOFILE
,
1490 .read_play
= rtsp_read_play
,
1491 .read_pause
= rtsp_read_pause
,
1495 static int sdp_probe(AVProbeData
*p1
)
1497 const char *p
= p1
->buf
, *p_end
= p1
->buf
+ p1
->buf_size
;
1499 /* we look for a line beginning "c=IN IP4" */
1500 while (p
< p_end
&& *p
!= '\0') {
1501 if (p
+ sizeof("c=IN IP4") - 1 < p_end
&& av_strstart(p
, "c=IN IP4", NULL
))
1502 return AVPROBE_SCORE_MAX
/ 2;
1504 while(p
< p_end
- 1 && *p
!= '\n') p
++;
1513 #define SDP_MAX_SIZE 8192
1515 static int sdp_read_header(AVFormatContext
*s
,
1516 AVFormatParameters
*ap
)
1518 RTSPState
*rt
= s
->priv_data
;
1519 RTSPStream
*rtsp_st
;
1524 /* read the whole sdp file */
1525 /* XXX: better loading */
1526 content
= av_malloc(SDP_MAX_SIZE
);
1527 size
= get_buffer(s
->pb
, content
, SDP_MAX_SIZE
- 1);
1530 return AVERROR_INVALIDDATA
;
1532 content
[size
] ='\0';
1534 sdp_parse(s
, content
);
1537 /* open each RTP stream */
1538 for(i
=0;i
<rt
->nb_rtsp_streams
;i
++) {
1539 rtsp_st
= rt
->rtsp_streams
[i
];
1541 snprintf(url
, sizeof(url
), "rtp://%s:%d?localport=%d&ttl=%d",
1542 inet_ntoa(rtsp_st
->sdp_ip
),
1546 if (url_open(&rtsp_st
->rtp_handle
, url
, URL_RDWR
) < 0) {
1547 err
= AVERROR_INVALIDDATA
;
1550 if ((err
= rtsp_open_transport_ctx(s
, rtsp_st
)))
1555 rtsp_close_streams(rt
);
1559 static int sdp_read_packet(AVFormatContext
*s
,
1562 return rtsp_read_packet(s
, pkt
);
1565 static int sdp_read_close(AVFormatContext
*s
)
1567 RTSPState
*rt
= s
->priv_data
;
1568 rtsp_close_streams(rt
);
1572 #ifdef CONFIG_SDP_DEMUXER
1573 AVInputFormat sdp_demuxer
= {
1575 NULL_IF_CONFIG_SMALL("SDP"),
1584 #ifdef CONFIG_REDIR_DEMUXER
1585 /* dummy redirector format (used directly in av_open_input_file now) */
1586 static int redir_probe(AVProbeData
*pd
)
1590 while (redir_isspace(*p
))
1592 if (av_strstart(p
, "http://", NULL
) ||
1593 av_strstart(p
, "rtsp://", NULL
))
1594 return AVPROBE_SCORE_MAX
;
1598 static int redir_read_header(AVFormatContext
*s
, AVFormatParameters
*ap
)
1602 AVFormatContext
*ic
= NULL
;
1603 ByteIOContext
*f
= s
->pb
;
1605 /* parse each URL and try to open it */
1607 while (c
!= URL_EOF
) {
1610 if (!redir_isspace(c
))
1619 if (c
== URL_EOF
|| redir_isspace(c
))
1621 if ((q
- buf
) < sizeof(buf
) - 1)
1626 //printf("URL='%s'\n", buf);
1627 /* try to open the media file */
1628 if (av_open_input_file(&ic
, buf
, NULL
, 0, NULL
) == 0)
1632 return AVERROR(EIO
);
1640 AVInputFormat redir_demuxer
= {
1642 NULL_IF_CONFIG_SMALL("Redirector format"),