2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 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 #define _XOPEN_SOURCE 600
26 #define closesocket close
31 #include "libavformat/avformat.h"
32 #include "libavformat/network.h"
33 #include "libavformat/os_support.h"
34 #include "libavformat/rtpdec.h"
35 #include "libavformat/rtsp.h"
36 #include "libavutil/avstring.h"
37 #include "libavutil/lfg.h"
38 #include "libavutil/random_seed.h"
39 #include "libavcore/parseutils.h"
40 #include "libavcodec/opt.h"
44 #include <sys/ioctl.h>
59 const char program_name
[] = "FFserver";
60 const int program_birth_year
= 2000;
62 static const OptionDef options
[];
65 HTTPSTATE_WAIT_REQUEST
,
66 HTTPSTATE_SEND_HEADER
,
67 HTTPSTATE_SEND_DATA_HEADER
,
68 HTTPSTATE_SEND_DATA
, /* sending TCP or UDP data */
69 HTTPSTATE_SEND_DATA_TRAILER
,
70 HTTPSTATE_RECEIVE_DATA
,
71 HTTPSTATE_WAIT_FEED
, /* wait for data from the feed */
74 RTSPSTATE_WAIT_REQUEST
,
76 RTSPSTATE_SEND_PACKET
,
79 static const char *http_state
[] = {
95 #if !FF_API_MAX_STREAMS
96 #define MAX_STREAMS 20
99 #define IOBUFFER_INIT_SIZE 8192
101 /* timeouts are in ms */
102 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
103 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
105 #define SYNC_TIMEOUT (10 * 1000)
107 typedef struct RTSPActionServerSetup
{
109 char transport_option
[512];
110 } RTSPActionServerSetup
;
113 int64_t count1
, count2
;
114 int64_t time1
, time2
;
117 /* context associated with one connection */
118 typedef struct HTTPContext
{
119 enum HTTPState state
;
120 int fd
; /* socket file descriptor */
121 struct sockaddr_in from_addr
; /* origin */
122 struct pollfd
*poll_entry
; /* used when polling */
124 uint8_t *buffer_ptr
, *buffer_end
;
127 int chunked_encoding
;
128 int chunk_size
; /* 0 if it needs to be read */
129 struct HTTPContext
*next
;
130 int got_key_frame
; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
134 /* input format handling */
135 AVFormatContext
*fmt_in
;
136 int64_t start_time
; /* In milliseconds - this wraps fairly often */
137 int64_t first_pts
; /* initial pts value */
138 int64_t cur_pts
; /* current pts value from the stream in us */
139 int64_t cur_frame_duration
; /* duration of the current frame in us */
140 int cur_frame_bytes
; /* output frame size, needed to compute
141 the time at which we send each
143 int pts_stream_index
; /* stream we choose as clock reference */
144 int64_t cur_clock
; /* current clock reference value in us */
145 /* output format handling */
146 struct FFStream
*stream
;
147 /* -1 is invalid stream */
148 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
149 int switch_feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
151 AVFormatContext fmt_ctx
; /* instance of FFStream for one user */
152 int last_packet_sent
; /* true if last data packet was sent */
154 DataRateData datarate
;
161 int is_packetized
; /* if true, the stream is packetized */
162 int packet_stream_index
; /* current stream for output in state machine */
164 /* RTSP state specific */
165 uint8_t *pb_buffer
; /* XXX: use that in all the code */
167 int seq
; /* RTSP sequence number */
169 /* RTP state specific */
170 enum RTSPLowerTransport rtp_protocol
;
171 char session_id
[32]; /* session id */
172 AVFormatContext
*rtp_ctx
[MAX_STREAMS
];
174 /* RTP/UDP specific */
175 URLContext
*rtp_handles
[MAX_STREAMS
];
177 /* RTP/TCP specific */
178 struct HTTPContext
*rtsp_c
;
179 uint8_t *packet_buffer
, *packet_buffer_ptr
, *packet_buffer_end
;
182 /* each generated stream is described here */
186 STREAM_TYPE_REDIRECT
,
189 enum IPAddressAction
{
194 typedef struct IPAddressACL
{
195 struct IPAddressACL
*next
;
196 enum IPAddressAction action
;
197 /* These are in host order */
198 struct in_addr first
;
202 /* description of each stream of the ffserver.conf file */
203 typedef struct FFStream
{
204 enum StreamType stream_type
;
205 char filename
[1024]; /* stream filename */
206 struct FFStream
*feed
; /* feed we are using (can be null if
208 AVFormatParameters
*ap_in
; /* input parameters */
209 AVInputFormat
*ifmt
; /* if non NULL, force input format */
212 char dynamic_acl
[1024];
214 int prebuffer
; /* Number of millseconds early to start */
215 int64_t max_time
; /* Number of milliseconds to run */
217 AVStream
*streams
[MAX_STREAMS
];
218 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
219 char feed_filename
[1024]; /* file name of the feed storage, or
220 input file name for a stream */
225 pid_t pid
; /* Of ffmpeg process */
226 time_t pid_start
; /* Of ffmpeg process */
228 struct FFStream
*next
;
229 unsigned bandwidth
; /* bandwidth, in kbits/s */
232 /* multicast specific */
234 struct in_addr multicast_ip
;
235 int multicast_port
; /* first port used for multicast */
237 int loop
; /* if true, send the stream in loops (only meaningful if file) */
240 int feed_opened
; /* true if someone is writing to the feed */
241 int is_feed
; /* true if it is a feed */
242 int readonly
; /* True if writing is prohibited to the file */
243 int truncate
; /* True if feeder connection truncate the feed file */
245 int64_t bytes_served
;
246 int64_t feed_max_size
; /* maximum storage size, zero means unlimited */
247 int64_t feed_write_index
; /* current write position in feed (it wraps around) */
248 int64_t feed_size
; /* current size of feed */
249 struct FFStream
*next_feed
;
252 typedef struct FeedData
{
253 long long data_count
;
254 float avg_frame_size
; /* frame size averaged over last frames with exponential mean */
257 static struct sockaddr_in my_http_addr
;
258 static struct sockaddr_in my_rtsp_addr
;
260 static char logfilename
[1024];
261 static HTTPContext
*first_http_ctx
;
262 static FFStream
*first_feed
; /* contains only feeds */
263 static FFStream
*first_stream
; /* contains all streams, including feeds */
265 static void new_connection(int server_fd
, int is_rtsp
);
266 static void close_connection(HTTPContext
*c
);
269 static int handle_connection(HTTPContext
*c
);
270 static int http_parse_request(HTTPContext
*c
);
271 static int http_send_data(HTTPContext
*c
);
272 static void compute_status(HTTPContext
*c
);
273 static int open_input_stream(HTTPContext
*c
, const char *info
);
274 static int http_start_receive_data(HTTPContext
*c
);
275 static int http_receive_data(HTTPContext
*c
);
278 static int rtsp_parse_request(HTTPContext
*c
);
279 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
);
280 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
);
281 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
, RTSPMessageHeader
*h
);
282 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPMessageHeader
*h
);
283 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPMessageHeader
*h
);
284 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPMessageHeader
*h
);
287 static int prepare_sdp_description(FFStream
*stream
, uint8_t **pbuffer
,
288 struct in_addr my_ip
);
291 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
292 FFStream
*stream
, const char *session_id
,
293 enum RTSPLowerTransport rtp_protocol
);
294 static int rtp_new_av_stream(HTTPContext
*c
,
295 int stream_index
, struct sockaddr_in
*dest_addr
,
296 HTTPContext
*rtsp_c
);
298 static const char *my_program_name
;
299 static const char *my_program_dir
;
301 static const char *config_filename
= "/etc/ffserver.conf";
303 static int ffserver_debug
;
304 static int ffserver_daemon
;
305 static int no_launch
;
306 static int need_to_start_children
;
308 /* maximum number of simultaneous HTTP connections */
309 static unsigned int nb_max_http_connections
= 2000;
310 static unsigned int nb_max_connections
= 5;
311 static unsigned int nb_connections
;
313 static uint64_t max_bandwidth
= 1000;
314 static uint64_t current_bandwidth
;
316 static int64_t cur_time
; // Making this global saves on passing it around everywhere
318 static AVLFG random_state
;
320 static FILE *logfile
= NULL
;
322 /* FIXME: make ffserver work with IPv6 */
323 /* resolve host with also IP address parsing */
324 static int resolve_host(struct in_addr
*sin_addr
, const char *hostname
)
327 if (!ff_inet_aton(hostname
, sin_addr
)) {
329 struct addrinfo
*ai
, *cur
;
330 struct addrinfo hints
;
331 memset(&hints
, 0, sizeof(hints
));
332 hints
.ai_family
= AF_INET
;
333 if (getaddrinfo(hostname
, NULL
, &hints
, &ai
))
335 /* getaddrinfo returns a linked list of addrinfo structs.
336 * Even if we set ai_family = AF_INET above, make sure
337 * that the returned one actually is of the correct type. */
338 for (cur
= ai
; cur
; cur
= cur
->ai_next
) {
339 if (cur
->ai_family
== AF_INET
) {
340 *sin_addr
= ((struct sockaddr_in
*)cur
->ai_addr
)->sin_addr
;
349 hp
= gethostbyname(hostname
);
352 memcpy(sin_addr
, hp
->h_addr_list
[0], sizeof(struct in_addr
));
358 static char *ctime1(char *buf2
)
366 p
= buf2
+ strlen(p
) - 1;
372 static void http_vlog(const char *fmt
, va_list vargs
)
374 static int print_prefix
= 1;
379 fprintf(logfile
, "%s ", buf
);
381 print_prefix
= strstr(fmt
, "\n") != NULL
;
382 vfprintf(logfile
, fmt
, vargs
);
387 static void __attribute__ ((format (printf
, 1, 2))) http_log(const char *fmt
, ...)
390 va_start(vargs
, fmt
);
391 http_vlog(fmt
, vargs
);
395 static void http_av_log(void *ptr
, int level
, const char *fmt
, va_list vargs
)
397 static int print_prefix
= 1;
398 AVClass
*avc
= ptr ?
*(AVClass
**)ptr
: NULL
;
399 if (level
> av_log_get_level())
401 if (print_prefix
&& avc
)
402 http_log("[%s @ %p]", avc
->item_name(ptr
), ptr
);
403 print_prefix
= strstr(fmt
, "\n") != NULL
;
404 http_vlog(fmt
, vargs
);
407 static void log_connection(HTTPContext
*c
)
412 http_log("%s - - [%s] \"%s %s\" %d %"PRId64
"\n",
413 inet_ntoa(c
->from_addr
.sin_addr
), c
->method
, c
->url
,
414 c
->protocol
, (c
->http_error ? c
->http_error
: 200), c
->data_count
);
417 static void update_datarate(DataRateData
*drd
, int64_t count
)
419 if (!drd
->time1
&& !drd
->count1
) {
420 drd
->time1
= drd
->time2
= cur_time
;
421 drd
->count1
= drd
->count2
= count
;
422 } else if (cur_time
- drd
->time2
> 5000) {
423 drd
->time1
= drd
->time2
;
424 drd
->count1
= drd
->count2
;
425 drd
->time2
= cur_time
;
430 /* In bytes per second */
431 static int compute_datarate(DataRateData
*drd
, int64_t count
)
433 if (cur_time
== drd
->time1
)
436 return ((count
- drd
->count1
) * 1000) / (cur_time
- drd
->time1
);
440 static void start_children(FFStream
*feed
)
445 for (; feed
; feed
= feed
->next
) {
446 if (feed
->child_argv
&& !feed
->pid
) {
447 feed
->pid_start
= time(0);
452 http_log("Unable to create children\n");
461 av_strlcpy(pathname
, my_program_name
, sizeof(pathname
));
463 slash
= strrchr(pathname
, '/');
468 strcpy(slash
, "ffmpeg");
470 http_log("Launch commandline: ");
471 http_log("%s ", pathname
);
472 for (i
= 1; feed
->child_argv
[i
] && feed
->child_argv
[i
][0]; i
++)
473 http_log("%s ", feed
->child_argv
[i
]);
476 for (i
= 3; i
< 256; i
++)
479 if (!ffserver_debug
) {
480 i
= open("/dev/null", O_RDWR
);
489 /* This is needed to make relative pathnames work */
490 chdir(my_program_dir
);
492 signal(SIGPIPE
, SIG_DFL
);
494 execvp(pathname
, feed
->child_argv
);
502 /* open a listening socket */
503 static int socket_open_listen(struct sockaddr_in
*my_addr
)
507 server_fd
= socket(AF_INET
,SOCK_STREAM
,0);
514 setsockopt(server_fd
, SOL_SOCKET
, SO_REUSEADDR
, &tmp
, sizeof(tmp
));
516 if (bind (server_fd
, (struct sockaddr
*) my_addr
, sizeof (*my_addr
)) < 0) {
518 snprintf(bindmsg
, sizeof(bindmsg
), "bind(port %d)", ntohs(my_addr
->sin_port
));
520 closesocket(server_fd
);
524 if (listen (server_fd
, 5) < 0) {
526 closesocket(server_fd
);
529 ff_socket_nonblock(server_fd
, 1);
534 /* start all multicast streams */
535 static void start_multicast(void)
540 struct sockaddr_in dest_addr
;
541 int default_port
, stream_index
;
544 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
545 if (stream
->is_multicast
) {
546 /* open the RTP connection */
547 snprintf(session_id
, sizeof(session_id
), "%08x%08x",
548 av_lfg_get(&random_state
), av_lfg_get(&random_state
));
550 /* choose a port if none given */
551 if (stream
->multicast_port
== 0) {
552 stream
->multicast_port
= default_port
;
556 dest_addr
.sin_family
= AF_INET
;
557 dest_addr
.sin_addr
= stream
->multicast_ip
;
558 dest_addr
.sin_port
= htons(stream
->multicast_port
);
560 rtp_c
= rtp_new_connection(&dest_addr
, stream
, session_id
,
561 RTSP_LOWER_TRANSPORT_UDP_MULTICAST
);
565 if (open_input_stream(rtp_c
, "") < 0) {
566 http_log("Could not open input stream for stream '%s'\n",
571 /* open each RTP stream */
572 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
574 dest_addr
.sin_port
= htons(stream
->multicast_port
+
576 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, NULL
) < 0) {
577 http_log("Could not open output stream '%s/streamid=%d'\n",
578 stream
->filename
, stream_index
);
583 /* change state to send data */
584 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
589 /* main loop of the http server */
590 static int http_server(void)
592 int server_fd
= 0, rtsp_server_fd
= 0;
593 int ret
, delay
, delay1
;
594 struct pollfd
*poll_table
, *poll_entry
;
595 HTTPContext
*c
, *c_next
;
597 if(!(poll_table
= av_mallocz((nb_max_http_connections
+ 2)*sizeof(*poll_table
)))) {
598 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections
);
602 if (my_http_addr
.sin_port
) {
603 server_fd
= socket_open_listen(&my_http_addr
);
608 if (my_rtsp_addr
.sin_port
) {
609 rtsp_server_fd
= socket_open_listen(&my_rtsp_addr
);
610 if (rtsp_server_fd
< 0)
614 if (!rtsp_server_fd
&& !server_fd
) {
615 http_log("HTTP and RTSP disabled.\n");
619 http_log("FFserver started.\n");
621 start_children(first_feed
);
626 poll_entry
= poll_table
;
628 poll_entry
->fd
= server_fd
;
629 poll_entry
->events
= POLLIN
;
632 if (rtsp_server_fd
) {
633 poll_entry
->fd
= rtsp_server_fd
;
634 poll_entry
->events
= POLLIN
;
638 /* wait for events on each HTTP handle */
645 case HTTPSTATE_SEND_HEADER
:
646 case RTSPSTATE_SEND_REPLY
:
647 case RTSPSTATE_SEND_PACKET
:
648 c
->poll_entry
= poll_entry
;
650 poll_entry
->events
= POLLOUT
;
653 case HTTPSTATE_SEND_DATA_HEADER
:
654 case HTTPSTATE_SEND_DATA
:
655 case HTTPSTATE_SEND_DATA_TRAILER
:
656 if (!c
->is_packetized
) {
657 /* for TCP, we output as much as we can (may need to put a limit) */
658 c
->poll_entry
= poll_entry
;
660 poll_entry
->events
= POLLOUT
;
663 /* when ffserver is doing the timing, we work by
664 looking at which packet need to be sent every
666 delay1
= 10; /* one tick wait XXX: 10 ms assumed */
671 case HTTPSTATE_WAIT_REQUEST
:
672 case HTTPSTATE_RECEIVE_DATA
:
673 case HTTPSTATE_WAIT_FEED
:
674 case RTSPSTATE_WAIT_REQUEST
:
675 /* need to catch errors */
676 c
->poll_entry
= poll_entry
;
678 poll_entry
->events
= POLLIN
;/* Maybe this will work */
682 c
->poll_entry
= NULL
;
688 /* wait for an event on one connection. We poll at least every
689 second to handle timeouts */
691 ret
= poll(poll_table
, poll_entry
- poll_table
, delay
);
692 if (ret
< 0 && ff_neterrno() != FF_NETERROR(EAGAIN
) &&
693 ff_neterrno() != FF_NETERROR(EINTR
))
697 cur_time
= av_gettime() / 1000;
699 if (need_to_start_children
) {
700 need_to_start_children
= 0;
701 start_children(first_feed
);
704 /* now handle the events */
705 for(c
= first_http_ctx
; c
!= NULL
; c
= c_next
) {
707 if (handle_connection(c
) < 0) {
708 /* close and free the connection */
714 poll_entry
= poll_table
;
716 /* new HTTP connection request ? */
717 if (poll_entry
->revents
& POLLIN
)
718 new_connection(server_fd
, 0);
721 if (rtsp_server_fd
) {
722 /* new RTSP connection request ? */
723 if (poll_entry
->revents
& POLLIN
)
724 new_connection(rtsp_server_fd
, 1);
729 /* start waiting for a new HTTP/RTSP request */
730 static void start_wait_request(HTTPContext
*c
, int is_rtsp
)
732 c
->buffer_ptr
= c
->buffer
;
733 c
->buffer_end
= c
->buffer
+ c
->buffer_size
- 1; /* leave room for '\0' */
736 c
->timeout
= cur_time
+ RTSP_REQUEST_TIMEOUT
;
737 c
->state
= RTSPSTATE_WAIT_REQUEST
;
739 c
->timeout
= cur_time
+ HTTP_REQUEST_TIMEOUT
;
740 c
->state
= HTTPSTATE_WAIT_REQUEST
;
744 static void http_send_too_busy_reply(int fd
)
747 int len
= snprintf(buffer
, sizeof(buffer
),
748 "HTTP/1.0 503 Server too busy\r\n"
749 "Content-type: text/html\r\n"
751 "<html><head><title>Too busy</title></head><body>\r\n"
752 "<p>The server is too busy to serve your request at this time.</p>\r\n"
753 "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
754 "</body></html>\r\n",
755 nb_connections
, nb_max_connections
);
756 send(fd
, buffer
, len
, 0);
760 static void new_connection(int server_fd
, int is_rtsp
)
762 struct sockaddr_in from_addr
;
764 HTTPContext
*c
= NULL
;
766 len
= sizeof(from_addr
);
767 fd
= accept(server_fd
, (struct sockaddr
*)&from_addr
,
770 http_log("error during accept %s\n", strerror(errno
));
773 ff_socket_nonblock(fd
, 1);
775 if (nb_connections
>= nb_max_connections
) {
776 http_send_too_busy_reply(fd
);
780 /* add a new connection */
781 c
= av_mallocz(sizeof(HTTPContext
));
786 c
->poll_entry
= NULL
;
787 c
->from_addr
= from_addr
;
788 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
789 c
->buffer
= av_malloc(c
->buffer_size
);
793 c
->next
= first_http_ctx
;
797 start_wait_request(c
, is_rtsp
);
809 static void close_connection(HTTPContext
*c
)
811 HTTPContext
**cp
, *c1
;
813 AVFormatContext
*ctx
;
817 /* remove connection from list */
818 cp
= &first_http_ctx
;
819 while ((*cp
) != NULL
) {
827 /* remove references, if any (XXX: do it faster) */
828 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
833 /* remove connection associated resources */
837 /* close each frame parser */
838 for(i
=0;i
<c
->fmt_in
->nb_streams
;i
++) {
839 st
= c
->fmt_in
->streams
[i
];
840 if (st
->codec
->codec
)
841 avcodec_close(st
->codec
);
843 av_close_input_file(c
->fmt_in
);
846 /* free RTP output streams if any */
849 nb_streams
= c
->stream
->nb_streams
;
851 for(i
=0;i
<nb_streams
;i
++) {
854 av_write_trailer(ctx
);
855 av_metadata_free(&ctx
->metadata
);
856 av_free(ctx
->streams
[0]);
859 h
= c
->rtp_handles
[i
];
866 if (!c
->last_packet_sent
&& c
->state
== HTTPSTATE_SEND_DATA_TRAILER
) {
869 if (url_open_dyn_buf(&ctx
->pb
) >= 0) {
870 av_write_trailer(ctx
);
871 av_freep(&c
->pb_buffer
);
872 url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
877 for(i
=0; i
<ctx
->nb_streams
; i
++)
878 av_free(ctx
->streams
[i
]);
880 if (c
->stream
&& !c
->post
&& c
->stream
->stream_type
== STREAM_TYPE_LIVE
)
881 current_bandwidth
-= c
->stream
->bandwidth
;
883 /* signal that there is no feed if we are the feeder socket */
884 if (c
->state
== HTTPSTATE_RECEIVE_DATA
&& c
->stream
) {
885 c
->stream
->feed_opened
= 0;
889 av_freep(&c
->pb_buffer
);
890 av_freep(&c
->packet_buffer
);
896 static int handle_connection(HTTPContext
*c
)
901 case HTTPSTATE_WAIT_REQUEST
:
902 case RTSPSTATE_WAIT_REQUEST
:
904 if ((c
->timeout
- cur_time
) < 0)
906 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
909 /* no need to read if no events */
910 if (!(c
->poll_entry
->revents
& POLLIN
))
914 len
= recv(c
->fd
, c
->buffer_ptr
, 1, 0);
916 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
917 ff_neterrno() != FF_NETERROR(EINTR
))
919 } else if (len
== 0) {
922 /* search for end of request. */
924 c
->buffer_ptr
+= len
;
926 if ((ptr
>= c
->buffer
+ 2 && !memcmp(ptr
-2, "\n\n", 2)) ||
927 (ptr
>= c
->buffer
+ 4 && !memcmp(ptr
-4, "\r\n\r\n", 4))) {
928 /* request found : parse it and reply */
929 if (c
->state
== HTTPSTATE_WAIT_REQUEST
) {
930 ret
= http_parse_request(c
);
932 ret
= rtsp_parse_request(c
);
936 } else if (ptr
>= c
->buffer_end
) {
937 /* request too long: cannot do anything */
939 } else goto read_loop
;
943 case HTTPSTATE_SEND_HEADER
:
944 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
947 /* no need to write if no events */
948 if (!(c
->poll_entry
->revents
& POLLOUT
))
950 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
952 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
953 ff_neterrno() != FF_NETERROR(EINTR
)) {
954 /* error : close connection */
955 av_freep(&c
->pb_buffer
);
959 c
->buffer_ptr
+= len
;
961 c
->stream
->bytes_served
+= len
;
962 c
->data_count
+= len
;
963 if (c
->buffer_ptr
>= c
->buffer_end
) {
964 av_freep(&c
->pb_buffer
);
968 /* all the buffer was sent : synchronize to the incoming stream */
969 c
->state
= HTTPSTATE_SEND_DATA_HEADER
;
970 c
->buffer_ptr
= c
->buffer_end
= c
->buffer
;
975 case HTTPSTATE_SEND_DATA
:
976 case HTTPSTATE_SEND_DATA_HEADER
:
977 case HTTPSTATE_SEND_DATA_TRAILER
:
978 /* for packetized output, we consider we can always write (the
979 input streams sets the speed). It may be better to verify
980 that we do not rely too much on the kernel queues */
981 if (!c
->is_packetized
) {
982 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
985 /* no need to read if no events */
986 if (!(c
->poll_entry
->revents
& POLLOUT
))
989 if (http_send_data(c
) < 0)
991 /* close connection if trailer sent */
992 if (c
->state
== HTTPSTATE_SEND_DATA_TRAILER
)
995 case HTTPSTATE_RECEIVE_DATA
:
996 /* no need to read if no events */
997 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
999 if (!(c
->poll_entry
->revents
& POLLIN
))
1001 if (http_receive_data(c
) < 0)
1004 case HTTPSTATE_WAIT_FEED
:
1005 /* no need to read if no events */
1006 if (c
->poll_entry
->revents
& (POLLIN
| POLLERR
| POLLHUP
))
1009 /* nothing to do, we'll be waken up by incoming feed packets */
1012 case RTSPSTATE_SEND_REPLY
:
1013 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
1014 av_freep(&c
->pb_buffer
);
1017 /* no need to write if no events */
1018 if (!(c
->poll_entry
->revents
& POLLOUT
))
1020 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
1022 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
1023 ff_neterrno() != FF_NETERROR(EINTR
)) {
1024 /* error : close connection */
1025 av_freep(&c
->pb_buffer
);
1029 c
->buffer_ptr
+= len
;
1030 c
->data_count
+= len
;
1031 if (c
->buffer_ptr
>= c
->buffer_end
) {
1032 /* all the buffer was sent : wait for a new request */
1033 av_freep(&c
->pb_buffer
);
1034 start_wait_request(c
, 1);
1038 case RTSPSTATE_SEND_PACKET
:
1039 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
1040 av_freep(&c
->packet_buffer
);
1043 /* no need to write if no events */
1044 if (!(c
->poll_entry
->revents
& POLLOUT
))
1046 len
= send(c
->fd
, c
->packet_buffer_ptr
,
1047 c
->packet_buffer_end
- c
->packet_buffer_ptr
, 0);
1049 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
1050 ff_neterrno() != FF_NETERROR(EINTR
)) {
1051 /* error : close connection */
1052 av_freep(&c
->packet_buffer
);
1056 c
->packet_buffer_ptr
+= len
;
1057 if (c
->packet_buffer_ptr
>= c
->packet_buffer_end
) {
1058 /* all the buffer was sent : wait for a new request */
1059 av_freep(&c
->packet_buffer
);
1060 c
->state
= RTSPSTATE_WAIT_REQUEST
;
1064 case HTTPSTATE_READY
:
1073 static int extract_rates(char *rates
, int ratelen
, const char *request
)
1077 for (p
= request
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1078 if (strncasecmp(p
, "Pragma:", 7) == 0) {
1079 const char *q
= p
+ 7;
1081 while (*q
&& *q
!= '\n' && isspace(*q
))
1084 if (strncasecmp(q
, "stream-switch-entry=", 20) == 0) {
1090 memset(rates
, 0xff, ratelen
);
1093 while (*q
&& *q
!= '\n' && *q
!= ':')
1096 if (sscanf(q
, ":%d:%d", &stream_no
, &rate_no
) != 2)
1100 if (stream_no
< ratelen
&& stream_no
>= 0)
1101 rates
[stream_no
] = rate_no
;
1103 while (*q
&& *q
!= '\n' && !isspace(*q
))
1110 p
= strchr(p
, '\n');
1120 static int find_stream_in_feed(FFStream
*feed
, AVCodecContext
*codec
, int bit_rate
)
1123 int best_bitrate
= 100000000;
1126 for (i
= 0; i
< feed
->nb_streams
; i
++) {
1127 AVCodecContext
*feed_codec
= feed
->streams
[i
]->codec
;
1129 if (feed_codec
->codec_id
!= codec
->codec_id
||
1130 feed_codec
->sample_rate
!= codec
->sample_rate
||
1131 feed_codec
->width
!= codec
->width
||
1132 feed_codec
->height
!= codec
->height
)
1135 /* Potential stream */
1137 /* We want the fastest stream less than bit_rate, or the slowest
1138 * faster than bit_rate
1141 if (feed_codec
->bit_rate
<= bit_rate
) {
1142 if (best_bitrate
> bit_rate
|| feed_codec
->bit_rate
> best_bitrate
) {
1143 best_bitrate
= feed_codec
->bit_rate
;
1147 if (feed_codec
->bit_rate
< best_bitrate
) {
1148 best_bitrate
= feed_codec
->bit_rate
;
1157 static int modify_current_stream(HTTPContext
*c
, char *rates
)
1160 FFStream
*req
= c
->stream
;
1161 int action_required
= 0;
1163 /* Not much we can do for a feed */
1167 for (i
= 0; i
< req
->nb_streams
; i
++) {
1168 AVCodecContext
*codec
= req
->streams
[i
]->codec
;
1172 c
->switch_feed_streams
[i
] = req
->feed_streams
[i
];
1175 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 2);
1178 /* Wants off or slow */
1179 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 4);
1181 /* This doesn't work well when it turns off the only stream! */
1182 c
->switch_feed_streams
[i
] = -2;
1183 c
->feed_streams
[i
] = -2;
1188 if (c
->switch_feed_streams
[i
] >= 0 && c
->switch_feed_streams
[i
] != c
->feed_streams
[i
])
1189 action_required
= 1;
1192 return action_required
;
1195 /* XXX: factorize in utils.c ? */
1196 /* XXX: take care with different space meaning */
1197 static void skip_spaces(const char **pp
)
1201 while (*p
== ' ' || *p
== '\t')
1206 static void get_word(char *buf
, int buf_size
, const char **pp
)
1214 while (!isspace(*p
) && *p
!= '\0') {
1215 if ((q
- buf
) < buf_size
- 1)
1224 static void get_arg(char *buf
, int buf_size
, const char **pp
)
1231 while (isspace(*p
)) p
++;
1234 if (*p
== '\"' || *p
== '\'')
1246 if ((q
- buf
) < buf_size
- 1)
1251 if (quote
&& *p
== quote
)
1256 static void parse_acl_row(FFStream
*stream
, FFStream
* feed
, IPAddressACL
*ext_acl
,
1257 const char *p
, const char *filename
, int line_num
)
1263 get_arg(arg
, sizeof(arg
), &p
);
1264 if (strcasecmp(arg
, "allow") == 0)
1265 acl
.action
= IP_ALLOW
;
1266 else if (strcasecmp(arg
, "deny") == 0)
1267 acl
.action
= IP_DENY
;
1269 fprintf(stderr
, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
1270 filename
, line_num
, arg
);
1274 get_arg(arg
, sizeof(arg
), &p
);
1276 if (resolve_host(&acl
.first
, arg
) != 0) {
1277 fprintf(stderr
, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1278 filename
, line_num
, arg
);
1281 acl
.last
= acl
.first
;
1283 get_arg(arg
, sizeof(arg
), &p
);
1286 if (resolve_host(&acl
.last
, arg
) != 0) {
1287 fprintf(stderr
, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
1288 filename
, line_num
, arg
);
1294 IPAddressACL
*nacl
= av_mallocz(sizeof(*nacl
));
1295 IPAddressACL
**naclp
= 0;
1301 naclp
= &stream
->acl
;
1307 fprintf(stderr
, "%s:%d: ACL found not in <stream> or <feed>\n",
1308 filename
, line_num
);
1314 naclp
= &(*naclp
)->next
;
1322 static IPAddressACL
* parse_dynamic_acl(FFStream
*stream
, HTTPContext
*c
)
1327 IPAddressACL
*acl
= NULL
;
1331 f
= fopen(stream
->dynamic_acl
, "r");
1333 perror(stream
->dynamic_acl
);
1337 acl
= av_mallocz(sizeof(IPAddressACL
));
1341 if (fgets(line
, sizeof(line
), f
) == NULL
)
1347 if (*p
== '\0' || *p
== '#')
1349 get_arg(cmd
, sizeof(cmd
), &p
);
1351 if (!strcasecmp(cmd
, "ACL"))
1352 parse_acl_row(NULL
, NULL
, acl
, p
, stream
->dynamic_acl
, line_num
);
1359 static void free_acl_list(IPAddressACL
*in_acl
)
1361 IPAddressACL
*pacl
,*pacl2
;
1371 static int validate_acl_list(IPAddressACL
*in_acl
, HTTPContext
*c
)
1373 enum IPAddressAction last_action
= IP_DENY
;
1375 struct in_addr
*src
= &c
->from_addr
.sin_addr
;
1376 unsigned long src_addr
= src
->s_addr
;
1378 for (acl
= in_acl
; acl
; acl
= acl
->next
) {
1379 if (src_addr
>= acl
->first
.s_addr
&& src_addr
<= acl
->last
.s_addr
)
1380 return (acl
->action
== IP_ALLOW
) ?
1 : 0;
1381 last_action
= acl
->action
;
1384 /* Nothing matched, so return not the last action */
1385 return (last_action
== IP_DENY
) ?
1 : 0;
1388 static int validate_acl(FFStream
*stream
, HTTPContext
*c
)
1394 /* if stream->acl is null validate_acl_list will return 1 */
1395 ret
= validate_acl_list(stream
->acl
, c
);
1397 if (stream
->dynamic_acl
[0]) {
1398 acl
= parse_dynamic_acl(stream
, c
);
1400 ret
= validate_acl_list(acl
, c
);
1408 /* compute the real filename of a file by matching it without its
1409 extensions to all the stream filenames */
1410 static void compute_real_filename(char *filename
, int max_size
)
1417 /* compute filename by matching without the file extensions */
1418 av_strlcpy(file1
, filename
, sizeof(file1
));
1419 p
= strrchr(file1
, '.');
1422 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
1423 av_strlcpy(file2
, stream
->filename
, sizeof(file2
));
1424 p
= strrchr(file2
, '.');
1427 if (!strcmp(file1
, file2
)) {
1428 av_strlcpy(filename
, stream
->filename
, max_size
);
1443 /* parse http request and prepare header */
1444 static int http_parse_request(HTTPContext
*c
)
1447 enum RedirType redir_type
;
1449 char info
[1024], filename
[1024];
1453 const char *mime_type
;
1457 char *useragent
= 0;
1460 get_word(cmd
, sizeof(cmd
), (const char **)&p
);
1461 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
1463 if (!strcmp(cmd
, "GET"))
1465 else if (!strcmp(cmd
, "POST"))
1470 get_word(url
, sizeof(url
), (const char **)&p
);
1471 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
1473 get_word(protocol
, sizeof(protocol
), (const char **)&p
);
1474 if (strcmp(protocol
, "HTTP/1.0") && strcmp(protocol
, "HTTP/1.1"))
1477 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
1480 http_log("%s - - New connection: %s %s\n", inet_ntoa(c
->from_addr
.sin_addr
), cmd
, url
);
1482 /* find the filename and the optional info string in the request */
1483 p
= strchr(url
, '?');
1485 av_strlcpy(info
, p
, sizeof(info
));
1490 av_strlcpy(filename
, url
+ ((*url
== '/') ?
1 : 0), sizeof(filename
)-1);
1492 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1493 if (strncasecmp(p
, "User-Agent:", 11) == 0) {
1495 if (*useragent
&& *useragent
!= '\n' && isspace(*useragent
))
1499 p
= strchr(p
, '\n');
1506 redir_type
= REDIR_NONE
;
1507 if (av_match_ext(filename
, "asx")) {
1508 redir_type
= REDIR_ASX
;
1509 filename
[strlen(filename
)-1] = 'f';
1510 } else if (av_match_ext(filename
, "asf") &&
1511 (!useragent
|| strncasecmp(useragent
, "NSPlayer", 8) != 0)) {
1512 /* if this isn't WMP or lookalike, return the redirector file */
1513 redir_type
= REDIR_ASF
;
1514 } else if (av_match_ext(filename
, "rpm,ram")) {
1515 redir_type
= REDIR_RAM
;
1516 strcpy(filename
+ strlen(filename
)-2, "m");
1517 } else if (av_match_ext(filename
, "rtsp")) {
1518 redir_type
= REDIR_RTSP
;
1519 compute_real_filename(filename
, sizeof(filename
) - 1);
1520 } else if (av_match_ext(filename
, "sdp")) {
1521 redir_type
= REDIR_SDP
;
1522 compute_real_filename(filename
, sizeof(filename
) - 1);
1525 // "redirect" / request to index.html
1526 if (!strlen(filename
))
1527 av_strlcpy(filename
, "index.html", sizeof(filename
) - 1);
1529 stream
= first_stream
;
1530 while (stream
!= NULL
) {
1531 if (!strcmp(stream
->filename
, filename
) && validate_acl(stream
, c
))
1533 stream
= stream
->next
;
1535 if (stream
== NULL
) {
1536 snprintf(msg
, sizeof(msg
), "File '%s' not found", url
);
1537 http_log("File '%s' not found\n", url
);
1542 memcpy(c
->feed_streams
, stream
->feed_streams
, sizeof(c
->feed_streams
));
1543 memset(c
->switch_feed_streams
, -1, sizeof(c
->switch_feed_streams
));
1545 if (stream
->stream_type
== STREAM_TYPE_REDIRECT
) {
1546 c
->http_error
= 301;
1548 q
+= snprintf(q
, c
->buffer_size
,
1549 "HTTP/1.0 301 Moved\r\n"
1551 "Content-type: text/html\r\n"
1553 "<html><head><title>Moved</title></head><body>\r\n"
1554 "You should be <a href=\"%s\">redirected</a>.\r\n"
1555 "</body></html>\r\n", stream
->feed_filename
, stream
->feed_filename
);
1556 /* prepare output buffer */
1557 c
->buffer_ptr
= c
->buffer
;
1559 c
->state
= HTTPSTATE_SEND_HEADER
;
1563 /* If this is WMP, get the rate information */
1564 if (extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1565 if (modify_current_stream(c
, ratebuf
)) {
1566 for (i
= 0; i
< FF_ARRAY_ELEMS(c
->feed_streams
); i
++) {
1567 if (c
->switch_feed_streams
[i
] >= 0)
1568 c
->switch_feed_streams
[i
] = -1;
1573 if (c
->post
== 0 && stream
->stream_type
== STREAM_TYPE_LIVE
)
1574 current_bandwidth
+= stream
->bandwidth
;
1576 /* If already streaming this feed, do not let start another feeder. */
1577 if (stream
->feed_opened
) {
1578 snprintf(msg
, sizeof(msg
), "This feed is already being received.");
1579 http_log("Feed '%s' already being received\n", stream
->feed_filename
);
1583 if (c
->post
== 0 && max_bandwidth
< current_bandwidth
) {
1584 c
->http_error
= 503;
1586 q
+= snprintf(q
, c
->buffer_size
,
1587 "HTTP/1.0 503 Server too busy\r\n"
1588 "Content-type: text/html\r\n"
1590 "<html><head><title>Too busy</title></head><body>\r\n"
1591 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1592 "<p>The bandwidth being served (including your stream) is %"PRIu64
"kbit/sec, "
1593 "and this exceeds the limit of %"PRIu64
"kbit/sec.</p>\r\n"
1594 "</body></html>\r\n", current_bandwidth
, max_bandwidth
);
1595 /* prepare output buffer */
1596 c
->buffer_ptr
= c
->buffer
;
1598 c
->state
= HTTPSTATE_SEND_HEADER
;
1602 if (redir_type
!= REDIR_NONE
) {
1605 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1606 if (strncasecmp(p
, "Host:", 5) == 0) {
1610 p
= strchr(p
, '\n');
1621 while (isspace(*hostinfo
))
1624 eoh
= strchr(hostinfo
, '\n');
1626 if (eoh
[-1] == '\r')
1629 if (eoh
- hostinfo
< sizeof(hostbuf
) - 1) {
1630 memcpy(hostbuf
, hostinfo
, eoh
- hostinfo
);
1631 hostbuf
[eoh
- hostinfo
] = 0;
1633 c
->http_error
= 200;
1635 switch(redir_type
) {
1637 q
+= snprintf(q
, c
->buffer_size
,
1638 "HTTP/1.0 200 ASX Follows\r\n"
1639 "Content-type: video/x-ms-asf\r\n"
1641 "<ASX Version=\"3\">\r\n"
1642 //"<!-- Autogenerated by ffserver -->\r\n"
1643 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1644 "</ASX>\r\n", hostbuf
, filename
, info
);
1647 q
+= snprintf(q
, c
->buffer_size
,
1648 "HTTP/1.0 200 RAM Follows\r\n"
1649 "Content-type: audio/x-pn-realaudio\r\n"
1651 "# Autogenerated by ffserver\r\n"
1652 "http://%s/%s%s\r\n", hostbuf
, filename
, info
);
1655 q
+= snprintf(q
, c
->buffer_size
,
1656 "HTTP/1.0 200 ASF Redirect follows\r\n"
1657 "Content-type: video/x-ms-asf\r\n"
1660 "Ref1=http://%s/%s%s\r\n", hostbuf
, filename
, info
);
1664 char hostname
[256], *p
;
1665 /* extract only hostname */
1666 av_strlcpy(hostname
, hostbuf
, sizeof(hostname
));
1667 p
= strrchr(hostname
, ':');
1670 q
+= snprintf(q
, c
->buffer_size
,
1671 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1672 /* XXX: incorrect mime type ? */
1673 "Content-type: application/x-rtsp\r\n"
1675 "rtsp://%s:%d/%s\r\n", hostname
, ntohs(my_rtsp_addr
.sin_port
), filename
);
1681 int sdp_data_size
, len
;
1682 struct sockaddr_in my_addr
;
1684 q
+= snprintf(q
, c
->buffer_size
,
1685 "HTTP/1.0 200 OK\r\n"
1686 "Content-type: application/sdp\r\n"
1689 len
= sizeof(my_addr
);
1690 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
1692 /* XXX: should use a dynamic buffer */
1693 sdp_data_size
= prepare_sdp_description(stream
,
1696 if (sdp_data_size
> 0) {
1697 memcpy(q
, sdp_data
, sdp_data_size
);
1709 /* prepare output buffer */
1710 c
->buffer_ptr
= c
->buffer
;
1712 c
->state
= HTTPSTATE_SEND_HEADER
;
1718 snprintf(msg
, sizeof(msg
), "ASX/RAM file not handled");
1722 stream
->conns_served
++;
1724 /* XXX: add there authenticate and IP match */
1727 /* if post, it means a feed is being sent */
1728 if (!stream
->is_feed
) {
1729 /* However it might be a status report from WMP! Let us log the
1730 * data as it might come in handy one day. */
1734 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1735 if (strncasecmp(p
, "Pragma: log-line=", 17) == 0) {
1739 if (strncasecmp(p
, "Pragma: client-id=", 18) == 0)
1740 client_id
= strtol(p
+ 18, 0, 10);
1741 p
= strchr(p
, '\n');
1749 char *eol
= strchr(logline
, '\n');
1754 if (eol
[-1] == '\r')
1756 http_log("%.*s\n", (int) (eol
- logline
), logline
);
1757 c
->suppress_log
= 1;
1762 http_log("\nGot request:\n%s\n", c
->buffer
);
1765 if (client_id
&& extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1768 /* Now we have to find the client_id */
1769 for (wmpc
= first_http_ctx
; wmpc
; wmpc
= wmpc
->next
) {
1770 if (wmpc
->wmp_client_id
== client_id
)
1774 if (wmpc
&& modify_current_stream(wmpc
, ratebuf
))
1775 wmpc
->switch_pending
= 1;
1778 snprintf(msg
, sizeof(msg
), "POST command not handled");
1782 if (http_start_receive_data(c
) < 0) {
1783 snprintf(msg
, sizeof(msg
), "could not open feed");
1787 c
->state
= HTTPSTATE_RECEIVE_DATA
;
1792 if (strcmp(stream
->filename
+ strlen(stream
->filename
) - 4, ".asf") == 0)
1793 http_log("\nGot request:\n%s\n", c
->buffer
);
1796 if (c
->stream
->stream_type
== STREAM_TYPE_STATUS
)
1799 /* open input stream */
1800 if (open_input_stream(c
, info
) < 0) {
1801 snprintf(msg
, sizeof(msg
), "Input stream corresponding to '%s' not found", url
);
1805 /* prepare http header */
1807 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "HTTP/1.0 200 OK\r\n");
1808 mime_type
= c
->stream
->fmt
->mime_type
;
1810 mime_type
= "application/x-octet-stream";
1811 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Pragma: no-cache\r\n");
1813 /* for asf, we need extra headers */
1814 if (!strcmp(c
->stream
->fmt
->name
,"asf_stream")) {
1815 /* Need to allocate a client id */
1817 c
->wmp_client_id
= av_lfg_get(&random_state
);
1819 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c
->wmp_client_id
);
1821 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "Content-Type: %s\r\n", mime_type
);
1822 q
+= snprintf(q
, q
- (char *) c
->buffer
+ c
->buffer_size
, "\r\n");
1824 /* prepare output buffer */
1826 c
->buffer_ptr
= c
->buffer
;
1828 c
->state
= HTTPSTATE_SEND_HEADER
;
1831 c
->http_error
= 404;
1833 q
+= snprintf(q
, c
->buffer_size
,
1834 "HTTP/1.0 404 Not Found\r\n"
1835 "Content-type: text/html\r\n"
1838 "<head><title>404 Not Found</title></head>\n"
1841 /* prepare output buffer */
1842 c
->buffer_ptr
= c
->buffer
;
1844 c
->state
= HTTPSTATE_SEND_HEADER
;
1848 c
->http_error
= 200; /* horrible : we use this value to avoid
1849 going to the send data state */
1850 c
->state
= HTTPSTATE_SEND_HEADER
;
1854 static void fmt_bytecount(ByteIOContext
*pb
, int64_t count
)
1856 static const char *suffix
= " kMGTP";
1859 for (s
= suffix
; count
>= 100000 && s
[1]; count
/= 1000, s
++);
1861 url_fprintf(pb
, "%"PRId64
"%c", count
, *s
);
1864 static void compute_status(HTTPContext
*c
)
1873 if (url_open_dyn_buf(&pb
) < 0) {
1874 /* XXX: return an error ? */
1875 c
->buffer_ptr
= c
->buffer
;
1876 c
->buffer_end
= c
->buffer
;
1880 url_fprintf(pb
, "HTTP/1.0 200 OK\r\n");
1881 url_fprintf(pb
, "Content-type: %s\r\n", "text/html");
1882 url_fprintf(pb
, "Pragma: no-cache\r\n");
1883 url_fprintf(pb
, "\r\n");
1885 url_fprintf(pb
, "<html><head><title>%s Status</title>\n", program_name
);
1886 if (c
->stream
->feed_filename
[0])
1887 url_fprintf(pb
, "<link rel=\"shortcut icon\" href=\"%s\">\n", c
->stream
->feed_filename
);
1888 url_fprintf(pb
, "</head>\n<body>");
1889 url_fprintf(pb
, "<h1>%s Status</h1>\n", program_name
);
1891 url_fprintf(pb
, "<h2>Available Streams</h2>\n");
1892 url_fprintf(pb
, "<table cellspacing=0 cellpadding=4>\n");
1893 url_fprintf(pb
, "<tr><th valign=top>Path<th align=left>Served<br>Conns<th><br>bytes<th valign=top>Format<th>Bit rate<br>kbits/s<th align=left>Video<br>kbits/s<th><br>Codec<th align=left>Audio<br>kbits/s<th><br>Codec<th align=left valign=top>Feed\n");
1894 stream
= first_stream
;
1895 while (stream
!= NULL
) {
1896 char sfilename
[1024];
1899 if (stream
->feed
!= stream
) {
1900 av_strlcpy(sfilename
, stream
->filename
, sizeof(sfilename
) - 10);
1901 eosf
= sfilename
+ strlen(sfilename
);
1902 if (eosf
- sfilename
>= 4) {
1903 if (strcmp(eosf
- 4, ".asf") == 0)
1904 strcpy(eosf
- 4, ".asx");
1905 else if (strcmp(eosf
- 3, ".rm") == 0)
1906 strcpy(eosf
- 3, ".ram");
1907 else if (stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp")) {
1908 /* generate a sample RTSP director if
1909 unicast. Generate an SDP redirector if
1911 eosf
= strrchr(sfilename
, '.');
1913 eosf
= sfilename
+ strlen(sfilename
);
1914 if (stream
->is_multicast
)
1915 strcpy(eosf
, ".sdp");
1917 strcpy(eosf
, ".rtsp");
1921 url_fprintf(pb
, "<tr><td><a href=\"/%s\">%s</a> ",
1922 sfilename
, stream
->filename
);
1923 url_fprintf(pb
, "<td align=right> %d <td align=right> ",
1924 stream
->conns_served
);
1925 fmt_bytecount(pb
, stream
->bytes_served
);
1926 switch(stream
->stream_type
) {
1927 case STREAM_TYPE_LIVE
: {
1928 int audio_bit_rate
= 0;
1929 int video_bit_rate
= 0;
1930 const char *audio_codec_name
= "";
1931 const char *video_codec_name
= "";
1932 const char *audio_codec_name_extra
= "";
1933 const char *video_codec_name_extra
= "";
1935 for(i
=0;i
<stream
->nb_streams
;i
++) {
1936 AVStream
*st
= stream
->streams
[i
];
1937 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
1938 switch(st
->codec
->codec_type
) {
1939 case AVMEDIA_TYPE_AUDIO
:
1940 audio_bit_rate
+= st
->codec
->bit_rate
;
1942 if (*audio_codec_name
)
1943 audio_codec_name_extra
= "...";
1944 audio_codec_name
= codec
->name
;
1947 case AVMEDIA_TYPE_VIDEO
:
1948 video_bit_rate
+= st
->codec
->bit_rate
;
1950 if (*video_codec_name
)
1951 video_codec_name_extra
= "...";
1952 video_codec_name
= codec
->name
;
1955 case AVMEDIA_TYPE_DATA
:
1956 video_bit_rate
+= st
->codec
->bit_rate
;
1962 url_fprintf(pb
, "<td align=center> %s <td align=right> %d <td align=right> %d <td> %s %s <td align=right> %d <td> %s %s",
1965 video_bit_rate
/ 1000, video_codec_name
, video_codec_name_extra
,
1966 audio_bit_rate
/ 1000, audio_codec_name
, audio_codec_name_extra
);
1968 url_fprintf(pb
, "<td>%s", stream
->feed
->filename
);
1970 url_fprintf(pb
, "<td>%s", stream
->feed_filename
);
1971 url_fprintf(pb
, "\n");
1975 url_fprintf(pb
, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
1979 stream
= stream
->next
;
1981 url_fprintf(pb
, "</table>\n");
1983 stream
= first_stream
;
1984 while (stream
!= NULL
) {
1985 if (stream
->feed
== stream
) {
1986 url_fprintf(pb
, "<h2>Feed %s</h2>", stream
->filename
);
1988 url_fprintf(pb
, "Running as pid %d.\n", stream
->pid
);
1990 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1995 /* This is somewhat linux specific I guess */
1996 snprintf(ps_cmd
, sizeof(ps_cmd
),
1997 "ps -o \"%%cpu,cputime\" --no-headers %d",
2000 pid_stat
= popen(ps_cmd
, "r");
2005 if (fscanf(pid_stat
, "%10s %64s", cpuperc
,
2007 url_fprintf(pb
, "Currently using %s%% of the cpu. Total time used %s.\n",
2015 url_fprintf(pb
, "<p>");
2017 url_fprintf(pb
, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
2019 for (i
= 0; i
< stream
->nb_streams
; i
++) {
2020 AVStream
*st
= stream
->streams
[i
];
2021 AVCodec
*codec
= avcodec_find_encoder(st
->codec
->codec_id
);
2022 const char *type
= "unknown";
2023 char parameters
[64];
2027 switch(st
->codec
->codec_type
) {
2028 case AVMEDIA_TYPE_AUDIO
:
2030 snprintf(parameters
, sizeof(parameters
), "%d channel(s), %d Hz", st
->codec
->channels
, st
->codec
->sample_rate
);
2032 case AVMEDIA_TYPE_VIDEO
:
2034 snprintf(parameters
, sizeof(parameters
), "%dx%d, q=%d-%d, fps=%d", st
->codec
->width
, st
->codec
->height
,
2035 st
->codec
->qmin
, st
->codec
->qmax
, st
->codec
->time_base
.den
/ st
->codec
->time_base
.num
);
2040 url_fprintf(pb
, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
2041 i
, type
, st
->codec
->bit_rate
/1000, codec ? codec
->name
: "", parameters
);
2043 url_fprintf(pb
, "</table>\n");
2046 stream
= stream
->next
;
2049 /* connection status */
2050 url_fprintf(pb
, "<h2>Connection Status</h2>\n");
2052 url_fprintf(pb
, "Number of connections: %d / %d<br>\n",
2053 nb_connections
, nb_max_connections
);
2055 url_fprintf(pb
, "Bandwidth in use: %"PRIu64
"k / %"PRIu64
"k<br>\n",
2056 current_bandwidth
, max_bandwidth
);
2058 url_fprintf(pb
, "<table>\n");
2059 url_fprintf(pb
, "<tr><th>#<th>File<th>IP<th>Proto<th>State<th>Target bits/sec<th>Actual bits/sec<th>Bytes transferred\n");
2060 c1
= first_http_ctx
;
2062 while (c1
!= NULL
) {
2068 for (j
= 0; j
< c1
->stream
->nb_streams
; j
++) {
2069 if (!c1
->stream
->feed
)
2070 bitrate
+= c1
->stream
->streams
[j
]->codec
->bit_rate
;
2071 else if (c1
->feed_streams
[j
] >= 0)
2072 bitrate
+= c1
->stream
->feed
->streams
[c1
->feed_streams
[j
]]->codec
->bit_rate
;
2077 p
= inet_ntoa(c1
->from_addr
.sin_addr
);
2078 url_fprintf(pb
, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
2080 c1
->stream ? c1
->stream
->filename
: "",
2081 c1
->state
== HTTPSTATE_RECEIVE_DATA ?
"(input)" : "",
2084 http_state
[c1
->state
]);
2085 fmt_bytecount(pb
, bitrate
);
2086 url_fprintf(pb
, "<td align=right>");
2087 fmt_bytecount(pb
, compute_datarate(&c1
->datarate
, c1
->data_count
) * 8);
2088 url_fprintf(pb
, "<td align=right>");
2089 fmt_bytecount(pb
, c1
->data_count
);
2090 url_fprintf(pb
, "\n");
2093 url_fprintf(pb
, "</table>\n");
2098 url_fprintf(pb
, "<hr size=1 noshade>Generated at %s", p
);
2099 url_fprintf(pb
, "</body>\n</html>\n");
2101 len
= url_close_dyn_buf(pb
, &c
->pb_buffer
);
2102 c
->buffer_ptr
= c
->pb_buffer
;
2103 c
->buffer_end
= c
->pb_buffer
+ len
;
2106 /* check if the parser needs to be opened for stream i */
2107 static void open_parser(AVFormatContext
*s
, int i
)
2109 AVStream
*st
= s
->streams
[i
];
2112 if (!st
->codec
->codec
) {
2113 codec
= avcodec_find_decoder(st
->codec
->codec_id
);
2114 if (codec
&& (codec
->capabilities
& CODEC_CAP_PARSE_ONLY
)) {
2115 st
->codec
->parse_only
= 1;
2116 if (avcodec_open(st
->codec
, codec
) < 0)
2117 st
->codec
->parse_only
= 0;
2122 static int open_input_stream(HTTPContext
*c
, const char *info
)
2125 char input_filename
[1024];
2127 int buf_size
, i
, ret
;
2130 /* find file name */
2131 if (c
->stream
->feed
) {
2132 strcpy(input_filename
, c
->stream
->feed
->feed_filename
);
2133 buf_size
= FFM_PACKET_SIZE
;
2134 /* compute position (absolute time) */
2135 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
2136 stream_pos
= parse_date(buf
, 0);
2137 if (stream_pos
== INT64_MIN
)
2139 } else if (find_info_tag(buf
, sizeof(buf
), "buffer", info
)) {
2140 int prebuffer
= strtol(buf
, 0, 10);
2141 stream_pos
= av_gettime() - prebuffer
* (int64_t)1000000;
2143 stream_pos
= av_gettime() - c
->stream
->prebuffer
* (int64_t)1000;
2145 strcpy(input_filename
, c
->stream
->feed_filename
);
2147 /* compute position (relative time) */
2148 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
2149 stream_pos
= parse_date(buf
, 1);
2150 if (stream_pos
== INT64_MIN
)
2155 if (input_filename
[0] == '\0')
2159 if ((ret
= av_open_input_file(&s
, input_filename
, c
->stream
->ifmt
,
2160 buf_size
, c
->stream
->ap_in
)) < 0) {
2161 http_log("could not open %s: %d\n", input_filename
, ret
);
2164 s
->flags
|= AVFMT_FLAG_GENPTS
;
2166 if (strcmp(s
->iformat
->name
, "ffm") && av_find_stream_info(c
->fmt_in
) < 0) {
2167 http_log("Could not find stream info '%s'\n", input_filename
);
2168 av_close_input_file(s
);
2172 /* open each parser */
2173 for(i
=0;i
<s
->nb_streams
;i
++)
2176 /* choose stream as clock source (we favorize video stream if
2177 present) for packet sending */
2178 c
->pts_stream_index
= 0;
2179 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2180 if (c
->pts_stream_index
== 0 &&
2181 c
->stream
->streams
[i
]->codec
->codec_type
== AVMEDIA_TYPE_VIDEO
) {
2182 c
->pts_stream_index
= i
;
2187 if (c
->fmt_in
->iformat
->read_seek
)
2188 av_seek_frame(c
->fmt_in
, -1, stream_pos
, 0);
2190 /* set the start time (needed for maxtime and RTP packet timing) */
2191 c
->start_time
= cur_time
;
2192 c
->first_pts
= AV_NOPTS_VALUE
;
2196 /* return the server clock (in us) */
2197 static int64_t get_server_clock(HTTPContext
*c
)
2199 /* compute current pts value from system time */
2200 return (cur_time
- c
->start_time
) * 1000;
2203 /* return the estimated time at which the current packet must be sent
2205 static int64_t get_packet_send_clock(HTTPContext
*c
)
2207 int bytes_left
, bytes_sent
, frame_bytes
;
2209 frame_bytes
= c
->cur_frame_bytes
;
2210 if (frame_bytes
<= 0)
2213 bytes_left
= c
->buffer_end
- c
->buffer_ptr
;
2214 bytes_sent
= frame_bytes
- bytes_left
;
2215 return c
->cur_pts
+ (c
->cur_frame_duration
* bytes_sent
) / frame_bytes
;
2220 static int http_prepare_data(HTTPContext
*c
)
2223 AVFormatContext
*ctx
;
2225 av_freep(&c
->pb_buffer
);
2227 case HTTPSTATE_SEND_DATA_HEADER
:
2228 memset(&c
->fmt_ctx
, 0, sizeof(c
->fmt_ctx
));
2229 av_metadata_set2(&c
->fmt_ctx
.metadata
, "author" , c
->stream
->author
, 0);
2230 av_metadata_set2(&c
->fmt_ctx
.metadata
, "comment" , c
->stream
->comment
, 0);
2231 av_metadata_set2(&c
->fmt_ctx
.metadata
, "copyright", c
->stream
->copyright
, 0);
2232 av_metadata_set2(&c
->fmt_ctx
.metadata
, "title" , c
->stream
->title
, 0);
2234 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2237 st
= av_mallocz(sizeof(AVStream
));
2238 c
->fmt_ctx
.streams
[i
] = st
;
2239 /* if file or feed, then just take streams from FFStream struct */
2240 if (!c
->stream
->feed
||
2241 c
->stream
->feed
== c
->stream
)
2242 src
= c
->stream
->streams
[i
];
2244 src
= c
->stream
->feed
->streams
[c
->stream
->feed_streams
[i
]];
2248 st
->codec
->frame_number
= 0; /* XXX: should be done in
2249 AVStream, not in codec */
2251 /* set output format parameters */
2252 c
->fmt_ctx
.oformat
= c
->stream
->fmt
;
2253 c
->fmt_ctx
.nb_streams
= c
->stream
->nb_streams
;
2255 c
->got_key_frame
= 0;
2257 /* prepare header and save header data in a stream */
2258 if (url_open_dyn_buf(&c
->fmt_ctx
.pb
) < 0) {
2259 /* XXX: potential leak */
2262 c
->fmt_ctx
.pb
->is_streamed
= 1;
2265 * HACK to avoid mpeg ps muxer to spit many underflow errors
2266 * Default value from FFmpeg
2267 * Try to set it use configuration option
2269 c
->fmt_ctx
.preload
= (int)(0.5*AV_TIME_BASE
);
2270 c
->fmt_ctx
.max_delay
= (int)(0.7*AV_TIME_BASE
);
2272 av_set_parameters(&c
->fmt_ctx
, NULL
);
2273 if (av_write_header(&c
->fmt_ctx
) < 0) {
2274 http_log("Error writing output header\n");
2277 av_metadata_free(&c
->fmt_ctx
.metadata
);
2279 len
= url_close_dyn_buf(c
->fmt_ctx
.pb
, &c
->pb_buffer
);
2280 c
->buffer_ptr
= c
->pb_buffer
;
2281 c
->buffer_end
= c
->pb_buffer
+ len
;
2283 c
->state
= HTTPSTATE_SEND_DATA
;
2284 c
->last_packet_sent
= 0;
2286 case HTTPSTATE_SEND_DATA
:
2287 /* find a new packet */
2288 /* read a packet from the input stream */
2289 if (c
->stream
->feed
)
2290 ffm_set_write_index(c
->fmt_in
,
2291 c
->stream
->feed
->feed_write_index
,
2292 c
->stream
->feed
->feed_size
);
2294 if (c
->stream
->max_time
&&
2295 c
->stream
->max_time
+ c
->start_time
- cur_time
< 0)
2296 /* We have timed out */
2297 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2301 ret
= av_read_frame(c
->fmt_in
, &pkt
);
2303 if (c
->stream
->feed
) {
2304 /* if coming from feed, it means we reached the end of the
2305 ffm file, so must wait for more data */
2306 c
->state
= HTTPSTATE_WAIT_FEED
;
2307 return 1; /* state changed */
2308 } else if (ret
== AVERROR(EAGAIN
)) {
2309 /* input not ready, come back later */
2312 if (c
->stream
->loop
) {
2313 av_close_input_file(c
->fmt_in
);
2315 if (open_input_stream(c
, "") < 0)
2320 /* must send trailer now because eof or error */
2321 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2325 int source_index
= pkt
.stream_index
;
2326 /* update first pts if needed */
2327 if (c
->first_pts
== AV_NOPTS_VALUE
) {
2328 c
->first_pts
= av_rescale_q(pkt
.dts
, c
->fmt_in
->streams
[pkt
.stream_index
]->time_base
, AV_TIME_BASE_Q
);
2329 c
->start_time
= cur_time
;
2331 /* send it to the appropriate stream */
2332 if (c
->stream
->feed
) {
2333 /* if coming from a feed, select the right stream */
2334 if (c
->switch_pending
) {
2335 c
->switch_pending
= 0;
2336 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2337 if (c
->switch_feed_streams
[i
] == pkt
.stream_index
)
2338 if (pkt
.flags
& AV_PKT_FLAG_KEY
)
2339 c
->switch_feed_streams
[i
] = -1;
2340 if (c
->switch_feed_streams
[i
] >= 0)
2341 c
->switch_pending
= 1;
2344 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2345 if (c
->stream
->feed_streams
[i
] == pkt
.stream_index
) {
2346 AVStream
*st
= c
->fmt_in
->streams
[source_index
];
2347 pkt
.stream_index
= i
;
2348 if (pkt
.flags
& AV_PKT_FLAG_KEY
&&
2349 (st
->codec
->codec_type
== AVMEDIA_TYPE_VIDEO
||
2350 c
->stream
->nb_streams
== 1))
2351 c
->got_key_frame
= 1;
2352 if (!c
->stream
->send_on_key
|| c
->got_key_frame
)
2357 AVCodecContext
*codec
;
2358 AVStream
*ist
, *ost
;
2360 ist
= c
->fmt_in
->streams
[source_index
];
2361 /* specific handling for RTP: we use several
2362 output stream (one for each RTP
2363 connection). XXX: need more abstract handling */
2364 if (c
->is_packetized
) {
2365 /* compute send time and duration */
2366 c
->cur_pts
= av_rescale_q(pkt
.dts
, ist
->time_base
, AV_TIME_BASE_Q
);
2367 c
->cur_pts
-= c
->first_pts
;
2368 c
->cur_frame_duration
= av_rescale_q(pkt
.duration
, ist
->time_base
, AV_TIME_BASE_Q
);
2369 /* find RTP context */
2370 c
->packet_stream_index
= pkt
.stream_index
;
2371 ctx
= c
->rtp_ctx
[c
->packet_stream_index
];
2373 av_free_packet(&pkt
);
2376 codec
= ctx
->streams
[0]->codec
;
2377 /* only one stream per RTP connection */
2378 pkt
.stream_index
= 0;
2382 codec
= ctx
->streams
[pkt
.stream_index
]->codec
;
2385 if (c
->is_packetized
) {
2386 int max_packet_size
;
2387 if (c
->rtp_protocol
== RTSP_LOWER_TRANSPORT_TCP
)
2388 max_packet_size
= RTSP_TCP_MAX_PACKET_SIZE
;
2390 max_packet_size
= url_get_max_packet_size(c
->rtp_handles
[c
->packet_stream_index
]);
2391 ret
= url_open_dyn_packet_buf(&ctx
->pb
, max_packet_size
);
2393 ret
= url_open_dyn_buf(&ctx
->pb
);
2396 /* XXX: potential leak */
2399 ost
= ctx
->streams
[pkt
.stream_index
];
2401 ctx
->pb
->is_streamed
= 1;
2402 if (pkt
.dts
!= AV_NOPTS_VALUE
)
2403 pkt
.dts
= av_rescale_q(pkt
.dts
, ist
->time_base
, ost
->time_base
);
2404 if (pkt
.pts
!= AV_NOPTS_VALUE
)
2405 pkt
.pts
= av_rescale_q(pkt
.pts
, ist
->time_base
, ost
->time_base
);
2406 pkt
.duration
= av_rescale_q(pkt
.duration
, ist
->time_base
, ost
->time_base
);
2407 if (av_write_frame(ctx
, &pkt
) < 0) {
2408 http_log("Error writing frame to output\n");
2409 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2412 len
= url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
2413 c
->cur_frame_bytes
= len
;
2414 c
->buffer_ptr
= c
->pb_buffer
;
2415 c
->buffer_end
= c
->pb_buffer
+ len
;
2417 codec
->frame_number
++;
2419 av_free_packet(&pkt
);
2423 av_free_packet(&pkt
);
2428 case HTTPSTATE_SEND_DATA_TRAILER
:
2429 /* last packet test ? */
2430 if (c
->last_packet_sent
|| c
->is_packetized
)
2433 /* prepare header */
2434 if (url_open_dyn_buf(&ctx
->pb
) < 0) {
2435 /* XXX: potential leak */
2438 c
->fmt_ctx
.pb
->is_streamed
= 1;
2439 av_write_trailer(ctx
);
2440 len
= url_close_dyn_buf(ctx
->pb
, &c
->pb_buffer
);
2441 c
->buffer_ptr
= c
->pb_buffer
;
2442 c
->buffer_end
= c
->pb_buffer
+ len
;
2444 c
->last_packet_sent
= 1;
2450 /* should convert the format at the same time */
2451 /* send data starting at c->buffer_ptr to the output connection
2452 (either UDP or TCP connection) */
2453 static int http_send_data(HTTPContext
*c
)
2458 if (c
->buffer_ptr
>= c
->buffer_end
) {
2459 ret
= http_prepare_data(c
);
2463 /* state change requested */
2466 if (c
->is_packetized
) {
2467 /* RTP data output */
2468 len
= c
->buffer_end
- c
->buffer_ptr
;
2470 /* fail safe - should never happen */
2472 c
->buffer_ptr
= c
->buffer_end
;
2475 len
= (c
->buffer_ptr
[0] << 24) |
2476 (c
->buffer_ptr
[1] << 16) |
2477 (c
->buffer_ptr
[2] << 8) |
2479 if (len
> (c
->buffer_end
- c
->buffer_ptr
))
2481 if ((get_packet_send_clock(c
) - get_server_clock(c
)) > 0) {
2482 /* nothing to send yet: we can wait */
2486 c
->data_count
+= len
;
2487 update_datarate(&c
->datarate
, c
->data_count
);
2489 c
->stream
->bytes_served
+= len
;
2491 if (c
->rtp_protocol
== RTSP_LOWER_TRANSPORT_TCP
) {
2492 /* RTP packets are sent inside the RTSP TCP connection */
2494 int interleaved_index
, size
;
2496 HTTPContext
*rtsp_c
;
2499 /* if no RTSP connection left, error */
2502 /* if already sending something, then wait. */
2503 if (rtsp_c
->state
!= RTSPSTATE_WAIT_REQUEST
)
2505 if (url_open_dyn_buf(&pb
) < 0)
2507 interleaved_index
= c
->packet_stream_index
* 2;
2508 /* RTCP packets are sent at odd indexes */
2509 if (c
->buffer_ptr
[1] == 200)
2510 interleaved_index
++;
2511 /* write RTSP TCP header */
2513 header
[1] = interleaved_index
;
2514 header
[2] = len
>> 8;
2516 put_buffer(pb
, header
, 4);
2517 /* write RTP packet data */
2519 put_buffer(pb
, c
->buffer_ptr
, len
);
2520 size
= url_close_dyn_buf(pb
, &c
->packet_buffer
);
2521 /* prepare asynchronous TCP sending */
2522 rtsp_c
->packet_buffer_ptr
= c
->packet_buffer
;
2523 rtsp_c
->packet_buffer_end
= c
->packet_buffer
+ size
;
2524 c
->buffer_ptr
+= len
;
2526 /* send everything we can NOW */
2527 len
= send(rtsp_c
->fd
, rtsp_c
->packet_buffer_ptr
,
2528 rtsp_c
->packet_buffer_end
- rtsp_c
->packet_buffer_ptr
, 0);
2530 rtsp_c
->packet_buffer_ptr
+= len
;
2531 if (rtsp_c
->packet_buffer_ptr
< rtsp_c
->packet_buffer_end
) {
2532 /* if we could not send all the data, we will
2533 send it later, so a new state is needed to
2534 "lock" the RTSP TCP connection */
2535 rtsp_c
->state
= RTSPSTATE_SEND_PACKET
;
2538 /* all data has been sent */
2539 av_freep(&c
->packet_buffer
);
2541 /* send RTP packet directly in UDP */
2543 url_write(c
->rtp_handles
[c
->packet_stream_index
],
2544 c
->buffer_ptr
, len
);
2545 c
->buffer_ptr
+= len
;
2546 /* here we continue as we can send several packets per 10 ms slot */
2549 /* TCP data output */
2550 len
= send(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
, 0);
2552 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2553 ff_neterrno() != FF_NETERROR(EINTR
))
2554 /* error : close connection */
2559 c
->buffer_ptr
+= len
;
2561 c
->data_count
+= len
;
2562 update_datarate(&c
->datarate
, c
->data_count
);
2564 c
->stream
->bytes_served
+= len
;
2572 static int http_start_receive_data(HTTPContext
*c
)
2576 if (c
->stream
->feed_opened
)
2579 /* Don't permit writing to this one */
2580 if (c
->stream
->readonly
)
2584 fd
= open(c
->stream
->feed_filename
, O_RDWR
);
2586 http_log("Error opening feeder file: %s\n", strerror(errno
));
2591 if (c
->stream
->truncate
) {
2592 /* truncate feed file */
2593 ffm_write_write_index(c
->feed_fd
, FFM_PACKET_SIZE
);
2594 ftruncate(c
->feed_fd
, FFM_PACKET_SIZE
);
2595 http_log("Truncating feed file '%s'\n", c
->stream
->feed_filename
);
2597 if ((c
->stream
->feed_write_index
= ffm_read_write_index(fd
)) < 0) {
2598 http_log("Error reading write index from feed file: %s\n", strerror(errno
));
2603 c
->stream
->feed_write_index
= FFMAX(ffm_read_write_index(fd
), FFM_PACKET_SIZE
);
2604 c
->stream
->feed_size
= lseek(fd
, 0, SEEK_END
);
2605 lseek(fd
, 0, SEEK_SET
);
2607 /* init buffer input */
2608 c
->buffer_ptr
= c
->buffer
;
2609 c
->buffer_end
= c
->buffer
+ FFM_PACKET_SIZE
;
2610 c
->stream
->feed_opened
= 1;
2611 c
->chunked_encoding
= !!av_stristr(c
->buffer
, "Transfer-Encoding: chunked");
2615 static int http_receive_data(HTTPContext
*c
)
2618 int len
, loop_run
= 0;
2620 while (c
->chunked_encoding
&& !c
->chunk_size
&&
2621 c
->buffer_end
> c
->buffer_ptr
) {
2622 /* read chunk header, if present */
2623 len
= recv(c
->fd
, c
->buffer_ptr
, 1, 0);
2626 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2627 ff_neterrno() != FF_NETERROR(EINTR
))
2628 /* error : close connection */
2631 } else if (len
== 0) {
2632 /* end of connection : close it */
2634 } else if (c
->buffer_ptr
- c
->buffer
>= 2 &&
2635 !memcmp(c
->buffer_ptr
- 1, "\r\n", 2)) {
2636 c
->chunk_size
= strtol(c
->buffer
, 0, 16);
2637 if (c
->chunk_size
== 0) // end of stream
2639 c
->buffer_ptr
= c
->buffer
;
2641 } else if (++loop_run
> 10) {
2642 /* no chunk header, abort */
2649 if (c
->buffer_end
> c
->buffer_ptr
) {
2650 len
= recv(c
->fd
, c
->buffer_ptr
,
2651 FFMIN(c
->chunk_size
, c
->buffer_end
- c
->buffer_ptr
), 0);
2653 if (ff_neterrno() != FF_NETERROR(EAGAIN
) &&
2654 ff_neterrno() != FF_NETERROR(EINTR
))
2655 /* error : close connection */
2657 } else if (len
== 0)
2658 /* end of connection : close it */
2661 c
->chunk_size
-= len
;
2662 c
->buffer_ptr
+= len
;
2663 c
->data_count
+= len
;
2664 update_datarate(&c
->datarate
, c
->data_count
);
2668 if (c
->buffer_ptr
- c
->buffer
>= 2 && c
->data_count
> FFM_PACKET_SIZE
) {
2669 if (c
->buffer
[0] != 'f' ||
2670 c
->buffer
[1] != 'm') {
2671 http_log("Feed stream has become desynchronized -- disconnecting\n");
2676 if (c
->buffer_ptr
>= c
->buffer_end
) {
2677 FFStream
*feed
= c
->stream
;
2678 /* a packet has been received : write it in the store, except
2680 if (c
->data_count
> FFM_PACKET_SIZE
) {
2682 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2683 /* XXX: use llseek or url_seek */
2684 lseek(c
->feed_fd
, feed
->feed_write_index
, SEEK_SET
);
2685 if (write(c
->feed_fd
, c
->buffer
, FFM_PACKET_SIZE
) < 0) {
2686 http_log("Error writing to feed file: %s\n", strerror(errno
));
2690 feed
->feed_write_index
+= FFM_PACKET_SIZE
;
2691 /* update file size */
2692 if (feed
->feed_write_index
> c
->stream
->feed_size
)
2693 feed
->feed_size
= feed
->feed_write_index
;
2695 /* handle wrap around if max file size reached */
2696 if (c
->stream
->feed_max_size
&& feed
->feed_write_index
>= c
->stream
->feed_max_size
)
2697 feed
->feed_write_index
= FFM_PACKET_SIZE
;
2700 if (ffm_write_write_index(c
->feed_fd
, feed
->feed_write_index
) < 0) {
2701 http_log("Error writing index to feed file: %s\n", strerror(errno
));
2705 /* wake up any waiting connections */
2706 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2707 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2708 c1
->stream
->feed
== c
->stream
->feed
)
2709 c1
->state
= HTTPSTATE_SEND_DATA
;
2712 /* We have a header in our hands that contains useful data */
2713 AVFormatContext
*s
= NULL
;
2715 AVInputFormat
*fmt_in
;
2718 /* use feed output format name to find corresponding input format */
2719 fmt_in
= av_find_input_format(feed
->fmt
->name
);
2723 url_open_buf(&pb
, c
->buffer
, c
->buffer_end
- c
->buffer
, URL_RDONLY
);
2724 pb
->is_streamed
= 1;
2726 if (av_open_input_stream(&s
, pb
, c
->stream
->feed_filename
, fmt_in
, NULL
) < 0) {
2731 /* Now we have the actual streams */
2732 if (s
->nb_streams
!= feed
->nb_streams
) {
2733 av_close_input_stream(s
);
2735 http_log("Feed '%s' stream number does not match registered feed\n",
2736 c
->stream
->feed_filename
);
2740 for (i
= 0; i
< s
->nb_streams
; i
++) {
2741 AVStream
*fst
= feed
->streams
[i
];
2742 AVStream
*st
= s
->streams
[i
];
2743 avcodec_copy_context(fst
->codec
, st
->codec
);
2746 av_close_input_stream(s
);
2749 c
->buffer_ptr
= c
->buffer
;
2754 c
->stream
->feed_opened
= 0;
2756 /* wake up any waiting connections to stop waiting for feed */
2757 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2758 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2759 c1
->stream
->feed
== c
->stream
->feed
)
2760 c1
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2765 /********************************************************************/
2768 static void rtsp_reply_header(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2775 switch(error_number
) {
2776 case RTSP_STATUS_OK
:
2779 case RTSP_STATUS_METHOD
:
2780 str
= "Method Not Allowed";
2782 case RTSP_STATUS_BANDWIDTH
:
2783 str
= "Not Enough Bandwidth";
2785 case RTSP_STATUS_SESSION
:
2786 str
= "Session Not Found";
2788 case RTSP_STATUS_STATE
:
2789 str
= "Method Not Valid in This State";
2791 case RTSP_STATUS_AGGREGATE
:
2792 str
= "Aggregate operation not allowed";
2794 case RTSP_STATUS_ONLY_AGGREGATE
:
2795 str
= "Only aggregate operation allowed";
2797 case RTSP_STATUS_TRANSPORT
:
2798 str
= "Unsupported transport";
2800 case RTSP_STATUS_INTERNAL
:
2801 str
= "Internal Server Error";
2803 case RTSP_STATUS_SERVICE
:
2804 str
= "Service Unavailable";
2806 case RTSP_STATUS_VERSION
:
2807 str
= "RTSP Version not supported";
2810 str
= "Unknown Error";
2814 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", error_number
, str
);
2815 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2817 /* output GMT time */
2820 strftime(buf2
, sizeof(buf2
), "%a, %d %b %Y %H:%M:%S", tm
);
2821 url_fprintf(c
->pb
, "Date: %s GMT\r\n", buf2
);
2824 static void rtsp_reply_error(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2826 rtsp_reply_header(c
, error_number
);
2827 url_fprintf(c
->pb
, "\r\n");
2830 static int rtsp_parse_request(HTTPContext
*c
)
2832 const char *p
, *p1
, *p2
;
2838 RTSPMessageHeader header1
, *header
= &header1
;
2840 c
->buffer_ptr
[0] = '\0';
2843 get_word(cmd
, sizeof(cmd
), &p
);
2844 get_word(url
, sizeof(url
), &p
);
2845 get_word(protocol
, sizeof(protocol
), &p
);
2847 av_strlcpy(c
->method
, cmd
, sizeof(c
->method
));
2848 av_strlcpy(c
->url
, url
, sizeof(c
->url
));
2849 av_strlcpy(c
->protocol
, protocol
, sizeof(c
->protocol
));
2851 if (url_open_dyn_buf(&c
->pb
) < 0) {
2852 /* XXX: cannot do more */
2853 c
->pb
= NULL
; /* safety */
2857 /* check version name */
2858 if (strcmp(protocol
, "RTSP/1.0") != 0) {
2859 rtsp_reply_error(c
, RTSP_STATUS_VERSION
);
2863 /* parse each header line */
2864 memset(header
, 0, sizeof(*header
));
2865 /* skip to next line */
2866 while (*p
!= '\n' && *p
!= '\0')
2870 while (*p
!= '\0') {
2871 p1
= memchr(p
, '\n', (char *)c
->buffer_ptr
- p
);
2875 if (p2
> p
&& p2
[-1] == '\r')
2877 /* skip empty line */
2881 if (len
> sizeof(line
) - 1)
2882 len
= sizeof(line
) - 1;
2883 memcpy(line
, p
, len
);
2885 ff_rtsp_parse_line(header
, line
, NULL
, NULL
);
2889 /* handle sequence number */
2890 c
->seq
= header
->seq
;
2892 if (!strcmp(cmd
, "DESCRIBE"))
2893 rtsp_cmd_describe(c
, url
);
2894 else if (!strcmp(cmd
, "OPTIONS"))
2895 rtsp_cmd_options(c
, url
);
2896 else if (!strcmp(cmd
, "SETUP"))
2897 rtsp_cmd_setup(c
, url
, header
);
2898 else if (!strcmp(cmd
, "PLAY"))
2899 rtsp_cmd_play(c
, url
, header
);
2900 else if (!strcmp(cmd
, "PAUSE"))
2901 rtsp_cmd_pause(c
, url
, header
);
2902 else if (!strcmp(cmd
, "TEARDOWN"))
2903 rtsp_cmd_teardown(c
, url
, header
);
2905 rtsp_reply_error(c
, RTSP_STATUS_METHOD
);
2908 len
= url_close_dyn_buf(c
->pb
, &c
->pb_buffer
);
2909 c
->pb
= NULL
; /* safety */
2911 /* XXX: cannot do more */
2914 c
->buffer_ptr
= c
->pb_buffer
;
2915 c
->buffer_end
= c
->pb_buffer
+ len
;
2916 c
->state
= RTSPSTATE_SEND_REPLY
;
2920 static int prepare_sdp_description(FFStream
*stream
, uint8_t **pbuffer
,
2921 struct in_addr my_ip
)
2923 AVFormatContext
*avc
;
2924 AVStream
*avs
= NULL
;
2927 avc
= avformat_alloc_context();
2931 av_metadata_set2(&avc
->metadata
, "title",
2932 stream
->title
[0] ? stream
->title
: "No Title", 0);
2933 avc
->nb_streams
= stream
->nb_streams
;
2934 if (stream
->is_multicast
) {
2935 snprintf(avc
->filename
, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2936 inet_ntoa(stream
->multicast_ip
),
2937 stream
->multicast_port
, stream
->multicast_ttl
);
2939 snprintf(avc
->filename
, 1024, "rtp://0.0.0.0");
2942 #if !FF_API_MAX_STREAMS
2943 if (avc
->nb_streams
>= INT_MAX
/sizeof(*avc
->streams
) ||
2944 !(avc
->streams
= av_malloc(avc
->nb_streams
* sizeof(*avc
->streams
))))
2947 if (avc
->nb_streams
>= INT_MAX
/sizeof(*avs
) ||
2948 !(avs
= av_malloc(avc
->nb_streams
* sizeof(*avs
))))
2951 for(i
= 0; i
< stream
->nb_streams
; i
++) {
2952 avc
->streams
[i
] = &avs
[i
];
2953 avc
->streams
[i
]->codec
= stream
->streams
[i
]->codec
;
2955 *pbuffer
= av_mallocz(2048);
2956 avf_sdp_create(&avc
, 1, *pbuffer
, 2048);
2959 #if !FF_API_MAX_STREAMS
2960 av_free(avc
->streams
);
2962 av_metadata_free(&avc
->metadata
);
2966 return strlen(*pbuffer
);
2969 static void rtsp_cmd_options(HTTPContext
*c
, const char *url
)
2971 // rtsp_reply_header(c, RTSP_STATUS_OK);
2972 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK
, "OK");
2973 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2974 url_fprintf(c
->pb
, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2975 url_fprintf(c
->pb
, "\r\n");
2978 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
)
2984 int content_length
, len
;
2985 struct sockaddr_in my_addr
;
2987 /* find which url is asked */
2988 av_url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2993 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2994 if (!stream
->is_feed
&&
2995 stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp") &&
2996 !strcmp(path
, stream
->filename
)) {
3000 /* no stream found */
3001 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
3005 /* prepare the media description in sdp format */
3007 /* get the host IP */
3008 len
= sizeof(my_addr
);
3009 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
3010 content_length
= prepare_sdp_description(stream
, &content
, my_addr
.sin_addr
);
3011 if (content_length
< 0) {
3012 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
3015 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3016 url_fprintf(c
->pb
, "Content-Base: %s/\r\n", url
);
3017 url_fprintf(c
->pb
, "Content-Type: application/sdp\r\n");
3018 url_fprintf(c
->pb
, "Content-Length: %d\r\n", content_length
);
3019 url_fprintf(c
->pb
, "\r\n");
3020 put_buffer(c
->pb
, content
, content_length
);
3024 static HTTPContext
*find_rtp_session(const char *session_id
)
3028 if (session_id
[0] == '\0')
3031 for(c
= first_http_ctx
; c
!= NULL
; c
= c
->next
) {
3032 if (!strcmp(c
->session_id
, session_id
))
3038 static RTSPTransportField
*find_transport(RTSPMessageHeader
*h
, enum RTSPLowerTransport lower_transport
)
3040 RTSPTransportField
*th
;
3043 for(i
=0;i
<h
->nb_transports
;i
++) {
3044 th
= &h
->transports
[i
];
3045 if (th
->lower_transport
== lower_transport
)
3051 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
,
3052 RTSPMessageHeader
*h
)
3055 int stream_index
, rtp_port
, rtcp_port
;
3060 RTSPTransportField
*th
;
3061 struct sockaddr_in dest_addr
;
3062 RTSPActionServerSetup setup
;
3064 /* find which url is asked */
3065 av_url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
3070 /* now check each stream */
3071 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
3072 if (!stream
->is_feed
&&
3073 stream
->fmt
&& !strcmp(stream
->fmt
->name
, "rtp")) {
3074 /* accept aggregate filenames only if single stream */
3075 if (!strcmp(path
, stream
->filename
)) {
3076 if (stream
->nb_streams
!= 1) {
3077 rtsp_reply_error(c
, RTSP_STATUS_AGGREGATE
);
3084 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
3086 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
3087 stream
->filename
, stream_index
);
3088 if (!strcmp(path
, buf
))
3093 /* no stream found */
3094 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
3098 /* generate session id if needed */
3099 if (h
->session_id
[0] == '\0')
3100 snprintf(h
->session_id
, sizeof(h
->session_id
), "%08x%08x",
3101 av_lfg_get(&random_state
), av_lfg_get(&random_state
));
3103 /* find rtp session, and create it if none found */
3104 rtp_c
= find_rtp_session(h
->session_id
);
3106 /* always prefer UDP */
3107 th
= find_transport(h
, RTSP_LOWER_TRANSPORT_UDP
);
3109 th
= find_transport(h
, RTSP_LOWER_TRANSPORT_TCP
);
3111 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
3116 rtp_c
= rtp_new_connection(&c
->from_addr
, stream
, h
->session_id
,
3117 th
->lower_transport
);
3119 rtsp_reply_error(c
, RTSP_STATUS_BANDWIDTH
);
3123 /* open input stream */
3124 if (open_input_stream(rtp_c
, "") < 0) {
3125 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
3130 /* test if stream is OK (test needed because several SETUP needs
3131 to be done for a given file) */
3132 if (rtp_c
->stream
!= stream
) {
3133 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
);
3137 /* test if stream is already set up */
3138 if (rtp_c
->rtp_ctx
[stream_index
]) {
3139 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3143 /* check transport */
3144 th
= find_transport(h
, rtp_c
->rtp_protocol
);
3145 if (!th
|| (th
->lower_transport
== RTSP_LOWER_TRANSPORT_UDP
&&
3146 th
->client_port_min
<= 0)) {
3147 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
3151 /* setup default options */
3152 setup
.transport_option
[0] = '\0';
3153 dest_addr
= rtp_c
->from_addr
;
3154 dest_addr
.sin_port
= htons(th
->client_port_min
);
3157 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
, c
) < 0) {
3158 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
3162 /* now everything is OK, so we can send the connection parameters */
3163 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3165 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3167 switch(rtp_c
->rtp_protocol
) {
3168 case RTSP_LOWER_TRANSPORT_UDP
:
3169 rtp_port
= rtp_get_local_rtp_port(rtp_c
->rtp_handles
[stream_index
]);
3170 rtcp_port
= rtp_get_local_rtcp_port(rtp_c
->rtp_handles
[stream_index
]);
3171 url_fprintf(c
->pb
, "Transport: RTP/AVP/UDP;unicast;"
3172 "client_port=%d-%d;server_port=%d-%d",
3173 th
->client_port_min
, th
->client_port_max
,
3174 rtp_port
, rtcp_port
);
3176 case RTSP_LOWER_TRANSPORT_TCP
:
3177 url_fprintf(c
->pb
, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
3178 stream_index
* 2, stream_index
* 2 + 1);
3183 if (setup
.transport_option
[0] != '\0')
3184 url_fprintf(c
->pb
, ";%s", setup
.transport_option
);
3185 url_fprintf(c
->pb
, "\r\n");
3188 url_fprintf(c
->pb
, "\r\n");
3192 /* find an rtp connection by using the session ID. Check consistency
3194 static HTTPContext
*find_rtp_session_with_url(const char *url
,
3195 const char *session_id
)
3203 rtp_c
= find_rtp_session(session_id
);
3207 /* find which url is asked */
3208 av_url_split(NULL
, 0, NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
3212 if(!strcmp(path
, rtp_c
->stream
->filename
)) return rtp_c
;
3213 for(s
=0; s
<rtp_c
->stream
->nb_streams
; ++s
) {
3214 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
3215 rtp_c
->stream
->filename
, s
);
3216 if(!strncmp(path
, buf
, sizeof(buf
))) {
3217 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3224 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPMessageHeader
*h
)
3228 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3230 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3234 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
3235 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
&&
3236 rtp_c
->state
!= HTTPSTATE_READY
) {
3237 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
3241 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
3243 /* now everything is OK, so we can send the connection parameters */
3244 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3246 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);