2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #define HAVE_AV_CONFIG_H
23 #include <netinet/in.h>
26 #include <sys/ioctl.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
34 #include <arpa/inet.h>
42 /* maximum number of simultaneous HTTP connections */
43 #define HTTP_MAX_CONNECTIONS 2000
46 HTTPSTATE_WAIT_REQUEST
,
47 HTTPSTATE_SEND_HEADER
,
48 HTTPSTATE_SEND_DATA_HEADER
,
49 HTTPSTATE_SEND_DATA
, /* sending TCP or UDP data */
50 HTTPSTATE_SEND_DATA_TRAILER
,
51 HTTPSTATE_RECEIVE_DATA
,
52 HTTPSTATE_WAIT_FEED
, /* wait for data from the feed */
53 HTTPSTATE_WAIT
, /* wait before sending next packets */
54 HTTPSTATE_WAIT_SHORT
, /* short wait for short term
55 bandwidth limitation */
58 RTSPSTATE_WAIT_REQUEST
,
62 const char *http_state
[] = {
79 #define IOBUFFER_INIT_SIZE 8192
81 /* coef for exponential mean for bitrate estimation in statistics */
84 /* timeouts are in ms */
85 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
86 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
88 #define SYNC_TIMEOUT (10 * 1000)
95 /* context associated with one connection */
96 typedef struct HTTPContext
{
98 int fd
; /* socket file descriptor */
99 struct sockaddr_in from_addr
; /* origin */
100 struct pollfd
*poll_entry
; /* used when polling */
102 UINT8
*buffer_ptr
, *buffer_end
;
104 struct HTTPContext
*next
;
105 int got_key_frame
; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
109 /* input format handling */
110 AVFormatContext
*fmt_in
;
111 long start_time
; /* In milliseconds - this wraps fairly often */
112 INT64 first_pts
; /* initial pts value */
113 int pts_stream_index
; /* stream we choose as clock reference */
114 /* output format handling */
115 struct FFStream
*stream
;
116 /* -1 is invalid stream */
117 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
118 int switch_feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
120 AVFormatContext fmt_ctx
; /* instance of FFStream for one user */
121 int last_packet_sent
; /* true if last data packet was sent */
123 DataRateData datarate
;
130 int is_packetized
; /* if true, the stream is packetized */
131 int packet_stream_index
; /* current stream for output in state machine */
133 /* RTSP state specific */
134 UINT8
*pb_buffer
; /* XXX: use that in all the code */
136 int seq
; /* RTSP sequence number */
138 /* RTP state specific */
139 enum RTSPProtocol rtp_protocol
;
140 char session_id
[32]; /* session id */
141 AVFormatContext
*rtp_ctx
[MAX_STREAMS
];
142 URLContext
*rtp_handles
[MAX_STREAMS
];
143 /* RTP short term bandwidth limitation */
144 int packet_byte_count
;
145 int packet_start_time_us
; /* used for short durations (a few
149 /* each generated stream is described here */
153 STREAM_TYPE_REDIRECT
,
156 enum IPAddressAction
{
161 typedef struct IPAddressACL
{
162 struct IPAddressACL
*next
;
163 enum IPAddressAction action
;
164 struct in_addr first
;
168 /* description of each stream of the ffserver.conf file */
169 typedef struct FFStream
{
170 enum StreamType stream_type
;
171 char filename
[1024]; /* stream filename */
172 struct FFStream
*feed
; /* feed we are using (can be null if
177 int prebuffer
; /* Number of millseconds early to start */
178 long max_time
; /* Number of milliseconds to run */
180 AVStream
*streams
[MAX_STREAMS
];
181 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
182 char feed_filename
[1024]; /* file name of the feed storage, or
183 input file name for a stream */
188 pid_t pid
; /* Of ffmpeg process */
189 time_t pid_start
; /* Of ffmpeg process */
191 struct FFStream
*next
;
192 int bandwidth
; /* bandwidth, in kbits/s */
195 /* multicast specific */
197 struct in_addr multicast_ip
;
198 int multicast_port
; /* first port used for multicast */
200 int loop
; /* if true, send the stream in loops (only meaningful if file) */
203 int feed_opened
; /* true if someone is writing to the feed */
204 int is_feed
; /* true if it is a feed */
207 INT64 feed_max_size
; /* maximum storage size */
208 INT64 feed_write_index
; /* current write position in feed (it wraps round) */
209 INT64 feed_size
; /* current size of feed */
210 struct FFStream
*next_feed
;
213 typedef struct FeedData
{
214 long long data_count
;
215 float avg_frame_size
; /* frame size averraged over last frames with exponential mean */
218 struct sockaddr_in my_http_addr
;
219 struct sockaddr_in my_rtsp_addr
;
221 char logfilename
[1024];
222 HTTPContext
*first_http_ctx
;
223 FFStream
*first_feed
; /* contains only feeds */
224 FFStream
*first_stream
; /* contains all streams, including feeds */
226 static void new_connection(int server_fd
, int is_rtsp
);
227 static void close_connection(HTTPContext
*c
);
230 static int handle_connection(HTTPContext
*c
);
231 static int http_parse_request(HTTPContext
*c
);
232 static int http_send_data(HTTPContext
*c
);
233 static void compute_stats(HTTPContext
*c
);
234 static int open_input_stream(HTTPContext
*c
, const char *info
);
235 static int http_start_receive_data(HTTPContext
*c
);
236 static int http_receive_data(HTTPContext
*c
);
237 static int compute_send_delay(HTTPContext
*c
);
240 static int rtsp_parse_request(HTTPContext
*c
);
241 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
);
242 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
243 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
244 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
245 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
248 static int prepare_sdp_description(FFStream
*stream
, UINT8
**pbuffer
,
249 struct in_addr my_ip
);
252 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
253 FFStream
*stream
, const char *session_id
);
254 static int rtp_new_av_stream(HTTPContext
*c
,
255 int stream_index
, struct sockaddr_in
*dest_addr
);
257 static const char *my_program_name
;
258 static const char *my_program_dir
;
260 static int ffserver_debug
;
261 static int ffserver_daemon
;
262 static int no_launch
;
263 static int need_to_start_children
;
265 int nb_max_connections
;
269 int current_bandwidth
;
271 static long cur_time
; // Making this global saves on passing it around everywhere
273 static long gettime_ms(void)
277 gettimeofday(&tv
,NULL
);
278 return (long long)tv
.tv_sec
* 1000 + (tv
.tv_usec
/ 1000);
281 static FILE *logfile
= NULL
;
283 static void http_log(char *fmt
, ...)
289 vfprintf(logfile
, fmt
, ap
);
295 static char *ctime1(char *buf2
)
303 p
= buf2
+ strlen(p
) - 1;
309 static void log_connection(HTTPContext
*c
)
316 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
317 inet_ntoa(c
->from_addr
.sin_addr
),
318 ctime1(buf2
), c
->method
, c
->url
,
319 c
->protocol
, (c
->http_error ? c
->http_error
: 200), c
->data_count
);
322 static void update_datarate(DataRateData
*drd
, INT64 count
)
324 if (!drd
->time1
&& !drd
->count1
) {
325 drd
->time1
= drd
->time2
= cur_time
;
326 drd
->count1
= drd
->count2
= count
;
328 if (cur_time
- drd
->time2
> 5000) {
329 drd
->time1
= drd
->time2
;
330 drd
->count1
= drd
->count2
;
331 drd
->time2
= cur_time
;
337 /* In bytes per second */
338 static int compute_datarate(DataRateData
*drd
, INT64 count
)
340 if (cur_time
== drd
->time1
)
343 return ((count
- drd
->count1
) * 1000) / (cur_time
- drd
->time1
);
346 static int get_longterm_datarate(DataRateData
*drd
, INT64 count
)
348 /* You get the first 3 seconds flat out */
349 if (cur_time
- drd
->time1
< 3000)
351 return compute_datarate(drd
, count
);
355 static void start_children(FFStream
*feed
)
360 for (; feed
; feed
= feed
->next
) {
361 if (feed
->child_argv
&& !feed
->pid
) {
362 feed
->pid_start
= time(0);
367 fprintf(stderr
, "Unable to create children\n");
376 for (i
= 3; i
< 256; i
++) {
380 if (!ffserver_debug
) {
381 i
= open("/dev/null", O_RDWR
);
390 pstrcpy(pathname
, sizeof(pathname
), my_program_name
);
392 slash
= strrchr(pathname
, '/');
398 strcpy(slash
, "ffmpeg");
400 /* This is needed to make relative pathnames work */
401 chdir(my_program_dir
);
403 execvp(pathname
, feed
->child_argv
);
411 /* open a listening socket */
412 static int socket_open_listen(struct sockaddr_in
*my_addr
)
416 server_fd
= socket(AF_INET
,SOCK_STREAM
,0);
423 setsockopt(server_fd
, SOL_SOCKET
, SO_REUSEADDR
, &tmp
, sizeof(tmp
));
425 if (bind (server_fd
, (struct sockaddr
*) my_addr
, sizeof (*my_addr
)) < 0) {
431 if (listen (server_fd
, 5) < 0) {
436 fcntl(server_fd
, F_SETFL
, O_NONBLOCK
);
441 /* start all multicast streams */
442 static void start_multicast(void)
447 struct sockaddr_in dest_addr
;
448 int default_port
, stream_index
;
451 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
452 if (stream
->is_multicast
) {
453 /* open the RTP connection */
454 snprintf(session_id
, sizeof(session_id
),
455 "%08x%08x", (int)random(), (int)random());
457 /* choose a port if none given */
458 if (stream
->multicast_port
== 0) {
459 stream
->multicast_port
= default_port
;
463 dest_addr
.sin_family
= AF_INET
;
464 dest_addr
.sin_addr
= stream
->multicast_ip
;
465 dest_addr
.sin_port
= htons(stream
->multicast_port
);
467 rtp_c
= rtp_new_connection(&dest_addr
, stream
, session_id
);
471 if (open_input_stream(rtp_c
, "") < 0) {
472 fprintf(stderr
, "Could not open input stream for stream '%s'\n",
477 rtp_c
->rtp_protocol
= RTSP_PROTOCOL_RTP_UDP_MULTICAST
;
479 /* open each RTP stream */
480 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
482 dest_addr
.sin_port
= htons(stream
->multicast_port
+
484 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
) < 0) {
485 fprintf(stderr
, "Could not open output stream '%s/streamid=%d'\n",
486 stream
->filename
, stream_index
);
491 /* change state to send data */
492 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
497 /* main loop of the http server */
498 static int http_server(void)
500 int server_fd
, ret
, rtsp_server_fd
, delay
, delay1
;
501 struct pollfd poll_table
[HTTP_MAX_CONNECTIONS
+ 2], *poll_entry
;
502 HTTPContext
*c
, *c_next
;
504 server_fd
= socket_open_listen(&my_http_addr
);
508 rtsp_server_fd
= socket_open_listen(&my_rtsp_addr
);
509 if (rtsp_server_fd
< 0)
512 http_log("ffserver started.\n");
514 start_children(first_feed
);
516 first_http_ctx
= NULL
;
518 first_http_ctx
= NULL
;
523 poll_entry
= poll_table
;
524 poll_entry
->fd
= server_fd
;
525 poll_entry
->events
= POLLIN
;
528 poll_entry
->fd
= rtsp_server_fd
;
529 poll_entry
->events
= POLLIN
;
532 /* wait for events on each HTTP handle */
539 case HTTPSTATE_SEND_HEADER
:
540 case RTSPSTATE_SEND_REPLY
:
541 c
->poll_entry
= poll_entry
;
543 poll_entry
->events
= POLLOUT
;
546 case HTTPSTATE_SEND_DATA_HEADER
:
547 case HTTPSTATE_SEND_DATA
:
548 case HTTPSTATE_SEND_DATA_TRAILER
:
549 if (!c
->is_packetized
) {
550 /* for TCP, we output as much as we can (may need to put a limit) */
551 c
->poll_entry
= poll_entry
;
553 poll_entry
->events
= POLLOUT
;
556 /* not strictly correct, but currently cannot add
557 more than one fd in poll entry */
561 case HTTPSTATE_WAIT_REQUEST
:
562 case HTTPSTATE_RECEIVE_DATA
:
563 case HTTPSTATE_WAIT_FEED
:
564 case RTSPSTATE_WAIT_REQUEST
:
565 /* need to catch errors */
566 c
->poll_entry
= poll_entry
;
568 poll_entry
->events
= POLLIN
;/* Maybe this will work */
572 c
->poll_entry
= NULL
;
573 delay1
= compute_send_delay(c
);
577 case HTTPSTATE_WAIT_SHORT
:
578 c
->poll_entry
= NULL
;
579 delay1
= 10; /* one tick wait XXX: 10 ms assumed */
584 c
->poll_entry
= NULL
;
590 /* wait for an event on one connection. We poll at least every
591 second to handle timeouts */
593 ret
= poll(poll_table
, poll_entry
- poll_table
, delay
);
596 cur_time
= gettime_ms();
598 if (need_to_start_children
) {
599 need_to_start_children
= 0;
600 start_children(first_feed
);
603 /* now handle the events */
604 for(c
= first_http_ctx
; c
!= NULL
; c
= c_next
) {
606 if (handle_connection(c
) < 0) {
607 /* close and free the connection */
613 poll_entry
= poll_table
;
614 /* new HTTP connection request ? */
615 if (poll_entry
->revents
& POLLIN
) {
616 new_connection(server_fd
, 0);
619 /* new RTSP connection request ? */
620 if (poll_entry
->revents
& POLLIN
) {
621 new_connection(rtsp_server_fd
, 1);
626 /* start waiting for a new HTTP/RTSP request */
627 static void start_wait_request(HTTPContext
*c
, int is_rtsp
)
629 c
->buffer_ptr
= c
->buffer
;
630 c
->buffer_end
= c
->buffer
+ c
->buffer_size
- 1; /* leave room for '\0' */
633 c
->timeout
= cur_time
+ RTSP_REQUEST_TIMEOUT
;
634 c
->state
= RTSPSTATE_WAIT_REQUEST
;
636 c
->timeout
= cur_time
+ HTTP_REQUEST_TIMEOUT
;
637 c
->state
= HTTPSTATE_WAIT_REQUEST
;
641 static void new_connection(int server_fd
, int is_rtsp
)
643 struct sockaddr_in from_addr
;
645 HTTPContext
*c
= NULL
;
647 len
= sizeof(from_addr
);
648 fd
= accept(server_fd
, (struct sockaddr
*)&from_addr
,
652 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
654 /* XXX: should output a warning page when coming
655 close to the connection limit */
656 if (nb_connections
>= nb_max_connections
)
659 /* add a new connection */
660 c
= av_mallocz(sizeof(HTTPContext
));
664 c
->next
= first_http_ctx
;
667 c
->poll_entry
= NULL
;
668 c
->from_addr
= from_addr
;
669 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
670 c
->buffer
= av_malloc(c
->buffer_size
);
675 start_wait_request(c
, is_rtsp
);
687 static void close_connection(HTTPContext
*c
)
689 HTTPContext
**cp
, *c1
;
691 AVFormatContext
*ctx
;
695 /* remove connection from list */
696 cp
= &first_http_ctx
;
697 while ((*cp
) != NULL
) {
706 /* remove connection associated resources */
710 /* close each frame parser */
711 for(i
=0;i
<c
->fmt_in
->nb_streams
;i
++) {
712 st
= c
->fmt_in
->streams
[i
];
713 if (st
->codec
.codec
) {
714 avcodec_close(&st
->codec
);
717 av_close_input_file(c
->fmt_in
);
720 /* free RTP output streams if any */
723 nb_streams
= c
->stream
->nb_streams
;
725 for(i
=0;i
<nb_streams
;i
++) {
728 av_write_trailer(ctx
);
731 h
= c
->rtp_handles
[i
];
738 current_bandwidth
-= c
->stream
->bandwidth
;
739 av_freep(&c
->pb_buffer
);
745 static int handle_connection(HTTPContext
*c
)
750 case HTTPSTATE_WAIT_REQUEST
:
751 case RTSPSTATE_WAIT_REQUEST
:
753 if ((c
->timeout
- cur_time
) < 0)
755 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
758 /* no need to read if no events */
759 if (!(c
->poll_entry
->revents
& POLLIN
))
762 len
= read(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
764 if (errno
!= EAGAIN
&& errno
!= EINTR
)
766 } else if (len
== 0) {
769 /* search for end of request. XXX: not fully correct since garbage could come after the end */
771 c
->buffer_ptr
+= len
;
773 if ((ptr
>= c
->buffer
+ 2 && !memcmp(ptr
-2, "\n\n", 2)) ||
774 (ptr
>= c
->buffer
+ 4 && !memcmp(ptr
-4, "\r\n\r\n", 4))) {
775 /* request found : parse it and reply */
776 if (c
->state
== HTTPSTATE_WAIT_REQUEST
) {
777 ret
= http_parse_request(c
);
779 ret
= rtsp_parse_request(c
);
783 } else if (ptr
>= c
->buffer_end
) {
784 /* request too long: cannot do anything */
790 case HTTPSTATE_SEND_HEADER
:
791 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
794 /* no need to write if no events */
795 if (!(c
->poll_entry
->revents
& POLLOUT
))
797 len
= write(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
799 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
800 /* error : close connection */
801 av_freep(&c
->pb_buffer
);
805 c
->buffer_ptr
+= len
;
807 c
->stream
->bytes_served
+= len
;
808 c
->data_count
+= len
;
809 if (c
->buffer_ptr
>= c
->buffer_end
) {
810 av_freep(&c
->pb_buffer
);
815 /* all the buffer was sent : synchronize to the incoming stream */
816 c
->state
= HTTPSTATE_SEND_DATA_HEADER
;
817 c
->buffer_ptr
= c
->buffer_end
= c
->buffer
;
822 case HTTPSTATE_SEND_DATA
:
823 case HTTPSTATE_SEND_DATA_HEADER
:
824 case HTTPSTATE_SEND_DATA_TRAILER
:
825 /* for packetized output, we consider we can always write (the
826 input streams sets the speed). It may be better to verify
827 that we do not rely too much on the kernel queues */
828 if (!c
->is_packetized
) {
829 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
832 /* no need to read if no events */
833 if (!(c
->poll_entry
->revents
& POLLOUT
))
836 if (http_send_data(c
) < 0)
839 case HTTPSTATE_RECEIVE_DATA
:
840 /* no need to read if no events */
841 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
843 if (!(c
->poll_entry
->revents
& POLLIN
))
845 if (http_receive_data(c
) < 0)
848 case HTTPSTATE_WAIT_FEED
:
849 /* no need to read if no events */
850 if (c
->poll_entry
->revents
& (POLLIN
| POLLERR
| POLLHUP
))
853 /* nothing to do, we'll be waken up by incoming feed packets */
857 /* if the delay expired, we can send new packets */
858 if (compute_send_delay(c
) <= 0)
859 c
->state
= HTTPSTATE_SEND_DATA
;
861 case HTTPSTATE_WAIT_SHORT
:
862 /* just return back to send data */
863 c
->state
= HTTPSTATE_SEND_DATA
;
866 case RTSPSTATE_SEND_REPLY
:
867 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
868 av_freep(&c
->pb_buffer
);
871 /* no need to write if no events */
872 if (!(c
->poll_entry
->revents
& POLLOUT
))
874 len
= write(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
876 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
877 /* error : close connection */
878 av_freep(&c
->pb_buffer
);
882 c
->buffer_ptr
+= len
;
883 c
->data_count
+= len
;
884 if (c
->buffer_ptr
>= c
->buffer_end
) {
885 /* all the buffer was sent : wait for a new request */
886 av_freep(&c
->pb_buffer
);
887 start_wait_request(c
, 1);
891 case HTTPSTATE_READY
:
900 static int extract_rates(char *rates
, int ratelen
, const char *request
)
904 for (p
= request
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
905 if (strncasecmp(p
, "Pragma:", 7) == 0) {
906 const char *q
= p
+ 7;
908 while (*q
&& *q
!= '\n' && isspace(*q
))
911 if (strncasecmp(q
, "stream-switch-entry=", 20) == 0) {
917 memset(rates
, 0xff, ratelen
);
920 while (*q
&& *q
!= '\n' && *q
!= ':')
923 if (sscanf(q
, ":%d:%d", &stream_no
, &rate_no
) != 2) {
927 if (stream_no
< ratelen
&& stream_no
>= 0) {
928 rates
[stream_no
] = rate_no
;
931 while (*q
&& *q
!= '\n' && !isspace(*q
))
948 static int find_stream_in_feed(FFStream
*feed
, AVCodecContext
*codec
, int bit_rate
)
951 int best_bitrate
= 100000000;
954 for (i
= 0; i
< feed
->nb_streams
; i
++) {
955 AVCodecContext
*feed_codec
= &feed
->streams
[i
]->codec
;
957 if (feed_codec
->codec_id
!= codec
->codec_id
||
958 feed_codec
->sample_rate
!= codec
->sample_rate
||
959 feed_codec
->width
!= codec
->width
||
960 feed_codec
->height
!= codec
->height
) {
964 /* Potential stream */
966 /* We want the fastest stream less than bit_rate, or the slowest
967 * faster than bit_rate
970 if (feed_codec
->bit_rate
<= bit_rate
) {
971 if (best_bitrate
> bit_rate
|| feed_codec
->bit_rate
> best_bitrate
) {
972 best_bitrate
= feed_codec
->bit_rate
;
976 if (feed_codec
->bit_rate
< best_bitrate
) {
977 best_bitrate
= feed_codec
->bit_rate
;
986 static int modify_current_stream(HTTPContext
*c
, char *rates
)
989 FFStream
*req
= c
->stream
;
990 int action_required
= 0;
992 for (i
= 0; i
< req
->nb_streams
; i
++) {
993 AVCodecContext
*codec
= &req
->streams
[i
]->codec
;
997 c
->switch_feed_streams
[i
] = req
->feed_streams
[i
];
1000 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 2);
1003 /* Wants off or slow */
1004 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 4);
1006 /* This doesn't work well when it turns off the only stream! */
1007 c
->switch_feed_streams
[i
] = -2;
1008 c
->feed_streams
[i
] = -2;
1013 if (c
->switch_feed_streams
[i
] >= 0 && c
->switch_feed_streams
[i
] != c
->feed_streams
[i
])
1014 action_required
= 1;
1017 return action_required
;
1021 static void do_switch_stream(HTTPContext
*c
, int i
)
1023 if (c
->switch_feed_streams
[i
] >= 0) {
1025 c
->feed_streams
[i
] = c
->switch_feed_streams
[i
];
1028 /* Now update the stream */
1030 c
->switch_feed_streams
[i
] = -1;
1033 /* XXX: factorize in utils.c ? */
1034 /* XXX: take care with different space meaning */
1035 static void skip_spaces(const char **pp
)
1039 while (*p
== ' ' || *p
== '\t')
1044 static void get_word(char *buf
, int buf_size
, const char **pp
)
1052 while (!isspace(*p
) && *p
!= '\0') {
1053 if ((q
- buf
) < buf_size
- 1)
1062 static int validate_acl(FFStream
*stream
, HTTPContext
*c
)
1064 enum IPAddressAction last_action
= IP_DENY
;
1066 struct in_addr
*src
= &c
->from_addr
.sin_addr
;
1068 for (acl
= stream
->acl
; acl
; acl
= acl
->next
) {
1069 if (src
->s_addr
>= acl
->first
.s_addr
&& src
->s_addr
<= acl
->last
.s_addr
) {
1070 return (acl
->action
== IP_ALLOW
) ?
1 : 0;
1072 last_action
= acl
->action
;
1075 /* Nothing matched, so return not the last action */
1076 return (last_action
== IP_DENY
) ?
1 : 0;
1079 /* compute the real filename of a file by matching it without its
1080 extensions to all the stream filenames */
1081 static void compute_real_filename(char *filename
, int max_size
)
1088 /* compute filename by matching without the file extensions */
1089 pstrcpy(file1
, sizeof(file1
), filename
);
1090 p
= strrchr(file1
, '.');
1093 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
1094 pstrcpy(file2
, sizeof(file2
), stream
->filename
);
1095 p
= strrchr(file2
, '.');
1098 if (!strcmp(file1
, file2
)) {
1099 pstrcpy(filename
, max_size
, stream
->filename
);
1114 /* parse http request and prepare header */
1115 static int http_parse_request(HTTPContext
*c
)
1119 enum RedirType redir_type
;
1121 char info
[1024], *filename
;
1125 const char *mime_type
;
1129 char *useragent
= 0;
1132 get_word(cmd
, sizeof(cmd
), (const char **)&p
);
1133 pstrcpy(c
->method
, sizeof(c
->method
), cmd
);
1135 if (!strcmp(cmd
, "GET"))
1137 else if (!strcmp(cmd
, "POST"))
1142 get_word(url
, sizeof(url
), (const char **)&p
);
1143 pstrcpy(c
->url
, sizeof(c
->url
), url
);
1145 get_word(protocol
, sizeof(protocol
), (const char **)&p
);
1146 if (strcmp(protocol
, "HTTP/1.0") && strcmp(protocol
, "HTTP/1.1"))
1149 pstrcpy(c
->protocol
, sizeof(c
->protocol
), protocol
);
1151 /* find the filename and the optional info string in the request */
1158 pstrcpy(info
, sizeof(info
), p
);
1164 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1165 if (strncasecmp(p
, "User-Agent:", 11) == 0) {
1167 if (*useragent
&& *useragent
!= '\n' && isspace(*useragent
))
1171 p
= strchr(p
, '\n');
1178 redir_type
= REDIR_NONE
;
1179 if (match_ext(filename
, "asx")) {
1180 redir_type
= REDIR_ASX
;
1181 filename
[strlen(filename
)-1] = 'f';
1182 } else if (match_ext(filename
, ".asf") &&
1183 (!useragent
|| strncasecmp(useragent
, "NSPlayer", 8) != 0)) {
1184 /* if this isn't WMP or lookalike, return the redirector file */
1185 redir_type
= REDIR_ASF
;
1186 } else if (match_ext(filename
, "rpm,ram")) {
1187 redir_type
= REDIR_RAM
;
1188 strcpy(filename
+ strlen(filename
)-2, "m");
1189 } else if (match_ext(filename
, "rtsp")) {
1190 redir_type
= REDIR_RTSP
;
1191 compute_real_filename(filename
, sizeof(url
) - 1);
1192 } else if (match_ext(filename
, "sdp")) {
1193 redir_type
= REDIR_SDP
;
1194 compute_real_filename(filename
, sizeof(url
) - 1);
1197 stream
= first_stream
;
1198 while (stream
!= NULL
) {
1199 if (!strcmp(stream
->filename
, filename
) && validate_acl(stream
, c
))
1201 stream
= stream
->next
;
1203 if (stream
== NULL
) {
1204 sprintf(msg
, "File '%s' not found", url
);
1209 memcpy(c
->feed_streams
, stream
->feed_streams
, sizeof(c
->feed_streams
));
1210 memset(c
->switch_feed_streams
, -1, sizeof(c
->switch_feed_streams
));
1212 if (stream
->stream_type
== STREAM_TYPE_REDIRECT
) {
1213 c
->http_error
= 301;
1215 q
+= sprintf(q
, "HTTP/1.0 301 Moved\r\n");
1216 q
+= sprintf(q
, "Location: %s\r\n", stream
->feed_filename
);
1217 q
+= sprintf(q
, "Content-type: text/html\r\n");
1218 q
+= sprintf(q
, "\r\n");
1219 q
+= sprintf(q
, "<html><head><title>Moved</title></head><body>\r\n");
1220 q
+= sprintf(q
, "You should be <a href=\"%s\">redirected</a>.\r\n", stream
->feed_filename
);
1221 q
+= sprintf(q
, "</body></html>\r\n");
1223 /* prepare output buffer */
1224 c
->buffer_ptr
= c
->buffer
;
1226 c
->state
= HTTPSTATE_SEND_HEADER
;
1230 /* If this is WMP, get the rate information */
1231 if (extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1232 if (modify_current_stream(c
, ratebuf
)) {
1233 for (i
= 0; i
< sizeof(c
->feed_streams
) / sizeof(c
->feed_streams
[0]); i
++) {
1234 if (c
->switch_feed_streams
[i
] >= 0)
1235 do_switch_stream(c
, i
);
1240 if (post
== 0 && stream
->stream_type
== STREAM_TYPE_LIVE
) {
1241 current_bandwidth
+= stream
->bandwidth
;
1244 if (post
== 0 && max_bandwidth
< current_bandwidth
) {
1245 c
->http_error
= 200;
1247 q
+= sprintf(q
, "HTTP/1.0 200 Server too busy\r\n");
1248 q
+= sprintf(q
, "Content-type: text/html\r\n");
1249 q
+= sprintf(q
, "\r\n");
1250 q
+= sprintf(q
, "<html><head><title>Too busy</title></head><body>\r\n");
1251 q
+= sprintf(q
, "The server is too busy to serve your request at this time.<p>\r\n");
1252 q
+= sprintf(q
, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
1253 current_bandwidth
, max_bandwidth
);
1254 q
+= sprintf(q
, "</body></html>\r\n");
1256 /* prepare output buffer */
1257 c
->buffer_ptr
= c
->buffer
;
1259 c
->state
= HTTPSTATE_SEND_HEADER
;
1263 if (redir_type
!= REDIR_NONE
) {
1266 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1267 if (strncasecmp(p
, "Host:", 5) == 0) {
1271 p
= strchr(p
, '\n');
1282 while (isspace(*hostinfo
))
1285 eoh
= strchr(hostinfo
, '\n');
1287 if (eoh
[-1] == '\r')
1290 if (eoh
- hostinfo
< sizeof(hostbuf
) - 1) {
1291 memcpy(hostbuf
, hostinfo
, eoh
- hostinfo
);
1292 hostbuf
[eoh
- hostinfo
] = 0;
1294 c
->http_error
= 200;
1296 switch(redir_type
) {
1298 q
+= sprintf(q
, "HTTP/1.0 200 ASX Follows\r\n");
1299 q
+= sprintf(q
, "Content-type: video/x-ms-asf\r\n");
1300 q
+= sprintf(q
, "\r\n");
1301 q
+= sprintf(q
, "<ASX Version=\"3\">\r\n");
1302 q
+= sprintf(q
, "<!-- Autogenerated by ffserver -->\r\n");
1303 q
+= sprintf(q
, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1304 hostbuf
, filename
, info
);
1305 q
+= sprintf(q
, "</ASX>\r\n");
1308 q
+= sprintf(q
, "HTTP/1.0 200 RAM Follows\r\n");
1309 q
+= sprintf(q
, "Content-type: audio/x-pn-realaudio\r\n");
1310 q
+= sprintf(q
, "\r\n");
1311 q
+= sprintf(q
, "# Autogenerated by ffserver\r\n");
1312 q
+= sprintf(q
, "http://%s/%s%s\r\n",
1313 hostbuf
, filename
, info
);
1316 q
+= sprintf(q
, "HTTP/1.0 200 ASF Redirect follows\r\n");
1317 q
+= sprintf(q
, "Content-type: video/x-ms-asf\r\n");
1318 q
+= sprintf(q
, "\r\n");
1319 q
+= sprintf(q
, "[Reference]\r\n");
1320 q
+= sprintf(q
, "Ref1=http://%s/%s%s\r\n",
1321 hostbuf
, filename
, info
);
1325 char hostname
[256], *p
;
1326 /* extract only hostname */
1327 pstrcpy(hostname
, sizeof(hostname
), hostbuf
);
1328 p
= strrchr(hostname
, ':');
1331 q
+= sprintf(q
, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1332 /* XXX: incorrect mime type ? */
1333 q
+= sprintf(q
, "Content-type: application/x-rtsp\r\n");
1334 q
+= sprintf(q
, "\r\n");
1335 q
+= sprintf(q
, "rtsp://%s:%d/%s\r\n",
1336 hostname
, ntohs(my_rtsp_addr
.sin_port
),
1343 int sdp_data_size
, len
;
1344 struct sockaddr_in my_addr
;
1346 q
+= sprintf(q
, "HTTP/1.0 200 OK\r\n");
1347 q
+= sprintf(q
, "Content-type: application/sdp\r\n");
1348 q
+= sprintf(q
, "\r\n");
1350 len
= sizeof(my_addr
);
1351 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
1353 /* XXX: should use a dynamic buffer */
1354 sdp_data_size
= prepare_sdp_description(stream
,
1357 if (sdp_data_size
> 0) {
1358 memcpy(q
, sdp_data
, sdp_data_size
);
1370 /* prepare output buffer */
1371 c
->buffer_ptr
= c
->buffer
;
1373 c
->state
= HTTPSTATE_SEND_HEADER
;
1379 sprintf(msg
, "ASX/RAM file not handled");
1383 stream
->conns_served
++;
1385 /* XXX: add there authenticate and IP match */
1388 /* if post, it means a feed is being sent */
1389 if (!stream
->is_feed
) {
1390 /* However it might be a status report from WMP! Lets log the data
1391 * as it might come in handy one day
1396 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1397 if (strncasecmp(p
, "Pragma: log-line=", 17) == 0) {
1401 if (strncasecmp(p
, "Pragma: client-id=", 18) == 0) {
1402 client_id
= strtol(p
+ 18, 0, 10);
1404 p
= strchr(p
, '\n');
1412 char *eol
= strchr(logline
, '\n');
1417 if (eol
[-1] == '\r')
1419 http_log("%.*s\n", eol
- logline
, logline
);
1420 c
->suppress_log
= 1;
1425 http_log("\nGot request:\n%s\n", c
->buffer
);
1428 if (client_id
&& extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1431 /* Now we have to find the client_id */
1432 for (wmpc
= first_http_ctx
; wmpc
; wmpc
= wmpc
->next
) {
1433 if (wmpc
->wmp_client_id
== client_id
)
1438 if (modify_current_stream(wmpc
, ratebuf
)) {
1439 wmpc
->switch_pending
= 1;
1444 sprintf(msg
, "POST command not handled");
1447 if (http_start_receive_data(c
) < 0) {
1448 sprintf(msg
, "could not open feed");
1452 c
->state
= HTTPSTATE_RECEIVE_DATA
;
1457 if (strcmp(stream
->filename
+ strlen(stream
->filename
) - 4, ".asf") == 0) {
1458 http_log("\nGot request:\n%s\n", c
->buffer
);
1462 if (c
->stream
->stream_type
== STREAM_TYPE_STATUS
)
1465 /* open input stream */
1466 if (open_input_stream(c
, info
) < 0) {
1467 sprintf(msg
, "Input stream corresponding to '%s' not found", url
);
1471 /* prepare http header */
1473 q
+= sprintf(q
, "HTTP/1.0 200 OK\r\n");
1474 mime_type
= c
->stream
->fmt
->mime_type
;
1476 mime_type
= "application/x-octet_stream";
1477 q
+= sprintf(q
, "Pragma: no-cache\r\n");
1479 /* for asf, we need extra headers */
1480 if (!strcmp(c
->stream
->fmt
->name
,"asf_stream")) {
1481 /* Need to allocate a client id */
1483 c
->wmp_client_id
= random() & 0x7fffffff;
1485 q
+= sprintf(q
, "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
);
1486 mime_type
= "application/octet-stream";
1488 q
+= sprintf(q
, "Content-Type: %s\r\n", mime_type
);
1489 q
+= sprintf(q
, "\r\n");
1491 /* prepare output buffer */
1493 c
->buffer_ptr
= c
->buffer
;
1495 c
->state
= HTTPSTATE_SEND_HEADER
;
1498 c
->http_error
= 404;
1500 q
+= sprintf(q
, "HTTP/1.0 404 Not Found\r\n");
1501 q
+= sprintf(q
, "Content-type: %s\r\n", "text/html");
1502 q
+= sprintf(q
, "\r\n");
1503 q
+= sprintf(q
, "<HTML>\n");
1504 q
+= sprintf(q
, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1505 q
+= sprintf(q
, "<BODY>%s</BODY>\n", msg
);
1506 q
+= sprintf(q
, "</HTML>\n");
1508 /* prepare output buffer */
1509 c
->buffer_ptr
= c
->buffer
;
1511 c
->state
= HTTPSTATE_SEND_HEADER
;
1515 c
->http_error
= 200; /* horrible : we use this value to avoid
1516 going to the send data state */
1517 c
->state
= HTTPSTATE_SEND_HEADER
;
1521 static void fmt_bytecount(ByteIOContext
*pb
, INT64 count
)
1523 static const char *suffix
= " kMGTP";
1526 for (s
= suffix
; count
>= 100000 && s
[1]; count
/= 1000, s
++) {
1529 url_fprintf(pb
, "%lld%c", count
, *s
);
1532 static void compute_stats(HTTPContext
*c
)
1539 ByteIOContext pb1
, *pb
= &pb1
;
1541 if (url_open_dyn_buf(pb
) < 0) {
1542 /* XXX: return an error ? */
1543 c
->buffer_ptr
= c
->buffer
;
1544 c
->buffer_end
= c
->buffer
;
1548 url_fprintf(pb
, "HTTP/1.0 200 OK\r\n");
1549 url_fprintf(pb
, "Content-type: %s\r\n", "text/html");
1550 url_fprintf(pb
, "Pragma: no-cache\r\n");
1551 url_fprintf(pb
, "\r\n");
1553 url_fprintf(pb
, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1554 if (c
->stream
->feed_filename
) {
1555 url_fprintf(pb
, "<link rel=\"shortcut icon\" href=\"%s\">\n", c
->stream
->feed_filename
);
1557 url_fprintf(pb
, "</HEAD>\n<BODY>");
1558 url_fprintf(pb
, "<H1>FFServer Status</H1>\n");
1560 url_fprintf(pb
, "<H2>Available Streams</H2>\n");
1561 url_fprintf(pb
, "<TABLE cellspacing=0 cellpadding=4>\n");
1562 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");
1563 stream
= first_stream
;
1564 while (stream
!= NULL
) {
1565 char sfilename
[1024];
1568 if (stream
->feed
!= stream
) {
1569 pstrcpy(sfilename
, sizeof(sfilename
) - 10, stream
->filename
);
1570 eosf
= sfilename
+ strlen(sfilename
);
1571 if (eosf
- sfilename
>= 4) {
1572 if (strcmp(eosf
- 4, ".asf") == 0) {
1573 strcpy(eosf
- 4, ".asx");
1574 } else if (strcmp(eosf
- 3, ".rm") == 0) {
1575 strcpy(eosf
- 3, ".ram");
1576 } else if (stream
->fmt
== &rtp_mux
) {
1577 /* generate a sample RTSP director if
1578 unicast. Generate an SDP redirector if
1580 eosf
= strrchr(sfilename
, '.');
1582 eosf
= sfilename
+ strlen(sfilename
);
1583 if (stream
->is_multicast
)
1584 strcpy(eosf
, ".sdp");
1586 strcpy(eosf
, ".rtsp");
1590 url_fprintf(pb
, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1591 sfilename
, stream
->filename
);
1592 url_fprintf(pb
, "<td align=right> %d <td align=right> ",
1593 stream
->conns_served
);
1594 fmt_bytecount(pb
, stream
->bytes_served
);
1595 switch(stream
->stream_type
) {
1596 case STREAM_TYPE_LIVE
:
1598 int audio_bit_rate
= 0;
1599 int video_bit_rate
= 0;
1600 const char *audio_codec_name
= "";
1601 const char *video_codec_name
= "";
1602 const char *audio_codec_name_extra
= "";
1603 const char *video_codec_name_extra
= "";
1605 for(i
=0;i
<stream
->nb_streams
;i
++) {
1606 AVStream
*st
= stream
->streams
[i
];
1607 AVCodec
*codec
= avcodec_find_encoder(st
->codec
.codec_id
);
1608 switch(st
->codec
.codec_type
) {
1609 case CODEC_TYPE_AUDIO
:
1610 audio_bit_rate
+= st
->codec
.bit_rate
;
1612 if (*audio_codec_name
)
1613 audio_codec_name_extra
= "...";
1614 audio_codec_name
= codec
->name
;
1617 case CODEC_TYPE_VIDEO
:
1618 video_bit_rate
+= st
->codec
.bit_rate
;
1620 if (*video_codec_name
)
1621 video_codec_name_extra
= "...";
1622 video_codec_name
= codec
->name
;
1629 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",
1632 video_bit_rate
/ 1000, video_codec_name
, video_codec_name_extra
,
1633 audio_bit_rate
/ 1000, audio_codec_name
, audio_codec_name_extra
);
1635 url_fprintf(pb
, "<TD>%s", stream
->feed
->filename
);
1637 url_fprintf(pb
, "<TD>%s", stream
->feed_filename
);
1639 url_fprintf(pb
, "\n");
1643 url_fprintf(pb
, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1647 stream
= stream
->next
;
1649 url_fprintf(pb
, "</TABLE>\n");
1651 stream
= first_stream
;
1652 while (stream
!= NULL
) {
1653 if (stream
->feed
== stream
) {
1654 url_fprintf(pb
, "<h2>Feed %s</h2>", stream
->filename
);
1656 url_fprintf(pb
, "Running as pid %d.\n", stream
->pid
);
1658 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1663 /* This is somewhat linux specific I guess */
1664 snprintf(ps_cmd
, sizeof(ps_cmd
),
1665 "ps -o \"%%cpu,cputime\" --no-headers %d",
1668 pid_stat
= popen(ps_cmd
, "r");
1673 if (fscanf(pid_stat
, "%10s %64s", cpuperc
,
1675 url_fprintf(pb
, "Currently using %s%% of the cpu. Total time used %s.\n",
1683 url_fprintf(pb
, "<p>");
1685 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");
1687 for (i
= 0; i
< stream
->nb_streams
; i
++) {
1688 AVStream
*st
= stream
->streams
[i
];
1689 AVCodec
*codec
= avcodec_find_encoder(st
->codec
.codec_id
);
1690 char *type
= "unknown";
1691 char parameters
[64];
1695 switch(st
->codec
.codec_type
) {
1696 case CODEC_TYPE_AUDIO
:
1699 case CODEC_TYPE_VIDEO
:
1701 sprintf(parameters
, "%dx%d, q=%d-%d, fps=%d", st
->codec
.width
, st
->codec
.height
,
1702 st
->codec
.qmin
, st
->codec
.qmax
, st
->codec
.frame_rate
/ FRAME_RATE_BASE
);
1707 url_fprintf(pb
, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1708 i
, type
, st
->codec
.bit_rate
/1000, codec ? codec
->name
: "", parameters
);
1710 url_fprintf(pb
, "</table>\n");
1713 stream
= stream
->next
;
1719 AVCodecContext
*enc
;
1723 stream
= first_feed
;
1724 while (stream
!= NULL
) {
1725 url_fprintf(pb
, "<H1>Feed '%s'</H1>\n", stream
->filename
);
1726 url_fprintf(pb
, "<TABLE>\n");
1727 url_fprintf(pb
, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1728 for(i
=0;i
<stream
->nb_streams
;i
++) {
1729 AVStream
*st
= stream
->streams
[i
];
1730 FeedData
*fdata
= st
->priv_data
;
1733 avcodec_string(buf
, sizeof(buf
), enc
);
1734 avg
= fdata
->avg_frame_size
* (float)enc
->rate
* 8.0;
1735 if (enc
->codec
->type
== CODEC_TYPE_AUDIO
&& enc
->frame_size
> 0)
1736 avg
/= enc
->frame_size
;
1737 url_fprintf(pb
, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1738 buf
, enc
->frame_number
, fdata
->data_count
, avg
/ 1000.0);
1740 url_fprintf(pb
, "</TABLE>\n");
1741 stream
= stream
->next_feed
;
1746 /* connection status */
1747 url_fprintf(pb
, "<H2>Connection Status</H2>\n");
1749 url_fprintf(pb
, "Number of connections: %d / %d<BR>\n",
1750 nb_connections
, nb_max_connections
);
1752 url_fprintf(pb
, "Bandwidth in use: %dk / %dk<BR>\n",
1753 current_bandwidth
, max_bandwidth
);
1755 url_fprintf(pb
, "<TABLE>\n");
1756 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");
1757 c1
= first_http_ctx
;
1759 while (c1
!= NULL
) {
1765 for (j
= 0; j
< c1
->stream
->nb_streams
; j
++) {
1766 if (!c1
->stream
->feed
) {
1767 bitrate
+= c1
->stream
->streams
[j
]->codec
.bit_rate
;
1769 if (c1
->feed_streams
[j
] >= 0) {
1770 bitrate
+= c1
->stream
->feed
->streams
[c1
->feed_streams
[j
]]->codec
.bit_rate
;
1777 p
= inet_ntoa(c1
->from_addr
.sin_addr
);
1778 url_fprintf(pb
, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1780 c1
->stream ? c1
->stream
->filename
: "",
1781 c1
->state
== HTTPSTATE_RECEIVE_DATA ?
"(input)" : "",
1784 http_state
[c1
->state
]);
1785 fmt_bytecount(pb
, bitrate
);
1786 url_fprintf(pb
, "<td align=right>");
1787 fmt_bytecount(pb
, compute_datarate(&c1
->datarate
, c1
->data_count
) * 8);
1788 url_fprintf(pb
, "<td align=right>");
1789 fmt_bytecount(pb
, c1
->data_count
);
1790 url_fprintf(pb
, "\n");
1793 url_fprintf(pb
, "</TABLE>\n");
1798 url_fprintf(pb
, "<HR size=1 noshade>Generated at %s", p
);
1799 url_fprintf(pb
, "</BODY>\n</HTML>\n");
1801 len
= url_close_dyn_buf(pb
, &c
->pb_buffer
);
1802 c
->buffer_ptr
= c
->pb_buffer
;
1803 c
->buffer_end
= c
->pb_buffer
+ len
;
1806 /* check if the parser needs to be opened for stream i */
1807 static void open_parser(AVFormatContext
*s
, int i
)
1809 AVStream
*st
= s
->streams
[i
];
1812 if (!st
->codec
.codec
) {
1813 codec
= avcodec_find_decoder(st
->codec
.codec_id
);
1814 if (codec
&& (codec
->capabilities
& CODEC_CAP_PARSE_ONLY
)) {
1815 st
->codec
.parse_only
= 1;
1816 if (avcodec_open(&st
->codec
, codec
) < 0) {
1817 st
->codec
.parse_only
= 0;
1823 static int open_input_stream(HTTPContext
*c
, const char *info
)
1826 char input_filename
[1024];
1831 /* find file name */
1832 if (c
->stream
->feed
) {
1833 strcpy(input_filename
, c
->stream
->feed
->feed_filename
);
1834 buf_size
= FFM_PACKET_SIZE
;
1835 /* compute position (absolute time) */
1836 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1837 stream_pos
= parse_date(buf
, 0);
1838 } else if (find_info_tag(buf
, sizeof(buf
), "buffer", info
)) {
1839 int prebuffer
= strtol(buf
, 0, 10);
1840 stream_pos
= av_gettime() - prebuffer
* (INT64
)1000000;
1842 stream_pos
= av_gettime() - c
->stream
->prebuffer
* (INT64
)1000;
1845 strcpy(input_filename
, c
->stream
->feed_filename
);
1847 /* compute position (relative time) */
1848 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1849 stream_pos
= parse_date(buf
, 1);
1854 if (input_filename
[0] == '\0')
1858 { time_t when
= stream_pos
/ 1000000;
1859 http_log("Stream pos = %lld, time=%s", stream_pos
, ctime(&when
));
1864 if (av_open_input_file(&s
, input_filename
, NULL
, buf_size
, NULL
) < 0) {
1865 http_log("%s not found", input_filename
);
1870 /* open each parser */
1871 for(i
=0;i
<s
->nb_streams
;i
++)
1874 /* choose stream as clock source (we favorize video stream if
1875 present) for packet sending */
1876 c
->pts_stream_index
= 0;
1877 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
1878 if (c
->pts_stream_index
== 0 &&
1879 c
->stream
->streams
[i
]->codec
.codec_type
== CODEC_TYPE_VIDEO
) {
1880 c
->pts_stream_index
= i
;
1884 if (c
->fmt_in
->iformat
->read_seek
) {
1885 c
->fmt_in
->iformat
->read_seek(c
->fmt_in
, stream_pos
);
1887 /* set the start time (needed for maxtime and RTP packet timing) */
1888 c
->start_time
= cur_time
;
1889 c
->first_pts
= AV_NOPTS_VALUE
;
1893 /* currently desactivated because the new PTS handling is not
1895 //#define AV_READ_FRAME
1896 #ifdef AV_READ_FRAME
1898 /* XXX: generalize that in ffmpeg for picture/audio/data. Currently
1899 the return packet MUST NOT be freed */
1900 int av_read_frame(AVFormatContext
*s
, AVPacket
*pkt
)
1903 int len
, ret
, old_nb_streams
, i
;
1905 /* see if remaining frames must be parsed */
1907 if (s
->cur_len
> 0) {
1908 st
= s
->streams
[s
->cur_pkt
.stream_index
];
1909 len
= avcodec_parse_frame(&st
->codec
, &pkt
->data
, &pkt
->size
,
1910 s
->cur_ptr
, s
->cur_len
);
1912 /* error: get next packet */
1918 /* init pts counter if not done */
1919 if (st
->pts
.den
== 0) {
1920 switch(st
->codec
.codec_type
) {
1921 case CODEC_TYPE_AUDIO
:
1922 st
->pts_incr
= (INT64
)s
->pts_den
;
1923 av_frac_init(&st
->pts
, st
->pts
.val
, 0,
1924 (INT64
)s
->pts_num
* st
->codec
.sample_rate
);
1926 case CODEC_TYPE_VIDEO
:
1927 st
->pts_incr
= (INT64
)s
->pts_den
* FRAME_RATE_BASE
;
1928 av_frac_init(&st
->pts
, st
->pts
.val
, 0,
1929 (INT64
)s
->pts_num
* st
->codec
.frame_rate
);
1936 /* a frame was read: return it */
1937 pkt
->pts
= st
->pts
.val
;
1939 printf("add pts=%Lx num=%Lx den=%Lx incr=%Lx\n",
1940 st
->pts
.val
, st
->pts
.num
, st
->pts
.den
, st
->pts_incr
);
1942 switch(st
->codec
.codec_type
) {
1943 case CODEC_TYPE_AUDIO
:
1944 av_frac_add(&st
->pts
, st
->pts_incr
* st
->codec
.frame_size
);
1946 case CODEC_TYPE_VIDEO
:
1947 av_frac_add(&st
->pts
, st
->pts_incr
);
1952 pkt
->stream_index
= s
->cur_pkt
.stream_index
;
1953 /* we use the codec indication because it is
1954 more accurate than the demux flags */
1956 if (st
->codec
.key_frame
)
1957 pkt
->flags
|= PKT_FLAG_KEY
;
1962 /* free previous packet */
1963 av_free_packet(&s
->cur_pkt
);
1965 old_nb_streams
= s
->nb_streams
;
1966 ret
= av_read_packet(s
, &s
->cur_pkt
);
1969 /* open parsers for each new streams */
1970 for(i
= old_nb_streams
; i
< s
->nb_streams
; i
++)
1972 st
= s
->streams
[s
->cur_pkt
.stream_index
];
1974 /* update current pts (XXX: dts handling) from packet, or
1975 use current pts if none given */
1976 if (s
->cur_pkt
.pts
!= AV_NOPTS_VALUE
) {
1977 av_frac_set(&st
->pts
, s
->cur_pkt
.pts
);
1979 s
->cur_pkt
.pts
= st
->pts
.val
;
1981 if (!st
->codec
.codec
) {
1982 /* no codec opened: just return the raw packet */
1985 /* no codec opened: just update the pts by considering we
1986 have one frame and free the packet */
1987 if (st
->pts
.den
== 0) {
1988 switch(st
->codec
.codec_type
) {
1989 case CODEC_TYPE_AUDIO
:
1990 st
->pts_incr
= (INT64
)s
->pts_den
* st
->codec
.frame_size
;
1991 av_frac_init(&st
->pts
, st
->pts
.val
, 0,
1992 (INT64
)s
->pts_num
* st
->codec
.sample_rate
);
1994 case CODEC_TYPE_VIDEO
:
1995 st
->pts_incr
= (INT64
)s
->pts_den
* FRAME_RATE_BASE
;
1996 av_frac_init(&st
->pts
, st
->pts
.val
, 0,
1997 (INT64
)s
->pts_num
* st
->codec
.frame_rate
);
2003 av_frac_add(&st
->pts
, st
->pts_incr
);
2006 s
->cur_ptr
= s
->cur_pkt
.data
;
2007 s
->cur_len
= s
->cur_pkt
.size
;
2013 static int compute_send_delay(HTTPContext
*c
)
2015 INT64 cur_pts
, delta_pts
, next_pts
;
2018 /* compute current pts value from system time */
2019 cur_pts
= ((INT64
)(cur_time
- c
->start_time
) * c
->fmt_in
->pts_den
) /
2020 (c
->fmt_in
->pts_num
* 1000LL);
2021 /* compute the delta from the stream we choose as
2022 main clock (we do that to avoid using explicit
2023 buffers to do exact packet reordering for each
2025 /* XXX: really need to fix the number of streams */
2026 if (c
->pts_stream_index
>= c
->fmt_in
->nb_streams
)
2029 next_pts
= c
->fmt_in
->streams
[c
->pts_stream_index
]->pts
.val
;
2030 delta_pts
= next_pts
- cur_pts
;
2031 if (delta_pts
<= 0) {
2034 delay1
= (delta_pts
* 1000 * c
->fmt_in
->pts_num
) / c
->fmt_in
->pts_den
;
2040 /* just fall backs */
2041 int av_read_frame(AVFormatContext
*s
, AVPacket
*pkt
)
2043 return av_read_packet(s
, pkt
);
2046 static int compute_send_delay(HTTPContext
*c
)
2048 int datarate
= 8 * get_longterm_datarate(&c
->datarate
, c
->data_count
);
2050 if (datarate
> c
->stream
->bandwidth
* 2000) {
2058 static int http_prepare_data(HTTPContext
*c
)
2061 AVFormatContext
*ctx
;
2064 case HTTPSTATE_SEND_DATA_HEADER
:
2065 memset(&c
->fmt_ctx
, 0, sizeof(c
->fmt_ctx
));
2066 pstrcpy(c
->fmt_ctx
.author
, sizeof(c
->fmt_ctx
.author
),
2068 pstrcpy(c
->fmt_ctx
.comment
, sizeof(c
->fmt_ctx
.comment
),
2069 c
->stream
->comment
);
2070 pstrcpy(c
->fmt_ctx
.copyright
, sizeof(c
->fmt_ctx
.copyright
),
2071 c
->stream
->copyright
);
2072 pstrcpy(c
->fmt_ctx
.title
, sizeof(c
->fmt_ctx
.title
),
2075 /* open output stream by using specified codecs */
2076 c
->fmt_ctx
.oformat
= c
->stream
->fmt
;
2077 c
->fmt_ctx
.nb_streams
= c
->stream
->nb_streams
;
2078 for(i
=0;i
<c
->fmt_ctx
.nb_streams
;i
++) {
2080 st
= av_mallocz(sizeof(AVStream
));
2081 c
->fmt_ctx
.streams
[i
] = st
;
2082 /* if file or feed, then just take streams from FFStream struct */
2083 if (!c
->stream
->feed
||
2084 c
->stream
->feed
== c
->stream
)
2085 memcpy(st
, c
->stream
->streams
[i
], sizeof(AVStream
));
2087 memcpy(st
, c
->stream
->feed
->streams
[c
->stream
->feed_streams
[i
]],
2089 st
->codec
.frame_number
= 0; /* XXX: should be done in
2090 AVStream, not in codec */
2092 c
->got_key_frame
= 0;
2094 /* prepare header and save header data in a stream */
2095 if (url_open_dyn_buf(&c
->fmt_ctx
.pb
) < 0) {
2096 /* XXX: potential leak */
2099 c
->fmt_ctx
.pb
.is_streamed
= 1;
2101 av_write_header(&c
->fmt_ctx
);
2103 len
= url_close_dyn_buf(&c
->fmt_ctx
.pb
, &c
->pb_buffer
);
2104 c
->buffer_ptr
= c
->pb_buffer
;
2105 c
->buffer_end
= c
->pb_buffer
+ len
;
2107 c
->state
= HTTPSTATE_SEND_DATA
;
2108 c
->last_packet_sent
= 0;
2110 case HTTPSTATE_SEND_DATA
:
2111 /* find a new packet */
2115 /* read a packet from the input stream */
2116 if (c
->stream
->feed
) {
2117 ffm_set_write_index(c
->fmt_in
,
2118 c
->stream
->feed
->feed_write_index
,
2119 c
->stream
->feed
->feed_size
);
2122 if (c
->stream
->max_time
&&
2123 c
->stream
->max_time
+ c
->start_time
- cur_time
< 0) {
2124 /* We have timed out */
2125 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2127 if (1 || c
->is_packetized
) {
2128 if (compute_send_delay(c
) > 0) {
2129 c
->state
= HTTPSTATE_WAIT
;
2130 return 1; /* state changed */
2134 if (av_read_frame(c
->fmt_in
, &pkt
) < 0) {
2135 if (c
->stream
->feed
&& c
->stream
->feed
->feed_opened
) {
2136 /* if coming from feed, it means we reached the end of the
2137 ffm file, so must wait for more data */
2138 c
->state
= HTTPSTATE_WAIT_FEED
;
2139 return 1; /* state changed */
2141 if (c
->stream
->loop
) {
2142 av_close_input_file(c
->fmt_in
);
2144 if (open_input_stream(c
, "") < 0)
2149 /* must send trailer now because eof or error */
2150 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2154 /* update first pts if needed */
2155 if (c
->first_pts
== AV_NOPTS_VALUE
)
2156 c
->first_pts
= pkt
.pts
;
2158 /* send it to the appropriate stream */
2159 if (c
->stream
->feed
) {
2160 /* if coming from a feed, select the right stream */
2161 if (c
->switch_pending
) {
2162 c
->switch_pending
= 0;
2163 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2164 if (c
->switch_feed_streams
[i
] == pkt
.stream_index
) {
2165 if (pkt
.flags
& PKT_FLAG_KEY
) {
2166 do_switch_stream(c
, i
);
2169 if (c
->switch_feed_streams
[i
] >= 0) {
2170 c
->switch_pending
= 1;
2174 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2175 if (c
->feed_streams
[i
] == pkt
.stream_index
) {
2176 pkt
.stream_index
= i
;
2177 if (pkt
.flags
& PKT_FLAG_KEY
) {
2178 c
->got_key_frame
|= 1 << i
;
2180 /* See if we have all the key frames, then
2181 * we start to send. This logic is not quite
2182 * right, but it works for the case of a
2183 * single video stream with one or more
2184 * audio streams (for which every frame is
2185 * typically a key frame).
2187 if (!c
->stream
->send_on_key
||
2188 ((c
->got_key_frame
+ 1) >> c
->stream
->nb_streams
)) {
2194 AVCodecContext
*codec
;
2197 /* specific handling for RTP: we use several
2198 output stream (one for each RTP
2199 connection). XXX: need more abstract handling */
2200 if (c
->is_packetized
) {
2201 c
->packet_stream_index
= pkt
.stream_index
;
2202 ctx
= c
->rtp_ctx
[c
->packet_stream_index
];
2203 codec
= &ctx
->streams
[0]->codec
;
2204 /* only one stream per RTP connection */
2205 pkt
.stream_index
= 0;
2209 codec
= &ctx
->streams
[pkt
.stream_index
]->codec
;
2212 codec
->key_frame
= ((pkt
.flags
& PKT_FLAG_KEY
) != 0);
2215 if (codec
->codec_type
== CODEC_TYPE_AUDIO
) {
2216 codec
->frame_size
= (codec
->sample_rate
* pkt
.duration
+ 500000) / 1000000;
2217 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
2221 if (c
->is_packetized
) {
2222 ret
= url_open_dyn_packet_buf(&ctx
->pb
,
2223 url_get_max_packet_size(c
->rtp_handles
[c
->packet_stream_index
]));
2224 c
->packet_byte_count
= 0;
2225 c
->packet_start_time_us
= av_gettime();
2227 ret
= url_open_dyn_buf(&ctx
->pb
);
2230 /* XXX: potential leak */
2233 if (av_write_frame(ctx
, pkt
.stream_index
, pkt
.data
, pkt
.size
)) {
2234 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2237 len
= url_close_dyn_buf(&ctx
->pb
, &c
->pb_buffer
);
2238 c
->buffer_ptr
= c
->pb_buffer
;
2239 c
->buffer_end
= c
->pb_buffer
+ len
;
2241 codec
->frame_number
++;
2243 #ifndef AV_READ_FRAME
2244 av_free_packet(&pkt
);
2251 case HTTPSTATE_SEND_DATA_TRAILER
:
2252 /* last packet test ? */
2253 if (c
->last_packet_sent
|| c
->is_packetized
)
2256 /* prepare header */
2257 if (url_open_dyn_buf(&ctx
->pb
) < 0) {
2258 /* XXX: potential leak */
2261 av_write_trailer(ctx
);
2262 len
= url_close_dyn_buf(&ctx
->pb
, &c
->pb_buffer
);
2263 c
->buffer_ptr
= c
->pb_buffer
;
2264 c
->buffer_end
= c
->pb_buffer
+ len
;
2266 c
->last_packet_sent
= 1;
2273 #define SHORT_TERM_BANDWIDTH 8000000
2275 /* should convert the format at the same time */
2276 static int http_send_data(HTTPContext
*c
)
2280 while (c
->buffer_ptr
>= c
->buffer_end
) {
2281 av_freep(&c
->pb_buffer
);
2282 ret
= http_prepare_data(c
);
2285 else if (ret
== 0) {
2288 /* state change requested */
2293 if (c
->buffer_ptr
< c
->buffer_end
) {
2294 if (c
->is_packetized
) {
2295 /* RTP/UDP data output */
2296 len
= c
->buffer_end
- c
->buffer_ptr
;
2298 /* fail safe - should never happen */
2300 c
->buffer_ptr
= c
->buffer_end
;
2303 len
= (c
->buffer_ptr
[0] << 24) |
2304 (c
->buffer_ptr
[1] << 16) |
2305 (c
->buffer_ptr
[2] << 8) |
2307 if (len
> (c
->buffer_end
- c
->buffer_ptr
))
2310 /* short term bandwidth limitation */
2311 dt
= av_gettime() - c
->packet_start_time_us
;
2315 if ((c
->packet_byte_count
+ len
) * (INT64
)1000000 >=
2316 (SHORT_TERM_BANDWIDTH
/ 8) * (INT64
)dt
) {
2317 /* bandwidth overflow : wait at most one tick and retry */
2318 c
->state
= HTTPSTATE_WAIT_SHORT
;
2323 url_write(c
->rtp_handles
[c
->packet_stream_index
],
2324 c
->buffer_ptr
, len
);
2325 c
->buffer_ptr
+= len
;
2326 c
->packet_byte_count
+= len
;
2328 /* TCP data output */
2329 len
= write(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
2331 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
2332 /* error : close connection */
2338 c
->buffer_ptr
+= len
;
2341 c
->data_count
+= len
;
2342 update_datarate(&c
->datarate
, c
->data_count
);
2344 c
->stream
->bytes_served
+= len
;
2349 static int http_start_receive_data(HTTPContext
*c
)
2353 if (c
->stream
->feed_opened
)
2357 fd
= open(c
->stream
->feed_filename
, O_RDWR
);
2362 c
->stream
->feed_write_index
= ffm_read_write_index(fd
);
2363 c
->stream
->feed_size
= lseek(fd
, 0, SEEK_END
);
2364 lseek(fd
, 0, SEEK_SET
);
2366 /* init buffer input */
2367 c
->buffer_ptr
= c
->buffer
;
2368 c
->buffer_end
= c
->buffer
+ FFM_PACKET_SIZE
;
2369 c
->stream
->feed_opened
= 1;
2373 static int http_receive_data(HTTPContext
*c
)
2377 if (c
->buffer_end
> c
->buffer_ptr
) {
2380 len
= read(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
2382 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
2383 /* error : close connection */
2386 } else if (len
== 0) {
2387 /* end of connection : close it */
2390 c
->buffer_ptr
+= len
;
2391 c
->data_count
+= len
;
2392 update_datarate(&c
->datarate
, c
->data_count
);
2396 if (c
->buffer_ptr
>= c
->buffer_end
) {
2397 FFStream
*feed
= c
->stream
;
2398 /* a packet has been received : write it in the store, except
2400 if (c
->data_count
> FFM_PACKET_SIZE
) {
2402 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2403 /* XXX: use llseek or url_seek */
2404 lseek(c
->feed_fd
, feed
->feed_write_index
, SEEK_SET
);
2405 write(c
->feed_fd
, c
->buffer
, FFM_PACKET_SIZE
);
2407 feed
->feed_write_index
+= FFM_PACKET_SIZE
;
2408 /* update file size */
2409 if (feed
->feed_write_index
> c
->stream
->feed_size
)
2410 feed
->feed_size
= feed
->feed_write_index
;
2412 /* handle wrap around if max file size reached */
2413 if (feed
->feed_write_index
>= c
->stream
->feed_max_size
)
2414 feed
->feed_write_index
= FFM_PACKET_SIZE
;
2417 ffm_write_write_index(c
->feed_fd
, feed
->feed_write_index
);
2419 /* wake up any waiting connections */
2420 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2421 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2422 c1
->stream
->feed
== c
->stream
->feed
) {
2423 c1
->state
= HTTPSTATE_SEND_DATA
;
2427 /* We have a header in our hands that contains useful data */
2429 AVInputFormat
*fmt_in
;
2430 ByteIOContext
*pb
= &s
.pb
;
2433 memset(&s
, 0, sizeof(s
));
2435 url_open_buf(pb
, c
->buffer
, c
->buffer_end
- c
->buffer
, URL_RDONLY
);
2436 pb
->buf_end
= c
->buffer_end
; /* ?? */
2437 pb
->is_streamed
= 1;
2439 /* use feed output format name to find corresponding input format */
2440 fmt_in
= av_find_input_format(feed
->fmt
->name
);
2444 s
.priv_data
= av_mallocz(fmt_in
->priv_data_size
);
2448 if (fmt_in
->read_header(&s
, 0) < 0) {
2449 av_freep(&s
.priv_data
);
2453 /* Now we have the actual streams */
2454 if (s
.nb_streams
!= feed
->nb_streams
) {
2455 av_freep(&s
.priv_data
);
2458 for (i
= 0; i
< s
.nb_streams
; i
++) {
2459 memcpy(&feed
->streams
[i
]->codec
,
2460 &s
.streams
[i
]->codec
, sizeof(AVCodecContext
));
2462 av_freep(&s
.priv_data
);
2464 c
->buffer_ptr
= c
->buffer
;
2469 c
->stream
->feed_opened
= 0;
2474 /********************************************************************/
2477 static void rtsp_reply_header(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2484 switch(error_number
) {
2485 #define DEF(n, c, s) case c: str = s; break;
2486 #include "rtspcodes.h"
2489 str
= "Unknown Error";
2493 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", error_number
, str
);
2494 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2496 /* output GMT time */
2500 p
= buf2
+ strlen(p
) - 1;
2503 url_fprintf(c
->pb
, "Date: %s GMT\r\n", buf2
);
2506 static void rtsp_reply_error(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2508 rtsp_reply_header(c
, error_number
);
2509 url_fprintf(c
->pb
, "\r\n");
2512 static int rtsp_parse_request(HTTPContext
*c
)
2514 const char *p
, *p1
, *p2
;
2521 RTSPHeader header1
, *header
= &header1
;
2523 c
->buffer_ptr
[0] = '\0';
2526 get_word(cmd
, sizeof(cmd
), &p
);
2527 get_word(url
, sizeof(url
), &p
);
2528 get_word(protocol
, sizeof(protocol
), &p
);
2530 pstrcpy(c
->method
, sizeof(c
->method
), cmd
);
2531 pstrcpy(c
->url
, sizeof(c
->url
), url
);
2532 pstrcpy(c
->protocol
, sizeof(c
->protocol
), protocol
);
2535 if (url_open_dyn_buf(c
->pb
) < 0) {
2536 /* XXX: cannot do more */
2537 c
->pb
= NULL
; /* safety */
2541 /* check version name */
2542 if (strcmp(protocol
, "RTSP/1.0") != 0) {
2543 rtsp_reply_error(c
, RTSP_STATUS_VERSION
);
2547 /* parse each header line */
2548 memset(header
, 0, sizeof(RTSPHeader
));
2549 /* skip to next line */
2550 while (*p
!= '\n' && *p
!= '\0')
2554 while (*p
!= '\0') {
2555 p1
= strchr(p
, '\n');
2559 if (p2
> p
&& p2
[-1] == '\r')
2561 /* skip empty line */
2565 if (len
> sizeof(line
) - 1)
2566 len
= sizeof(line
) - 1;
2567 memcpy(line
, p
, len
);
2569 rtsp_parse_line(header
, line
);
2573 /* handle sequence number */
2574 c
->seq
= header
->seq
;
2576 if (!strcmp(cmd
, "DESCRIBE")) {
2577 rtsp_cmd_describe(c
, url
);
2578 } else if (!strcmp(cmd
, "SETUP")) {
2579 rtsp_cmd_setup(c
, url
, header
);
2580 } else if (!strcmp(cmd
, "PLAY")) {
2581 rtsp_cmd_play(c
, url
, header
);
2582 } else if (!strcmp(cmd
, "PAUSE")) {
2583 rtsp_cmd_pause(c
, url
, header
);
2584 } else if (!strcmp(cmd
, "TEARDOWN")) {
2585 rtsp_cmd_teardown(c
, url
, header
);
2587 rtsp_reply_error(c
, RTSP_STATUS_METHOD
);
2590 len
= url_close_dyn_buf(c
->pb
, &c
->pb_buffer
);
2591 c
->pb
= NULL
; /* safety */
2593 /* XXX: cannot do more */
2596 c
->buffer_ptr
= c
->pb_buffer
;
2597 c
->buffer_end
= c
->pb_buffer
+ len
;
2598 c
->state
= RTSPSTATE_SEND_REPLY
;
2602 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2604 static int prepare_sdp_description(FFStream
*stream
, UINT8
**pbuffer
,
2605 struct in_addr my_ip
)
2607 ByteIOContext pb1
, *pb
= &pb1
;
2608 int i
, payload_type
, port
, private_payload_type
, j
;
2609 const char *ipstr
, *title
, *mediatype
;
2612 if (url_open_dyn_buf(pb
) < 0)
2615 /* general media info */
2617 url_fprintf(pb
, "v=0\n");
2618 ipstr
= inet_ntoa(my_ip
);
2619 url_fprintf(pb
, "o=- 0 0 IN IP4 %s\n", ipstr
);
2620 title
= stream
->title
;
2621 if (title
[0] == '\0')
2623 url_fprintf(pb
, "s=%s\n", title
);
2624 if (stream
->comment
[0] != '\0')
2625 url_fprintf(pb
, "i=%s\n", stream
->comment
);
2626 if (stream
->is_multicast
) {
2627 url_fprintf(pb
, "c=IN IP4 %s\n", inet_ntoa(stream
->multicast_ip
));
2629 /* for each stream, we output the necessary info */
2630 private_payload_type
= 96;
2631 for(i
= 0; i
< stream
->nb_streams
; i
++) {
2632 st
= stream
->streams
[i
];
2633 switch(st
->codec
.codec_type
) {
2634 case CODEC_TYPE_AUDIO
:
2635 mediatype
= "audio";
2637 case CODEC_TYPE_VIDEO
:
2638 mediatype
= "video";
2641 mediatype
= "application";
2644 /* NOTE: the port indication is not correct in case of
2645 unicast. It is not an issue because RTSP gives it */
2646 payload_type
= rtp_get_payload_type(&st
->codec
);
2647 if (payload_type
< 0)
2648 payload_type
= private_payload_type
++;
2649 if (stream
->is_multicast
) {
2650 port
= stream
->multicast_port
+ 2 * i
;
2654 url_fprintf(pb
, "m=%s %d RTP/AVP %d\n",
2655 mediatype
, port
, payload_type
);
2656 if (payload_type
>= 96) {
2657 /* for private payload type, we need to give more info */
2658 switch(st
->codec
.codec_id
) {
2659 case CODEC_ID_MPEG4
:
2662 url_fprintf(pb
, "a=rtpmap:%d MP4V-ES/%d\n",
2663 payload_type
, 90000);
2664 /* we must also add the mpeg4 header */
2665 data
= st
->codec
.extradata
;
2667 url_fprintf(pb
, "a=fmtp:%d config=");
2668 for(j
=0;j
<st
->codec
.extradata_size
;j
++) {
2669 url_fprintf(pb
, "%02x", data
[j
]);
2671 url_fprintf(pb
, "\n");
2676 /* XXX: add other codecs ? */
2680 url_fprintf(pb
, "a=control:streamid=%d\n", i
);
2682 return url_close_dyn_buf(pb
, pbuffer
);
2684 url_close_dyn_buf(pb
, pbuffer
);
2689 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
)
2695 int content_length
, len
;
2696 struct sockaddr_in my_addr
;
2698 /* find which url is asked */
2699 url_split(NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2704 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2705 if (!stream
->is_feed
&& stream
->fmt
== &rtp_mux
&&
2706 !strcmp(path
, stream
->filename
)) {
2710 /* no stream found */
2711 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2715 /* prepare the media description in sdp format */
2717 /* get the host IP */
2718 len
= sizeof(my_addr
);
2719 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
2721 content_length
= prepare_sdp_description(stream
, &content
, my_addr
.sin_addr
);
2722 if (content_length
< 0) {
2723 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2726 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2727 url_fprintf(c
->pb
, "Content-Type: application/sdp\r\n");
2728 url_fprintf(c
->pb
, "Content-Length: %d\r\n", content_length
);
2729 url_fprintf(c
->pb
, "\r\n");
2730 put_buffer(c
->pb
, content
, content_length
);
2733 static HTTPContext
*find_rtp_session(const char *session_id
)
2737 if (session_id
[0] == '\0')
2740 for(c
= first_http_ctx
; c
!= NULL
; c
= c
->next
) {
2741 if (!strcmp(c
->session_id
, session_id
))
2747 RTSPTransportField
*find_transport(RTSPHeader
*h
, enum RTSPProtocol protocol
)
2749 RTSPTransportField
*th
;
2752 for(i
=0;i
<h
->nb_transports
;i
++) {
2753 th
= &h
->transports
[i
];
2754 if (th
->protocol
== protocol
)
2760 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
,
2764 int stream_index
, port
;
2769 RTSPTransportField
*th
;
2770 struct sockaddr_in dest_addr
;
2771 RTSPActionServerSetup setup
;
2773 /* find which url is asked */
2774 url_split(NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2779 /* now check each stream */
2780 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2781 if (!stream
->is_feed
&& stream
->fmt
== &rtp_mux
) {
2782 /* accept aggregate filenames only if single stream */
2783 if (!strcmp(path
, stream
->filename
)) {
2784 if (stream
->nb_streams
!= 1) {
2785 rtsp_reply_error(c
, RTSP_STATUS_AGGREGATE
);
2792 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
2794 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
2795 stream
->filename
, stream_index
);
2796 if (!strcmp(path
, buf
))
2801 /* no stream found */
2802 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2806 /* generate session id if needed */
2807 if (h
->session_id
[0] == '\0') {
2808 snprintf(h
->session_id
, sizeof(h
->session_id
),
2809 "%08x%08x", (int)random(), (int)random());
2812 /* find rtp session, and create it if none found */
2813 rtp_c
= find_rtp_session(h
->session_id
);
2815 rtp_c
= rtp_new_connection(&c
->from_addr
, stream
, h
->session_id
);
2817 rtsp_reply_error(c
, RTSP_STATUS_BANDWIDTH
);
2821 /* open input stream */
2822 if (open_input_stream(rtp_c
, "") < 0) {
2823 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2827 /* always prefer UDP */
2828 th
= find_transport(h
, RTSP_PROTOCOL_RTP_UDP
);
2830 th
= find_transport(h
, RTSP_PROTOCOL_RTP_TCP
);
2832 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2836 rtp_c
->rtp_protocol
= th
->protocol
;
2839 /* test if stream is OK (test needed because several SETUP needs
2840 to be done for a given file) */
2841 if (rtp_c
->stream
!= stream
) {
2842 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
);
2846 /* test if stream is already set up */
2847 if (rtp_c
->rtp_ctx
[stream_index
]) {
2848 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2852 /* check transport */
2853 th
= find_transport(h
, rtp_c
->rtp_protocol
);
2854 if (!th
|| (th
->protocol
== RTSP_PROTOCOL_RTP_UDP
&&
2855 th
->client_port_min
<= 0)) {
2856 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2860 /* setup default options */
2861 setup
.transport_option
[0] = '\0';
2862 dest_addr
= rtp_c
->from_addr
;
2863 dest_addr
.sin_port
= htons(th
->client_port_min
);
2865 /* add transport option if needed */
2866 if (ff_rtsp_callback
) {
2867 setup
.ipaddr
= ntohl(dest_addr
.sin_addr
.s_addr
);
2868 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP
, rtp_c
->session_id
,
2869 (char *)&setup
, sizeof(setup
),
2870 stream
->rtsp_option
) < 0) {
2871 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2874 dest_addr
.sin_addr
.s_addr
= htonl(setup
.ipaddr
);
2878 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
) < 0) {
2879 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2883 /* now everything is OK, so we can send the connection parameters */
2884 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2886 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2888 switch(rtp_c
->rtp_protocol
) {
2889 case RTSP_PROTOCOL_RTP_UDP
:
2890 port
= rtp_get_local_port(rtp_c
->rtp_handles
[stream_index
]);
2891 url_fprintf(c
->pb
, "Transport: RTP/AVP/UDP;unicast;"
2892 "client_port=%d-%d;server_port=%d-%d",
2893 th
->client_port_min
, th
->client_port_min
+ 1,
2896 case RTSP_PROTOCOL_RTP_TCP
:
2897 url_fprintf(c
->pb
, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2898 stream_index
* 2, stream_index
* 2 + 1);
2903 if (setup
.transport_option
[0] != '\0') {
2904 url_fprintf(c
->pb
, ";%s", setup
.transport_option
);
2906 url_fprintf(c
->pb
, "\r\n");
2909 url_fprintf(c
->pb
, "\r\n");
2913 /* find an rtp connection by using the session ID. Check consistency
2915 static HTTPContext
*find_rtp_session_with_url(const char *url
,
2916 const char *session_id
)
2922 rtp_c
= find_rtp_session(session_id
);
2926 /* find which url is asked */
2927 url_split(NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2931 if (strcmp(path
, rtp_c
->stream
->filename
) != 0)
2936 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
2940 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
2942 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
2946 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
2947 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
&&
2948 rtp_c
->state
!= HTTPSTATE_READY
) {
2949 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2953 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
2955 /* now everything is OK, so we can send the connection parameters */
2956 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2958 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2959 url_fprintf(c
->pb
, "\r\n");
2962 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
2966 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
2968 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
2972 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
2973 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
) {
2974 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2978 rtp_c
->state
= HTTPSTATE_READY
;
2980 /* now everything is OK, so we can send the connection parameters */
2981 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2983 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2984 url_fprintf(c
->pb
, "\r\n");
2987 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
2991 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
2993 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
2997 /* abort the session */
2998 close_connection(rtp_c
);
3000 if (ff_rtsp_callback
) {
3001 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN
, rtp_c
->session_id
,
3003 rtp_c
->stream
->rtsp_option
);
3006 /* now everything is OK, so we can send the connection parameters */
3007 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3009 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3010 url_fprintf(c
->pb
, "\r\n");
3014 /********************************************************************/
3017 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
3018 FFStream
*stream
, const char *session_id
)
3020 HTTPContext
*c
= NULL
;
3022 /* XXX: should output a warning page when coming
3023 close to the connection limit */
3024 if (nb_connections
>= nb_max_connections
)
3027 /* add a new connection */
3028 c
= av_mallocz(sizeof(HTTPContext
));
3033 c
->poll_entry
= NULL
;
3034 c
->from_addr
= *from_addr
;
3035 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
3036 c
->buffer
= av_malloc(c
->buffer_size
);
3041 pstrcpy(c
->session_id
, sizeof(c
->session_id
), session_id
);
3042 c
->state
= HTTPSTATE_READY
;
3043 c
->is_packetized
= 1;
3044 /* protocol is shown in statistics */
3045 pstrcpy(c
->protocol
, sizeof(c
->protocol
), "RTP");
3047 current_bandwidth
+= stream
->bandwidth
;
3049 c
->next
= first_http_ctx
;
3061 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3062 command). if dest_addr is NULL, then TCP tunneling in RTSP is
3064 static int rtp_new_av_stream(HTTPContext
*c
,
3065 int stream_index
, struct sockaddr_in
*dest_addr
)
3067 AVFormatContext
*ctx
;
3074 /* now we can open the relevant output stream */
3075 ctx
= av_mallocz(sizeof(AVFormatContext
));
3078 ctx
->oformat
= &rtp_mux
;
3080 st
= av_mallocz(sizeof(AVStream
));
3083 ctx
->nb_streams
= 1;
3084 ctx
->streams
[0] = st
;
3086 if (!c
->stream
->feed
||
3087 c
->stream
->feed
== c
->stream
) {
3088 memcpy(st
, c
->stream
->streams
[stream_index
], sizeof(AVStream
));
3091 c
->stream
->feed
->streams
[c
->stream
->feed_streams
[stream_index
]],
3096 /* build destination RTP address */
3097 ipaddr
= inet_ntoa(dest_addr
->sin_addr
);
3099 /* XXX: also pass as parameter to function ? */
3100 if (c
->stream
->is_multicast
) {
3102 ttl
= c
->stream
->multicast_ttl
;
3105 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3106 "rtp://%s:%d?multicast=1&ttl=%d",
3107 ipaddr
, ntohs(dest_addr
->sin_port
), ttl
);
3109 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3110 "rtp://%s:%d", ipaddr
, ntohs(dest_addr
->sin_port
));
3113 if (url_open(&h
, ctx
->filename
, URL_WRONLY
) < 0)
3115 c
->rtp_handles
[stream_index
] = h
;
3120 http_log("%s:%d - - [%s] \"RTPSTART %s/streamid=%d\"\n",
3121 ipaddr
, ntohs(dest_addr
->sin_port
),
3123 c
->stream
->filename
, stream_index
);
3125 /* normally, no packets should be output here, but the packet size may be checked */
3126 if (url_open_dyn_packet_buf(&ctx
->pb
,
3127 url_get_max_packet_size(h
)) < 0) {
3128 /* XXX: close stream */
3131 if (av_write_header(ctx
) < 0) {
3138 url_close_dyn_buf(&ctx
->pb
, &dummy_buf
);
3141 c
->rtp_ctx
[stream_index
] = ctx
;
3145 /********************************************************************/
3146 /* ffserver initialization */
3148 AVStream
*add_av_stream1(FFStream
*stream
, AVCodecContext
*codec
)
3152 fst
= av_mallocz(sizeof(AVStream
));
3155 fst
->priv_data
= av_mallocz(sizeof(FeedData
));
3156 memcpy(&fst
->codec
, codec
, sizeof(AVCodecContext
));
3157 stream
->streams
[stream
->nb_streams
++] = fst
;
3161 /* return the stream number in the feed */
3162 int add_av_stream(FFStream
*feed
,
3166 AVCodecContext
*av
, *av1
;
3170 for(i
=0;i
<feed
->nb_streams
;i
++) {
3171 st
= feed
->streams
[i
];
3173 if (av1
->codec_id
== av
->codec_id
&&
3174 av1
->codec_type
== av
->codec_type
&&
3175 av1
->bit_rate
== av
->bit_rate
) {
3177 switch(av
->codec_type
) {
3178 case CODEC_TYPE_AUDIO
:
3179 if (av1
->channels
== av
->channels
&&
3180 av1
->sample_rate
== av
->sample_rate
)
3183 case CODEC_TYPE_VIDEO
:
3184 if (av1
->width
== av
->width
&&
3185 av1
->height
== av
->height
&&
3186 av1
->frame_rate
== av
->frame_rate
&&
3187 av1
->gop_size
== av
->gop_size
)
3196 fst
= add_av_stream1(feed
, av
);
3199 return feed
->nb_streams
- 1;
3204 void remove_stream(FFStream
*stream
)
3208 while (*ps
!= NULL
) {
3209 if (*ps
== stream
) {
3217 /* specific mpeg4 handling : we extract the raw parameters */
3218 void extract_mpeg4_header(AVFormatContext
*infile
)
3220 int mpeg4_count
, i
, size
;
3226 for(i
=0;i
<infile
->nb_streams
;i
++) {
3227 st
= infile
->streams
[i
];
3228 if (st
->codec
.codec_id
== CODEC_ID_MPEG4
&&
3229 st
->codec
.extradata
== NULL
) {
3236 printf("MPEG4 without extra data: trying to find header\n");
3237 while (mpeg4_count
> 0) {
3238 if (av_read_packet(infile
, &pkt
) < 0)
3240 st
= infile
->streams
[pkt
.stream_index
];
3241 if (st
->codec
.codec_id
== CODEC_ID_MPEG4
&&
3242 st
->codec
.extradata
== NULL
) {
3243 /* fill extradata with the header */
3244 /* XXX: we make hard suppositions here ! */
3246 while (p
< pkt
.data
+ pkt
.size
- 4) {
3247 /* stop when vop header is found */
3248 if (p
[0] == 0x00 && p
[1] == 0x00 &&
3249 p
[2] == 0x01 && p
[3] == 0xb6) {
3250 size
= p
- pkt
.data
;
3251 // av_hex_dump(pkt.data, size);
3252 st
->codec
.extradata
= av_malloc(size
);
3253 st
->codec
.extradata_size
= size
;
3254 memcpy(st
->codec
.extradata
, pkt
.data
, size
);
3261 av_free_packet(&pkt
);
3265 /* compute the needed AVStream for each file */
3266 void build_file_streams(void)
3268 FFStream
*stream
, *stream_next
;
3269 AVFormatContext
*infile
;
3272 /* gather all streams */
3273 for(stream
= first_stream
; stream
!= NULL
; stream
= stream_next
) {
3274 stream_next
= stream
->next
;
3275 if (stream
->stream_type
== STREAM_TYPE_LIVE
&&
3277 /* the stream comes from a file */
3278 /* try to open the file */
3280 if (av_open_input_file(&infile
, stream
->feed_filename
,
3281 NULL
, 0, NULL
) < 0) {
3282 http_log("%s not found", stream
->feed_filename
);
3283 /* remove stream (no need to spend more time on it) */
3285 remove_stream(stream
);