]> rtime.felk.cvut.cz Git - frescor/ffmpeg.git/blob - libavformat/rtsp.c
Change license headers to say 'FFmpeg' instead of 'this program/this library'
[frescor/ffmpeg.git] / libavformat / rtsp.c
1 /*
2  * RTSP/SDP client
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 #include "avformat.h"
22
23 #include <unistd.h> /* for select() prototype */
24 #include <sys/time.h>
25 #include <netinet/in.h>
26 #include <sys/socket.h>
27 #ifndef __BEOS__
28 # include <arpa/inet.h>
29 #else
30 # include "barpainet.h"
31 #endif
32
33 //#define DEBUG
34 //#define DEBUG_RTP_TCP
35
36 enum RTSPClientState {
37     RTSP_STATE_IDLE,
38     RTSP_STATE_PLAYING,
39     RTSP_STATE_PAUSED,
40 };
41
42 typedef struct RTSPState {
43     URLContext *rtsp_hd; /* RTSP TCP connexion handle */
44     int nb_rtsp_streams;
45     struct RTSPStream **rtsp_streams;
46
47     enum RTSPClientState state;
48     int64_t seek_timestamp;
49
50     /* XXX: currently we use unbuffered input */
51     //    ByteIOContext rtsp_gb;
52     int seq;        /* RTSP command sequence number */
53     char session_id[512];
54     enum RTSPProtocol protocol;
55     char last_reply[2048]; /* XXX: allocate ? */
56     RTPDemuxContext *cur_rtp;
57 } RTSPState;
58
59 typedef struct RTSPStream {
60     URLContext *rtp_handle; /* RTP stream handle */
61     RTPDemuxContext *rtp_ctx; /* RTP parse context */
62
63     int stream_index; /* corresponding stream index, if any. -1 if none (MPEG2TS case) */
64     int interleaved_min, interleaved_max;  /* interleave ids, if TCP transport */
65     char control_url[1024]; /* url for this stream (from SDP) */
66
67     int sdp_port; /* port (from SDP content - not used in RTSP) */
68     struct in_addr sdp_ip; /* IP address  (from SDP content - not used in RTSP) */
69     int sdp_ttl;  /* IP TTL (from SDP content - not used in RTSP) */
70     int sdp_payload_type; /* payload type - only used in SDP */
71     rtp_payload_data_t rtp_payload_data; /* rtp payload parsing infos from SDP */
72 } RTSPStream;
73
74 static int rtsp_read_play(AVFormatContext *s);
75
76 /* XXX: currently, the only way to change the protocols consists in
77    changing this variable */
78
79 int rtsp_default_protocols = (1 << RTSP_PROTOCOL_RTP_UDP);
80
81 FFRTSPCallback *ff_rtsp_callback = NULL;
82
83 static int rtsp_probe(AVProbeData *p)
84 {
85     if (strstart(p->filename, "rtsp:", NULL))
86         return AVPROBE_SCORE_MAX;
87     return 0;
88 }
89
90 static int redir_isspace(int c)
91 {
92     return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
93 }
94
95 static void skip_spaces(const char **pp)
96 {
97     const char *p;
98     p = *pp;
99     while (redir_isspace(*p))
100         p++;
101     *pp = p;
102 }
103
104 static void get_word_sep(char *buf, int buf_size, const char *sep,
105                          const char **pp)
106 {
107     const char *p;
108     char *q;
109
110     p = *pp;
111     if (*p == '/')
112         p++;
113     skip_spaces(&p);
114     q = buf;
115     while (!strchr(sep, *p) && *p != '\0') {
116         if ((q - buf) < buf_size - 1)
117             *q++ = *p;
118         p++;
119     }
120     if (buf_size > 0)
121         *q = '\0';
122     *pp = p;
123 }
124
125 static void get_word(char *buf, int buf_size, const char **pp)
126 {
127     const char *p;
128     char *q;
129
130     p = *pp;
131     skip_spaces(&p);
132     q = buf;
133     while (!redir_isspace(*p) && *p != '\0') {
134         if ((q - buf) < buf_size - 1)
135             *q++ = *p;
136         p++;
137     }
138     if (buf_size > 0)
139         *q = '\0';
140     *pp = p;
141 }
142
143 /* parse the rtpmap description: <codec_name>/<clock_rate>[/<other
144    params>] */
145 static int sdp_parse_rtpmap(AVCodecContext *codec, int payload_type, const char *p)
146 {
147     char buf[256];
148     int i;
149     AVCodec *c;
150     const char *c_name;
151
152     /* Loop into AVRtpDynamicPayloadTypes[] and AVRtpPayloadTypes[] and
153        see if we can handle this kind of payload */
154     get_word_sep(buf, sizeof(buf), "/", &p);
155     if (payload_type >= RTP_PT_PRIVATE) {
156         /* We are in dynmaic payload type case ... search into AVRtpDynamicPayloadTypes[] */
157         for (i = 0; AVRtpDynamicPayloadTypes[i].codec_id != CODEC_ID_NONE; ++i)
158             if (!strcmp(buf, AVRtpDynamicPayloadTypes[i].enc_name) && (codec->codec_type == AVRtpDynamicPayloadTypes[i].codec_type)) {
159                 codec->codec_id = AVRtpDynamicPayloadTypes[i].codec_id;
160                 break;
161             }
162     } else {
163         /* We are in a standard case ( from http://www.iana.org/assignments/rtp-parameters) */
164         /* search into AVRtpPayloadTypes[] */
165         for (i = 0; AVRtpPayloadTypes[i].pt >= 0; ++i)
166             if (!strcmp(buf, AVRtpPayloadTypes[i].enc_name) && (codec->codec_type == AVRtpPayloadTypes[i].codec_type)){
167                 codec->codec_id = AVRtpPayloadTypes[i].codec_id;
168                 break;
169             }
170     }
171
172     c = avcodec_find_decoder(codec->codec_id);
173     if (c && c->name)
174         c_name = c->name;
175     else
176         c_name = (char *)NULL;
177
178     if (c_name) {
179         get_word_sep(buf, sizeof(buf), "/", &p);
180         i = atoi(buf);
181         switch (codec->codec_type) {
182             case CODEC_TYPE_AUDIO:
183                 av_log(codec, AV_LOG_DEBUG, " audio codec set to : %s\n", c_name);
184                 codec->sample_rate = RTSP_DEFAULT_AUDIO_SAMPLERATE;
185                 codec->channels = RTSP_DEFAULT_NB_AUDIO_CHANNELS;
186                 if (i > 0) {
187                     codec->sample_rate = i;
188                     get_word_sep(buf, sizeof(buf), "/", &p);
189                     i = atoi(buf);
190                     if (i > 0)
191                         codec->channels = i;
192                 }
193                 av_log(codec, AV_LOG_DEBUG, " audio samplerate set to : %i\n", codec->sample_rate);
194                 av_log(codec, AV_LOG_DEBUG, " audio channels set to : %i\n", codec->channels);
195                 break;
196             case CODEC_TYPE_VIDEO:
197                 av_log(codec, AV_LOG_DEBUG, " video codec set to : %s\n", c_name);
198                 break;
199             default:
200                 break;
201         }
202         return 0;
203     }
204
205     return -1;
206 }
207
208 /* return the length and optionnaly the data */
209 static int hex_to_data(uint8_t *data, const char *p)
210 {
211     int c, len, v;
212
213     len = 0;
214     v = 1;
215     for(;;) {
216         skip_spaces(&p);
217         if (p == '\0')
218             break;
219         c = toupper((unsigned char)*p++);
220         if (c >= '0' && c <= '9')
221             c = c - '0';
222         else if (c >= 'A' && c <= 'F')
223             c = c - 'A' + 10;
224         else
225             break;
226         v = (v << 4) | c;
227         if (v & 0x100) {
228             if (data)
229                 data[len] = v;
230             len++;
231             v = 1;
232         }
233     }
234     return len;
235 }
236
237 static void sdp_parse_fmtp_config(AVCodecContext *codec, char *attr, char *value)
238 {
239     switch (codec->codec_id) {
240         case CODEC_ID_MPEG4:
241         case CODEC_ID_MPEG4AAC:
242             if (!strcmp(attr, "config")) {
243                 /* decode the hexa encoded parameter */
244                 int len = hex_to_data(NULL, value);
245                 codec->extradata = av_mallocz(len + FF_INPUT_BUFFER_PADDING_SIZE);
246                 if (!codec->extradata)
247                     return;
248                 codec->extradata_size = len;
249                 hex_to_data(codec->extradata, value);
250             }
251             break;
252         default:
253             break;
254     }
255     return;
256 }
257
258 typedef struct attrname_map
259 {
260     const char *str;
261     uint16_t type;
262     uint32_t offset;
263 } attrname_map_t;
264
265 /* All known fmtp parmeters and the corresping RTPAttrTypeEnum */
266 #define ATTR_NAME_TYPE_INT 0
267 #define ATTR_NAME_TYPE_STR 1
268 static attrname_map_t attr_names[]=
269 {
270     {"SizeLength",       ATTR_NAME_TYPE_INT, offsetof(rtp_payload_data_t, sizelength)},
271     {"IndexLength",      ATTR_NAME_TYPE_INT, offsetof(rtp_payload_data_t, indexlength)},
272     {"IndexDeltaLength", ATTR_NAME_TYPE_INT, offsetof(rtp_payload_data_t, indexdeltalength)},
273     {"profile-level-id", ATTR_NAME_TYPE_INT, offsetof(rtp_payload_data_t, profile_level_id)},
274     {"StreamType",       ATTR_NAME_TYPE_INT, offsetof(rtp_payload_data_t, streamtype)},
275     {"mode",             ATTR_NAME_TYPE_STR, offsetof(rtp_payload_data_t, mode)},
276     {NULL, -1, -1},
277 };
278
279 /* parse a SDP line and save stream attributes */
280 static void sdp_parse_fmtp(AVStream *st, const char *p)
281 {
282     char attr[256];
283     char value[4096];
284     int i;
285
286     RTSPStream *rtsp_st = st->priv_data;
287     AVCodecContext *codec = st->codec;
288     rtp_payload_data_t *rtp_payload_data = &rtsp_st->rtp_payload_data;
289
290     /* loop on each attribute */
291     for(;;) {
292         skip_spaces(&p);
293         if (*p == '\0')
294             break;
295         get_word_sep(attr, sizeof(attr), "=", &p);
296         if (*p == '=')
297             p++;
298         get_word_sep(value, sizeof(value), ";", &p);
299         if (*p == ';')
300             p++;
301         /* grab the codec extra_data from the config parameter of the fmtp line */
302         sdp_parse_fmtp_config(codec, attr, value);
303         /* Looking for a known attribute */
304         for (i = 0; attr_names[i].str; ++i) {
305             if (!strcasecmp(attr, attr_names[i].str)) {
306                 if (attr_names[i].type == ATTR_NAME_TYPE_INT)
307                     *(int *)((char *)rtp_payload_data + attr_names[i].offset) = atoi(value);
308                 else if (attr_names[i].type == ATTR_NAME_TYPE_STR)
309                     *(char **)((char *)rtp_payload_data + attr_names[i].offset) = av_strdup(value);
310             }
311         }
312     }
313 }
314
315 typedef struct SDPParseState {
316     /* SDP only */
317     struct in_addr default_ip;
318     int default_ttl;
319 } SDPParseState;
320
321 static void sdp_parse_line(AVFormatContext *s, SDPParseState *s1,
322                            int letter, const char *buf)
323 {
324     RTSPState *rt = s->priv_data;
325     char buf1[64], st_type[64];
326     const char *p;
327     int codec_type, payload_type, i;
328     AVStream *st;
329     RTSPStream *rtsp_st;
330     struct in_addr sdp_ip;
331     int ttl;
332
333 #ifdef DEBUG
334     printf("sdp: %c='%s'\n", letter, buf);
335 #endif
336
337     p = buf;
338     switch(letter) {
339     case 'c':
340         get_word(buf1, sizeof(buf1), &p);
341         if (strcmp(buf1, "IN") != 0)
342             return;
343         get_word(buf1, sizeof(buf1), &p);
344         if (strcmp(buf1, "IP4") != 0)
345             return;
346         get_word_sep(buf1, sizeof(buf1), "/", &p);
347         if (inet_aton(buf1, &sdp_ip) == 0)
348             return;
349         ttl = 16;
350         if (*p == '/') {
351             p++;
352             get_word_sep(buf1, sizeof(buf1), "/", &p);
353             ttl = atoi(buf1);
354         }
355         if (s->nb_streams == 0) {
356             s1->default_ip = sdp_ip;
357             s1->default_ttl = ttl;
358         } else {
359             st = s->streams[s->nb_streams - 1];
360             rtsp_st = st->priv_data;
361             rtsp_st->sdp_ip = sdp_ip;
362             rtsp_st->sdp_ttl = ttl;
363         }
364         break;
365     case 's':
366         pstrcpy(s->title, sizeof(s->title), p);
367         break;
368     case 'i':
369         if (s->nb_streams == 0) {
370             pstrcpy(s->comment, sizeof(s->comment), p);
371             break;
372         }
373         break;
374     case 'm':
375         /* new stream */
376         get_word(st_type, sizeof(st_type), &p);
377         if (!strcmp(st_type, "audio")) {
378             codec_type = CODEC_TYPE_AUDIO;
379         } else if (!strcmp(st_type, "video")) {
380             codec_type = CODEC_TYPE_VIDEO;
381         } else {
382             return;
383         }
384         rtsp_st = av_mallocz(sizeof(RTSPStream));
385         if (!rtsp_st)
386             return;
387         rtsp_st->stream_index = -1;
388         dynarray_add(&rt->rtsp_streams, &rt->nb_rtsp_streams, rtsp_st);
389
390         rtsp_st->sdp_ip = s1->default_ip;
391         rtsp_st->sdp_ttl = s1->default_ttl;
392
393         get_word(buf1, sizeof(buf1), &p); /* port */
394         rtsp_st->sdp_port = atoi(buf1);
395
396         get_word(buf1, sizeof(buf1), &p); /* protocol (ignored) */
397
398         /* XXX: handle list of formats */
399         get_word(buf1, sizeof(buf1), &p); /* format list */
400         rtsp_st->sdp_payload_type = atoi(buf1);
401
402         if (!strcmp(AVRtpPayloadTypes[rtsp_st->sdp_payload_type].enc_name, "MP2T")) {
403             /* no corresponding stream */
404         } else {
405             st = av_new_stream(s, 0);
406             if (!st)
407                 return;
408             st->priv_data = rtsp_st;
409             rtsp_st->stream_index = st->index;
410             st->codec->codec_type = codec_type;
411             if (rtsp_st->sdp_payload_type < RTP_PT_PRIVATE) {
412                 /* if standard payload type, we can find the codec right now */
413                 rtp_get_codec_info(st->codec, rtsp_st->sdp_payload_type);
414             }
415         }
416         /* put a default control url */
417         pstrcpy(rtsp_st->control_url, sizeof(rtsp_st->control_url), s->filename);
418         break;
419     case 'a':
420         if (strstart(p, "control:", &p) && s->nb_streams > 0) {
421             char proto[32];
422             /* get the control url */
423             st = s->streams[s->nb_streams - 1];
424             rtsp_st = st->priv_data;
425
426             /* XXX: may need to add full url resolution */
427             url_split(proto, sizeof(proto), NULL, 0, NULL, 0, NULL, NULL, 0, p);
428             if (proto[0] == '\0') {
429                 /* relative control URL */
430                 pstrcat(rtsp_st->control_url, sizeof(rtsp_st->control_url), "/");
431                 pstrcat(rtsp_st->control_url, sizeof(rtsp_st->control_url), p);
432             } else {
433                 pstrcpy(rtsp_st->control_url, sizeof(rtsp_st->control_url), p);
434             }
435         } else if (strstart(p, "rtpmap:", &p)) {
436             /* NOTE: rtpmap is only supported AFTER the 'm=' tag */
437             get_word(buf1, sizeof(buf1), &p);
438             payload_type = atoi(buf1);
439             for(i = 0; i < s->nb_streams;i++) {
440                 st = s->streams[i];
441                 rtsp_st = st->priv_data;
442                 if (rtsp_st->sdp_payload_type == payload_type) {
443                     sdp_parse_rtpmap(st->codec, payload_type, p);
444                 }
445             }
446         } else if (strstart(p, "fmtp:", &p)) {
447             /* NOTE: fmtp is only supported AFTER the 'a=rtpmap:xxx' tag */
448             get_word(buf1, sizeof(buf1), &p);
449             payload_type = atoi(buf1);
450             for(i = 0; i < s->nb_streams;i++) {
451                 st = s->streams[i];
452                 rtsp_st = st->priv_data;
453                 if (rtsp_st->sdp_payload_type == payload_type) {
454                     sdp_parse_fmtp(st, p);
455                 }
456             }
457         }
458         break;
459     }
460 }
461
462 static int sdp_parse(AVFormatContext *s, const char *content)
463 {
464     const char *p;
465     int letter;
466     char buf[1024], *q;
467     SDPParseState sdp_parse_state, *s1 = &sdp_parse_state;
468
469     memset(s1, 0, sizeof(SDPParseState));
470     p = content;
471     for(;;) {
472         skip_spaces(&p);
473         letter = *p;
474         if (letter == '\0')
475             break;
476         p++;
477         if (*p != '=')
478             goto next_line;
479         p++;
480         /* get the content */
481         q = buf;
482         while (*p != '\n' && *p != '\r' && *p != '\0') {
483             if ((q - buf) < sizeof(buf) - 1)
484                 *q++ = *p;
485             p++;
486         }
487         *q = '\0';
488         sdp_parse_line(s, s1, letter, buf);
489     next_line:
490         while (*p != '\n' && *p != '\0')
491             p++;
492         if (*p == '\n')
493             p++;
494     }
495     return 0;
496 }
497
498 static void rtsp_parse_range(int *min_ptr, int *max_ptr, const char **pp)
499 {
500     const char *p;
501     int v;
502
503     p = *pp;
504     skip_spaces(&p);
505     v = strtol(p, (char **)&p, 10);
506     if (*p == '-') {
507         p++;
508         *min_ptr = v;
509         v = strtol(p, (char **)&p, 10);
510         *max_ptr = v;
511     } else {
512         *min_ptr = v;
513         *max_ptr = v;
514     }
515     *pp = p;
516 }
517
518 /* XXX: only one transport specification is parsed */
519 static void rtsp_parse_transport(RTSPHeader *reply, const char *p)
520 {
521     char transport_protocol[16];
522     char profile[16];
523     char lower_transport[16];
524     char parameter[16];
525     RTSPTransportField *th;
526     char buf[256];
527
528     reply->nb_transports = 0;
529
530     for(;;) {
531         skip_spaces(&p);
532         if (*p == '\0')
533             break;
534
535         th = &reply->transports[reply->nb_transports];
536
537         get_word_sep(transport_protocol, sizeof(transport_protocol),
538                      "/", &p);
539         if (*p == '/')
540             p++;
541         get_word_sep(profile, sizeof(profile), "/;,", &p);
542         lower_transport[0] = '\0';
543         if (*p == '/') {
544             p++;
545             get_word_sep(lower_transport, sizeof(lower_transport),
546                          ";,", &p);
547         }
548         if (!strcasecmp(lower_transport, "TCP"))
549             th->protocol = RTSP_PROTOCOL_RTP_TCP;
550         else
551             th->protocol = RTSP_PROTOCOL_RTP_UDP;
552
553         if (*p == ';')
554             p++;
555         /* get each parameter */
556         while (*p != '\0' && *p != ',') {
557             get_word_sep(parameter, sizeof(parameter), "=;,", &p);
558             if (!strcmp(parameter, "port")) {
559                 if (*p == '=') {
560                     p++;
561                     rtsp_parse_range(&th->port_min, &th->port_max, &p);
562                 }
563             } else if (!strcmp(parameter, "client_port")) {
564                 if (*p == '=') {
565                     p++;
566                     rtsp_parse_range(&th->client_port_min,
567                                      &th->client_port_max, &p);
568                 }
569             } else if (!strcmp(parameter, "server_port")) {
570                 if (*p == '=') {
571                     p++;
572                     rtsp_parse_range(&th->server_port_min,
573                                      &th->server_port_max, &p);
574                 }
575             } else if (!strcmp(parameter, "interleaved")) {
576                 if (*p == '=') {
577                     p++;
578                     rtsp_parse_range(&th->interleaved_min,
579                                      &th->interleaved_max, &p);
580                 }
581             } else if (!strcmp(parameter, "multicast")) {
582                 if (th->protocol == RTSP_PROTOCOL_RTP_UDP)
583                     th->protocol = RTSP_PROTOCOL_RTP_UDP_MULTICAST;
584             } else if (!strcmp(parameter, "ttl")) {
585                 if (*p == '=') {
586                     p++;
587                     th->ttl = strtol(p, (char **)&p, 10);
588                 }
589             } else if (!strcmp(parameter, "destination")) {
590                 struct in_addr ipaddr;
591
592                 if (*p == '=') {
593                     p++;
594                     get_word_sep(buf, sizeof(buf), ";,", &p);
595                     if (inet_aton(buf, &ipaddr))
596                         th->destination = ntohl(ipaddr.s_addr);
597                 }
598             }
599             while (*p != ';' && *p != '\0' && *p != ',')
600                 p++;
601             if (*p == ';')
602                 p++;
603         }
604         if (*p == ',')
605             p++;
606
607         reply->nb_transports++;
608     }
609 }
610
611 static void rtsp_parse_range_npt(RTSPHeader *reply, const char *p)
612 {
613     char buf[256];
614
615     skip_spaces(&p);
616     if (!stristart(p, "npt=", &p))
617         return;
618
619     reply->range_start = AV_NOPTS_VALUE;
620     reply->range_end = AV_NOPTS_VALUE;
621
622     get_word_sep(buf, sizeof(buf), "-", &p);
623     reply->range_start = parse_date(buf, 1);
624     if (*p == '-') {
625         p++;
626         get_word_sep(buf, sizeof(buf), "-", &p);
627         reply->range_end = parse_date(buf, 1);
628     }
629 }
630
631 void rtsp_parse_line(RTSPHeader *reply, const char *buf)
632 {
633     const char *p;
634
635     /* NOTE: we do case independent match for broken servers */
636     p = buf;
637     if (stristart(p, "Session:", &p)) {
638         get_word_sep(reply->session_id, sizeof(reply->session_id), ";", &p);
639     } else if (stristart(p, "Content-Length:", &p)) {
640         reply->content_length = strtol(p, NULL, 10);
641     } else if (stristart(p, "Transport:", &p)) {
642         rtsp_parse_transport(reply, p);
643     } else if (stristart(p, "CSeq:", &p)) {
644         reply->seq = strtol(p, NULL, 10);
645     } else if (stristart(p, "Range:", &p)) {
646         rtsp_parse_range_npt(reply, p);
647     }
648 }
649
650 static int url_readbuf(URLContext *h, unsigned char *buf, int size)
651 {
652     int ret, len;
653
654     len = 0;
655     while (len < size) {
656         ret = url_read(h, buf+len, size-len);
657         if (ret < 1)
658             return ret;
659         len += ret;
660     }
661     return len;
662 }
663
664 /* skip a RTP/TCP interleaved packet */
665 static void rtsp_skip_packet(AVFormatContext *s)
666 {
667     RTSPState *rt = s->priv_data;
668     int ret, len, len1;
669     uint8_t buf[1024];
670
671     ret = url_readbuf(rt->rtsp_hd, buf, 3);
672     if (ret != 3)
673         return;
674     len = (buf[1] << 8) | buf[2];
675 #ifdef DEBUG
676     printf("skipping RTP packet len=%d\n", len);
677 #endif
678     /* skip payload */
679     while (len > 0) {
680         len1 = len;
681         if (len1 > sizeof(buf))
682             len1 = sizeof(buf);
683         ret = url_readbuf(rt->rtsp_hd, buf, len1);
684         if (ret != len1)
685             return;
686         len -= len1;
687     }
688 }
689
690 static void rtsp_send_cmd(AVFormatContext *s,
691                           const char *cmd, RTSPHeader *reply,
692                           unsigned char **content_ptr)
693 {
694     RTSPState *rt = s->priv_data;
695     char buf[4096], buf1[1024], *q;
696     unsigned char ch;
697     const char *p;
698     int content_length, line_count;
699     unsigned char *content = NULL;
700
701     memset(reply, 0, sizeof(RTSPHeader));
702
703     rt->seq++;
704     pstrcpy(buf, sizeof(buf), cmd);
705     snprintf(buf1, sizeof(buf1), "CSeq: %d\r\n", rt->seq);
706     pstrcat(buf, sizeof(buf), buf1);
707     if (rt->session_id[0] != '\0' && !strstr(cmd, "\nIf-Match:")) {
708         snprintf(buf1, sizeof(buf1), "Session: %s\r\n", rt->session_id);
709         pstrcat(buf, sizeof(buf), buf1);
710     }
711     pstrcat(buf, sizeof(buf), "\r\n");
712 #ifdef DEBUG
713     printf("Sending:\n%s--\n", buf);
714 #endif
715     url_write(rt->rtsp_hd, buf, strlen(buf));
716
717     /* parse reply (XXX: use buffers) */
718     line_count = 0;
719     rt->last_reply[0] = '\0';
720     for(;;) {
721         q = buf;
722         for(;;) {
723             if (url_readbuf(rt->rtsp_hd, &ch, 1) != 1)
724                 break;
725             if (ch == '\n')
726                 break;
727             if (ch == '$') {
728                 /* XXX: only parse it if first char on line ? */
729                 rtsp_skip_packet(s);
730             } else if (ch != '\r') {
731                 if ((q - buf) < sizeof(buf) - 1)
732                     *q++ = ch;
733             }
734         }
735         *q = '\0';
736 #ifdef DEBUG
737         printf("line='%s'\n", buf);
738 #endif
739         /* test if last line */
740         if (buf[0] == '\0')
741             break;
742         p = buf;
743         if (line_count == 0) {
744             /* get reply code */
745             get_word(buf1, sizeof(buf1), &p);
746             get_word(buf1, sizeof(buf1), &p);
747             reply->status_code = atoi(buf1);
748         } else {
749             rtsp_parse_line(reply, p);
750             pstrcat(rt->last_reply, sizeof(rt->last_reply), p);
751             pstrcat(rt->last_reply, sizeof(rt->last_reply), "\n");
752         }
753         line_count++;
754     }
755
756     if (rt->session_id[0] == '\0' && reply->session_id[0] != '\0')
757         pstrcpy(rt->session_id, sizeof(rt->session_id), reply->session_id);
758
759     content_length = reply->content_length;
760     if (content_length > 0) {
761         /* leave some room for a trailing '\0' (useful for simple parsing) */
762         content = av_malloc(content_length + 1);
763         (void)url_readbuf(rt->rtsp_hd, content, content_length);
764         content[content_length] = '\0';
765     }
766     if (content_ptr)
767         *content_ptr = content;
768 }
769
770 /* useful for modules: set RTSP callback function */
771
772 void rtsp_set_callback(FFRTSPCallback *rtsp_cb)
773 {
774     ff_rtsp_callback = rtsp_cb;
775 }
776
777
778 /* close and free RTSP streams */
779 static void rtsp_close_streams(RTSPState *rt)
780 {
781     int i;
782     RTSPStream *rtsp_st;
783
784     for(i=0;i<rt->nb_rtsp_streams;i++) {
785         rtsp_st = rt->rtsp_streams[i];
786         if (rtsp_st) {
787             if (rtsp_st->rtp_ctx)
788                 rtp_parse_close(rtsp_st->rtp_ctx);
789             if (rtsp_st->rtp_handle)
790                 url_close(rtsp_st->rtp_handle);
791         }
792         av_free(rtsp_st);
793     }
794     av_free(rt->rtsp_streams);
795 }
796
797 static int rtsp_read_header(AVFormatContext *s,
798                             AVFormatParameters *ap)
799 {
800     RTSPState *rt = s->priv_data;
801     char host[1024], path[1024], tcpname[1024], cmd[2048];
802     URLContext *rtsp_hd;
803     int port, i, j, ret, err;
804     RTSPHeader reply1, *reply = &reply1;
805     unsigned char *content = NULL;
806     RTSPStream *rtsp_st;
807     int protocol_mask;
808     AVStream *st;
809
810     /* extract hostname and port */
811     url_split(NULL, 0, NULL, 0,
812               host, sizeof(host), &port, path, sizeof(path), s->filename);
813     if (port < 0)
814         port = RTSP_DEFAULT_PORT;
815
816     /* open the tcp connexion */
817     snprintf(tcpname, sizeof(tcpname), "tcp://%s:%d", host, port);
818     if (url_open(&rtsp_hd, tcpname, URL_RDWR) < 0)
819         return AVERROR_IO;
820     rt->rtsp_hd = rtsp_hd;
821     rt->seq = 0;
822
823     /* describe the stream */
824     snprintf(cmd, sizeof(cmd),
825              "DESCRIBE %s RTSP/1.0\r\n"
826              "Accept: application/sdp\r\n",
827              s->filename);
828     rtsp_send_cmd(s, cmd, reply, &content);
829     if (!content) {
830         err = AVERROR_INVALIDDATA;
831         goto fail;
832     }
833     if (reply->status_code != RTSP_STATUS_OK) {
834         err = AVERROR_INVALIDDATA;
835         goto fail;
836     }
837
838     /* now we got the SDP description, we parse it */
839     ret = sdp_parse(s, (const char *)content);
840     av_freep(&content);
841     if (ret < 0) {
842         err = AVERROR_INVALIDDATA;
843         goto fail;
844     }
845
846     protocol_mask = rtsp_default_protocols;
847
848     /* for each stream, make the setup request */
849     /* XXX: we assume the same server is used for the control of each
850        RTSP stream */
851
852     for(j = RTSP_RTP_PORT_MIN, i = 0; i < rt->nb_rtsp_streams; ++i) {
853         char transport[2048];
854
855         rtsp_st = rt->rtsp_streams[i];
856
857         /* compute available transports */
858         transport[0] = '\0';
859
860         /* RTP/UDP */
861         if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP)) {
862             char buf[256];
863
864             /* first try in specified port range */
865             if (RTSP_RTP_PORT_MIN != 0) {
866                 while(j <= RTSP_RTP_PORT_MAX) {
867                     snprintf(buf, sizeof(buf), "rtp://?localport=%d", j);
868                     if (url_open(&rtsp_st->rtp_handle, buf, URL_RDONLY) == 0) {
869                         j += 2; /* we will use two port by rtp stream (rtp and rtcp) */
870                         goto rtp_opened;
871                     }
872                 }
873             }
874
875 /*            then try on any port
876 **            if (url_open(&rtsp_st->rtp_handle, "rtp://", URL_RDONLY) < 0) {
877 **                err = AVERROR_INVALIDDATA;
878 **                goto fail;
879 **            }
880 */
881
882         rtp_opened:
883             port = rtp_get_local_port(rtsp_st->rtp_handle);
884             if (transport[0] != '\0')
885                 pstrcat(transport, sizeof(transport), ",");
886             snprintf(transport + strlen(transport), sizeof(transport) - strlen(transport) - 1,
887                      "RTP/AVP/UDP;unicast;client_port=%d-%d",
888                      port, port + 1);
889         }
890
891         /* RTP/TCP */
892         else if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_TCP)) {
893             if (transport[0] != '\0')
894                 pstrcat(transport, sizeof(transport), ",");
895             snprintf(transport + strlen(transport), sizeof(transport) - strlen(transport) - 1,
896                      "RTP/AVP/TCP");
897         }
898
899         else if (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP_MULTICAST)) {
900             if (transport[0] != '\0')
901                 pstrcat(transport, sizeof(transport), ",");
902             snprintf(transport + strlen(transport),
903                      sizeof(transport) - strlen(transport) - 1,
904                      "RTP/AVP/UDP;multicast");
905         }
906         snprintf(cmd, sizeof(cmd),
907                  "SETUP %s RTSP/1.0\r\n"
908                  "Transport: %s\r\n",
909                  rtsp_st->control_url, transport);
910         rtsp_send_cmd(s, cmd, reply, NULL);
911         if (reply->status_code != RTSP_STATUS_OK ||
912             reply->nb_transports != 1) {
913             err = AVERROR_INVALIDDATA;
914             goto fail;
915         }
916
917         /* XXX: same protocol for all streams is required */
918         if (i > 0) {
919             if (reply->transports[0].protocol != rt->protocol) {
920                 err = AVERROR_INVALIDDATA;
921                 goto fail;
922             }
923         } else {
924             rt->protocol = reply->transports[0].protocol;
925         }
926
927         /* close RTP connection if not choosen */
928         if (reply->transports[0].protocol != RTSP_PROTOCOL_RTP_UDP &&
929             (protocol_mask & (1 << RTSP_PROTOCOL_RTP_UDP))) {
930             url_close(rtsp_st->rtp_handle);
931             rtsp_st->rtp_handle = NULL;
932         }
933
934         switch(reply->transports[0].protocol) {
935         case RTSP_PROTOCOL_RTP_TCP:
936             rtsp_st->interleaved_min = reply->transports[0].interleaved_min;
937             rtsp_st->interleaved_max = reply->transports[0].interleaved_max;
938             break;
939
940         case RTSP_PROTOCOL_RTP_UDP:
941             {
942                 char url[1024];
943
944                 /* XXX: also use address if specified */
945                 snprintf(url, sizeof(url), "rtp://%s:%d",
946                          host, reply->transports[0].server_port_min);
947                 if (rtp_set_remote_url(rtsp_st->rtp_handle, url) < 0) {
948                     err = AVERROR_INVALIDDATA;
949                     goto fail;
950                 }
951             }
952             break;
953         case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
954             {
955                 char url[1024];
956                 int ttl;
957
958                 ttl = reply->transports[0].ttl;
959                 if (!ttl)
960                     ttl = 16;
961                 snprintf(url, sizeof(url), "rtp://%s:%d?multicast=1&ttl=%d",
962                          host,
963                          reply->transports[0].server_port_min,
964                          ttl);
965                 if (url_open(&rtsp_st->rtp_handle, url, URL_RDONLY) < 0) {
966                     err = AVERROR_INVALIDDATA;
967                     goto fail;
968                 }
969             }
970             break;
971         }
972         /* open the RTP context */
973         st = NULL;
974         if (rtsp_st->stream_index >= 0)
975             st = s->streams[rtsp_st->stream_index];
976         if (!st)
977             s->ctx_flags |= AVFMTCTX_NOHEADER;
978         rtsp_st->rtp_ctx = rtp_parse_open(s, st, rtsp_st->sdp_payload_type, &rtsp_st->rtp_payload_data);
979
980         if (!rtsp_st->rtp_ctx) {
981             err = AVERROR_NOMEM;
982             goto fail;
983         }
984     }
985
986     /* use callback if available to extend setup */
987     if (ff_rtsp_callback) {
988         if (ff_rtsp_callback(RTSP_ACTION_CLIENT_SETUP, rt->session_id,
989                              NULL, 0, rt->last_reply) < 0) {
990             err = AVERROR_INVALIDDATA;
991             goto fail;
992         }
993     }
994
995
996     rt->state = RTSP_STATE_IDLE;
997     rt->seek_timestamp = 0; /* default is to start stream at position
998                                zero */
999     if (ap->initial_pause) {
1000         /* do not start immediately */
1001     } else {
1002         if (rtsp_read_play(s) < 0) {
1003             err = AVERROR_INVALIDDATA;
1004             goto fail;
1005         }
1006     }
1007     return 0;
1008  fail:
1009     rtsp_close_streams(rt);
1010     av_freep(&content);
1011     url_close(rt->rtsp_hd);
1012     return err;
1013 }
1014
1015 static int tcp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
1016                            uint8_t *buf, int buf_size)
1017 {
1018     RTSPState *rt = s->priv_data;
1019     int id, len, i, ret;
1020     RTSPStream *rtsp_st;
1021
1022 #ifdef DEBUG_RTP_TCP
1023     printf("tcp_read_packet:\n");
1024 #endif
1025  redo:
1026     for(;;) {
1027         ret = url_readbuf(rt->rtsp_hd, buf, 1);
1028 #ifdef DEBUG_RTP_TCP
1029         printf("ret=%d c=%02x [%c]\n", ret, buf[0], buf[0]);
1030 #endif
1031         if (ret != 1)
1032             return -1;
1033         if (buf[0] == '$')
1034             break;
1035     }
1036     ret = url_readbuf(rt->rtsp_hd, buf, 3);
1037     if (ret != 3)
1038         return -1;
1039     id = buf[0];
1040     len = (buf[1] << 8) | buf[2];
1041 #ifdef DEBUG_RTP_TCP
1042     printf("id=%d len=%d\n", id, len);
1043 #endif
1044     if (len > buf_size || len < 12)
1045         goto redo;
1046     /* get the data */
1047     ret = url_readbuf(rt->rtsp_hd, buf, len);
1048     if (ret != len)
1049         return -1;
1050
1051     /* find the matching stream */
1052     for(i = 0; i < rt->nb_rtsp_streams; i++) {
1053         rtsp_st = rt->rtsp_streams[i];
1054         if (id >= rtsp_st->interleaved_min &&
1055             id <= rtsp_st->interleaved_max)
1056             goto found;
1057     }
1058     goto redo;
1059  found:
1060     *prtsp_st = rtsp_st;
1061     return len;
1062 }
1063
1064 static int udp_read_packet(AVFormatContext *s, RTSPStream **prtsp_st,
1065                            uint8_t *buf, int buf_size)
1066 {
1067     RTSPState *rt = s->priv_data;
1068     RTSPStream *rtsp_st;
1069     fd_set rfds;
1070     int fd1, fd2, fd_max, n, i, ret;
1071     struct timeval tv;
1072
1073     for(;;) {
1074         if (url_interrupt_cb())
1075             return -1;
1076         FD_ZERO(&rfds);
1077         fd_max = -1;
1078         for(i = 0; i < rt->nb_rtsp_streams; i++) {
1079             rtsp_st = rt->rtsp_streams[i];
1080             /* currently, we cannot probe RTCP handle because of blocking restrictions */
1081             rtp_get_file_handles(rtsp_st->rtp_handle, &fd1, &fd2);
1082             if (fd1 > fd_max)
1083                 fd_max = fd1;
1084             FD_SET(fd1, &rfds);
1085         }
1086         tv.tv_sec = 0;
1087         tv.tv_usec = 100 * 1000;
1088         n = select(fd_max + 1, &rfds, NULL, NULL, &tv);
1089         if (n > 0) {
1090             for(i = 0; i < rt->nb_rtsp_streams; i++) {
1091                 rtsp_st = rt->rtsp_streams[i];
1092                 rtp_get_file_handles(rtsp_st->rtp_handle, &fd1, &fd2);
1093                 if (FD_ISSET(fd1, &rfds)) {
1094                     ret = url_read(rtsp_st->rtp_handle, buf, buf_size);
1095                     if (ret > 0) {
1096                         *prtsp_st = rtsp_st;
1097                         return ret;
1098                     }
1099                 }
1100             }
1101         }
1102     }
1103 }
1104
1105 static int rtsp_read_packet(AVFormatContext *s,
1106                             AVPacket *pkt)
1107 {
1108     RTSPState *rt = s->priv_data;
1109     RTSPStream *rtsp_st;
1110     int ret, len;
1111     uint8_t buf[RTP_MAX_PACKET_LENGTH];
1112
1113     /* get next frames from the same RTP packet */
1114     if (rt->cur_rtp) {
1115         ret = rtp_parse_packet(rt->cur_rtp, pkt, NULL, 0);
1116         if (ret == 0) {
1117             rt->cur_rtp = NULL;
1118             return 0;
1119         } else if (ret == 1) {
1120             return 0;
1121         } else {
1122             rt->cur_rtp = NULL;
1123         }
1124     }
1125
1126     /* read next RTP packet */
1127  redo:
1128     switch(rt->protocol) {
1129     default:
1130     case RTSP_PROTOCOL_RTP_TCP:
1131         len = tcp_read_packet(s, &rtsp_st, buf, sizeof(buf));
1132         break;
1133     case RTSP_PROTOCOL_RTP_UDP:
1134     case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
1135         len = udp_read_packet(s, &rtsp_st, buf, sizeof(buf));
1136         break;
1137     }
1138     if (len < 0)
1139         return AVERROR_IO;
1140     ret = rtp_parse_packet(rtsp_st->rtp_ctx, pkt, buf, len);
1141     if (ret < 0)
1142         goto redo;
1143     if (ret == 1) {
1144         /* more packets may follow, so we save the RTP context */
1145         rt->cur_rtp = rtsp_st->rtp_ctx;
1146     }
1147     return 0;
1148 }
1149
1150 static int rtsp_read_play(AVFormatContext *s)
1151 {
1152     RTSPState *rt = s->priv_data;
1153     RTSPHeader reply1, *reply = &reply1;
1154     char cmd[1024];
1155
1156     av_log(s, AV_LOG_DEBUG, "hello state=%d\n", rt->state);
1157
1158     if (rt->state == RTSP_STATE_PAUSED) {
1159         snprintf(cmd, sizeof(cmd),
1160                  "PLAY %s RTSP/1.0\r\n",
1161                  s->filename);
1162     } else {
1163         snprintf(cmd, sizeof(cmd),
1164                  "PLAY %s RTSP/1.0\r\n"
1165                  "Range: npt=%0.3f-\r\n",
1166                  s->filename,
1167                  (double)rt->seek_timestamp / AV_TIME_BASE);
1168     }
1169     rtsp_send_cmd(s, cmd, reply, NULL);
1170     if (reply->status_code != RTSP_STATUS_OK) {
1171         return -1;
1172     } else {
1173         rt->state = RTSP_STATE_PLAYING;
1174         return 0;
1175     }
1176 }
1177
1178 /* pause the stream */
1179 static int rtsp_read_pause(AVFormatContext *s)
1180 {
1181     RTSPState *rt = s->priv_data;
1182     RTSPHeader reply1, *reply = &reply1;
1183     char cmd[1024];
1184
1185     rt = s->priv_data;
1186
1187     if (rt->state != RTSP_STATE_PLAYING)
1188         return 0;
1189
1190     snprintf(cmd, sizeof(cmd),
1191              "PAUSE %s RTSP/1.0\r\n",
1192              s->filename);
1193     rtsp_send_cmd(s, cmd, reply, NULL);
1194     if (reply->status_code != RTSP_STATUS_OK) {
1195         return -1;
1196     } else {
1197         rt->state = RTSP_STATE_PAUSED;
1198         return 0;
1199     }
1200 }
1201
1202 static int rtsp_read_seek(AVFormatContext *s, int stream_index,
1203                           int64_t timestamp, int flags)
1204 {
1205     RTSPState *rt = s->priv_data;
1206
1207     rt->seek_timestamp = timestamp;
1208     switch(rt->state) {
1209     default:
1210     case RTSP_STATE_IDLE:
1211         break;
1212     case RTSP_STATE_PLAYING:
1213         if (rtsp_read_play(s) != 0)
1214             return -1;
1215         break;
1216     case RTSP_STATE_PAUSED:
1217         rt->state = RTSP_STATE_IDLE;
1218         break;
1219     }
1220     return 0;
1221 }
1222
1223 static int rtsp_read_close(AVFormatContext *s)
1224 {
1225     RTSPState *rt = s->priv_data;
1226     RTSPHeader reply1, *reply = &reply1;
1227     char cmd[1024];
1228
1229 #if 0
1230     /* NOTE: it is valid to flush the buffer here */
1231     if (rt->protocol == RTSP_PROTOCOL_RTP_TCP) {
1232         url_fclose(&rt->rtsp_gb);
1233     }
1234 #endif
1235     snprintf(cmd, sizeof(cmd),
1236              "TEARDOWN %s RTSP/1.0\r\n",
1237              s->filename);
1238     rtsp_send_cmd(s, cmd, reply, NULL);
1239
1240     if (ff_rtsp_callback) {
1241         ff_rtsp_callback(RTSP_ACTION_CLIENT_TEARDOWN, rt->session_id,
1242                          NULL, 0, NULL);
1243     }
1244
1245     rtsp_close_streams(rt);
1246     url_close(rt->rtsp_hd);
1247     return 0;
1248 }
1249
1250 AVInputFormat rtsp_demuxer = {
1251     "rtsp",
1252     "RTSP input format",
1253     sizeof(RTSPState),
1254     rtsp_probe,
1255     rtsp_read_header,
1256     rtsp_read_packet,
1257     rtsp_read_close,
1258     rtsp_read_seek,
1259     .flags = AVFMT_NOFILE,
1260     .read_play = rtsp_read_play,
1261     .read_pause = rtsp_read_pause,
1262 };
1263
1264 static int sdp_probe(AVProbeData *p1)
1265 {
1266     const char *p = p1->buf, *p_end = p1->buf + p1->buf_size;
1267
1268     /* we look for a line beginning "c=IN IP4" */
1269     while (p < p_end && *p != '\0') {
1270         if (p + sizeof("c=IN IP4") - 1 < p_end && strstart(p, "c=IN IP4", NULL))
1271             return AVPROBE_SCORE_MAX / 2;
1272
1273         while(p < p_end - 1 && *p != '\n') p++;
1274         if (++p >= p_end)
1275             break;
1276         if (*p == '\r')
1277             p++;
1278     }
1279     return 0;
1280 }
1281
1282 #define SDP_MAX_SIZE 8192
1283
1284 static int sdp_read_header(AVFormatContext *s,
1285                            AVFormatParameters *ap)
1286 {
1287     RTSPState *rt = s->priv_data;
1288     RTSPStream *rtsp_st;
1289     int size, i, err;
1290     char *content;
1291     char url[1024];
1292     AVStream *st;
1293
1294     /* read the whole sdp file */
1295     /* XXX: better loading */
1296     content = av_malloc(SDP_MAX_SIZE);
1297     size = get_buffer(&s->pb, content, SDP_MAX_SIZE - 1);
1298     if (size <= 0) {
1299         av_free(content);
1300         return AVERROR_INVALIDDATA;
1301     }
1302     content[size] ='\0';
1303
1304     sdp_parse(s, content);
1305     av_free(content);
1306
1307     /* open each RTP stream */
1308     for(i=0;i<rt->nb_rtsp_streams;i++) {
1309         rtsp_st = rt->rtsp_streams[i];
1310
1311         snprintf(url, sizeof(url), "rtp://%s:%d?multicast=1&ttl=%d",
1312                  inet_ntoa(rtsp_st->sdp_ip),
1313                  rtsp_st->sdp_port,
1314                  rtsp_st->sdp_ttl);
1315         if (url_open(&rtsp_st->rtp_handle, url, URL_RDONLY) < 0) {
1316             err = AVERROR_INVALIDDATA;
1317             goto fail;
1318         }
1319         /* open the RTP context */
1320         st = NULL;
1321         if (rtsp_st->stream_index >= 0)
1322             st = s->streams[rtsp_st->stream_index];
1323         if (!st)
1324             s->ctx_flags |= AVFMTCTX_NOHEADER;
1325         rtsp_st->rtp_ctx = rtp_parse_open(s, st, rtsp_st->sdp_payload_type, &rtsp_st->rtp_payload_data);
1326         if (!rtsp_st->rtp_ctx) {
1327             err = AVERROR_NOMEM;
1328             goto fail;
1329         }
1330     }
1331     return 0;
1332  fail:
1333     rtsp_close_streams(rt);
1334     return err;
1335 }
1336
1337 static int sdp_read_packet(AVFormatContext *s,
1338                             AVPacket *pkt)
1339 {
1340     return rtsp_read_packet(s, pkt);
1341 }
1342
1343 static int sdp_read_close(AVFormatContext *s)
1344 {
1345     RTSPState *rt = s->priv_data;
1346     rtsp_close_streams(rt);
1347     return 0;
1348 }
1349
1350 #ifdef CONFIG_SDP_DEMUXER
1351 AVInputFormat sdp_demuxer = {
1352     "sdp",
1353     "SDP",
1354     sizeof(RTSPState),
1355     sdp_probe,
1356     sdp_read_header,
1357     sdp_read_packet,
1358     sdp_read_close,
1359 };
1360 #endif
1361
1362 /* dummy redirector format (used directly in av_open_input_file now) */
1363 static int redir_probe(AVProbeData *pd)
1364 {
1365     const char *p;
1366     p = pd->buf;
1367     while (redir_isspace(*p))
1368         p++;
1369     if (strstart(p, "http://", NULL) ||
1370         strstart(p, "rtsp://", NULL))
1371         return AVPROBE_SCORE_MAX;
1372     return 0;
1373 }
1374
1375 /* called from utils.c */
1376 int redir_open(AVFormatContext **ic_ptr, ByteIOContext *f)
1377 {
1378     char buf[4096], *q;
1379     int c;
1380     AVFormatContext *ic = NULL;
1381
1382     /* parse each URL and try to open it */
1383     c = url_fgetc(f);
1384     while (c != URL_EOF) {
1385         /* skip spaces */
1386         for(;;) {
1387             if (!redir_isspace(c))
1388                 break;
1389             c = url_fgetc(f);
1390         }
1391         if (c == URL_EOF)
1392             break;
1393         /* record url */
1394         q = buf;
1395         for(;;) {
1396             if (c == URL_EOF || redir_isspace(c))
1397                 break;
1398             if ((q - buf) < sizeof(buf) - 1)
1399                 *q++ = c;
1400             c = url_fgetc(f);
1401         }
1402         *q = '\0';
1403         //printf("URL='%s'\n", buf);
1404         /* try to open the media file */
1405         if (av_open_input_file(&ic, buf, NULL, 0, NULL) == 0)
1406             break;
1407     }
1408     *ic_ptr = ic;
1409     if (!ic)
1410         return AVERROR_IO;
1411     else
1412         return 0;
1413 }
1414
1415 AVInputFormat redir_demuxer = {
1416     "redir",
1417     "Redirector format",
1418     0,
1419     redir_probe,
1420     NULL,
1421     NULL,
1422     NULL,
1423 };