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
25 #include <sys/ioctl.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
38 #ifdef CONFIG_HAVE_DLFCN
44 /* maximum number of simultaneous HTTP connections */
45 #define HTTP_MAX_CONNECTIONS 2000
48 HTTPSTATE_WAIT_REQUEST
,
49 HTTPSTATE_SEND_HEADER
,
50 HTTPSTATE_SEND_DATA_HEADER
,
51 HTTPSTATE_SEND_DATA
, /* sending TCP or UDP data */
52 HTTPSTATE_SEND_DATA_TRAILER
,
53 HTTPSTATE_RECEIVE_DATA
,
54 HTTPSTATE_WAIT_FEED
, /* wait for data from the feed */
55 HTTPSTATE_WAIT
, /* wait before sending next packets */
56 HTTPSTATE_WAIT_SHORT
, /* short wait for short term
57 bandwidth limitation */
60 RTSPSTATE_WAIT_REQUEST
,
64 const char *http_state
[] = {
81 #define IOBUFFER_INIT_SIZE 8192
83 /* coef for exponential mean for bitrate estimation in statistics */
86 /* timeouts are in ms */
87 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
88 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
90 #define SYNC_TIMEOUT (10 * 1000)
97 /* context associated with one connection */
98 typedef struct HTTPContext
{
100 int fd
; /* socket file descriptor */
101 struct sockaddr_in from_addr
; /* origin */
102 struct pollfd
*poll_entry
; /* used when polling */
104 UINT8
*buffer_ptr
, *buffer_end
;
106 struct HTTPContext
*next
;
107 int got_key_frame
; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
111 /* input format handling */
112 AVFormatContext
*fmt_in
;
113 long start_time
; /* In milliseconds - this wraps fairly often */
114 INT64 first_pts
; /* initial pts value */
115 int pts_stream_index
; /* stream we choose as clock reference */
116 /* output format handling */
117 struct FFStream
*stream
;
118 /* -1 is invalid stream */
119 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
120 int switch_feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
122 AVFormatContext fmt_ctx
; /* instance of FFStream for one user */
123 int last_packet_sent
; /* true if last data packet was sent */
125 DataRateData datarate
;
132 int is_packetized
; /* if true, the stream is packetized */
133 int packet_stream_index
; /* current stream for output in state machine */
135 /* RTSP state specific */
136 UINT8
*pb_buffer
; /* XXX: use that in all the code */
138 int seq
; /* RTSP sequence number */
140 /* RTP state specific */
141 enum RTSPProtocol rtp_protocol
;
142 char session_id
[32]; /* session id */
143 AVFormatContext
*rtp_ctx
[MAX_STREAMS
];
144 URLContext
*rtp_handles
[MAX_STREAMS
];
145 /* RTP short term bandwidth limitation */
146 int packet_byte_count
;
147 int packet_start_time_us
; /* used for short durations (a few
151 static AVFrame dummy_frame
;
153 /* each generated stream is described here */
157 STREAM_TYPE_REDIRECT
,
160 enum IPAddressAction
{
165 typedef struct IPAddressACL
{
166 struct IPAddressACL
*next
;
167 enum IPAddressAction action
;
168 struct in_addr first
;
172 /* description of each stream of the ffserver.conf file */
173 typedef struct FFStream
{
174 enum StreamType stream_type
;
175 char filename
[1024]; /* stream filename */
176 struct FFStream
*feed
; /* feed we are using (can be null if
181 int prebuffer
; /* Number of millseconds early to start */
182 long max_time
; /* Number of milliseconds to run */
184 AVStream
*streams
[MAX_STREAMS
];
185 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
186 char feed_filename
[1024]; /* file name of the feed storage, or
187 input file name for a stream */
192 pid_t pid
; /* Of ffmpeg process */
193 time_t pid_start
; /* Of ffmpeg process */
195 struct FFStream
*next
;
196 int bandwidth
; /* bandwidth, in kbits/s */
199 /* multicast specific */
201 struct in_addr multicast_ip
;
202 int multicast_port
; /* first port used for multicast */
204 int loop
; /* if true, send the stream in loops (only meaningful if file) */
207 int feed_opened
; /* true if someone is writing to the feed */
208 int is_feed
; /* true if it is a feed */
211 INT64 feed_max_size
; /* maximum storage size */
212 INT64 feed_write_index
; /* current write position in feed (it wraps round) */
213 INT64 feed_size
; /* current size of feed */
214 struct FFStream
*next_feed
;
217 typedef struct FeedData
{
218 long long data_count
;
219 float avg_frame_size
; /* frame size averraged over last frames with exponential mean */
222 struct sockaddr_in my_http_addr
;
223 struct sockaddr_in my_rtsp_addr
;
225 char logfilename
[1024];
226 HTTPContext
*first_http_ctx
;
227 FFStream
*first_feed
; /* contains only feeds */
228 FFStream
*first_stream
; /* contains all streams, including feeds */
230 static void new_connection(int server_fd
, int is_rtsp
);
231 static void close_connection(HTTPContext
*c
);
234 static int handle_connection(HTTPContext
*c
);
235 static int http_parse_request(HTTPContext
*c
);
236 static int http_send_data(HTTPContext
*c
);
237 static void compute_stats(HTTPContext
*c
);
238 static int open_input_stream(HTTPContext
*c
, const char *info
);
239 static int http_start_receive_data(HTTPContext
*c
);
240 static int http_receive_data(HTTPContext
*c
);
241 static int compute_send_delay(HTTPContext
*c
);
244 static int rtsp_parse_request(HTTPContext
*c
);
245 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
);
246 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
247 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
248 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
249 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
);
252 static int prepare_sdp_description(FFStream
*stream
, UINT8
**pbuffer
,
253 struct in_addr my_ip
);
256 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
257 FFStream
*stream
, const char *session_id
);
258 static int rtp_new_av_stream(HTTPContext
*c
,
259 int stream_index
, struct sockaddr_in
*dest_addr
);
261 static const char *my_program_name
;
262 static const char *my_program_dir
;
264 static int ffserver_debug
;
265 static int ffserver_daemon
;
266 static int no_launch
;
267 static int need_to_start_children
;
269 int nb_max_connections
;
273 int current_bandwidth
;
275 static long cur_time
; // Making this global saves on passing it around everywhere
277 static long gettime_ms(void)
281 gettimeofday(&tv
,NULL
);
282 return (long long)tv
.tv_sec
* 1000 + (tv
.tv_usec
/ 1000);
285 static FILE *logfile
= NULL
;
287 static void http_log(char *fmt
, ...)
293 vfprintf(logfile
, fmt
, ap
);
299 static char *ctime1(char *buf2
)
307 p
= buf2
+ strlen(p
) - 1;
313 static void log_connection(HTTPContext
*c
)
320 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
321 inet_ntoa(c
->from_addr
.sin_addr
),
322 ctime1(buf2
), c
->method
, c
->url
,
323 c
->protocol
, (c
->http_error ? c
->http_error
: 200), c
->data_count
);
326 static void update_datarate(DataRateData
*drd
, INT64 count
)
328 if (!drd
->time1
&& !drd
->count1
) {
329 drd
->time1
= drd
->time2
= cur_time
;
330 drd
->count1
= drd
->count2
= count
;
332 if (cur_time
- drd
->time2
> 5000) {
333 drd
->time1
= drd
->time2
;
334 drd
->count1
= drd
->count2
;
335 drd
->time2
= cur_time
;
341 /* In bytes per second */
342 static int compute_datarate(DataRateData
*drd
, INT64 count
)
344 if (cur_time
== drd
->time1
)
347 return ((count
- drd
->count1
) * 1000) / (cur_time
- drd
->time1
);
350 static int get_longterm_datarate(DataRateData
*drd
, INT64 count
)
352 /* You get the first 3 seconds flat out */
353 if (cur_time
- drd
->time1
< 3000)
355 return compute_datarate(drd
, count
);
359 static void start_children(FFStream
*feed
)
364 for (; feed
; feed
= feed
->next
) {
365 if (feed
->child_argv
&& !feed
->pid
) {
366 feed
->pid_start
= time(0);
371 fprintf(stderr
, "Unable to create children\n");
380 for (i
= 3; i
< 256; i
++) {
384 if (!ffserver_debug
) {
385 i
= open("/dev/null", O_RDWR
);
394 pstrcpy(pathname
, sizeof(pathname
), my_program_name
);
396 slash
= strrchr(pathname
, '/');
402 strcpy(slash
, "ffmpeg");
404 /* This is needed to make relative pathnames work */
405 chdir(my_program_dir
);
407 signal(SIGPIPE
, SIG_DFL
);
409 execvp(pathname
, feed
->child_argv
);
417 /* open a listening socket */
418 static int socket_open_listen(struct sockaddr_in
*my_addr
)
422 server_fd
= socket(AF_INET
,SOCK_STREAM
,0);
429 setsockopt(server_fd
, SOL_SOCKET
, SO_REUSEADDR
, &tmp
, sizeof(tmp
));
431 if (bind (server_fd
, (struct sockaddr
*) my_addr
, sizeof (*my_addr
)) < 0) {
433 snprintf(bindmsg
, sizeof(bindmsg
), "bind(port %d)", ntohs(my_addr
->sin_port
));
439 if (listen (server_fd
, 5) < 0) {
444 fcntl(server_fd
, F_SETFL
, O_NONBLOCK
);
449 /* start all multicast streams */
450 static void start_multicast(void)
455 struct sockaddr_in dest_addr
;
456 int default_port
, stream_index
;
459 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
460 if (stream
->is_multicast
) {
461 /* open the RTP connection */
462 snprintf(session_id
, sizeof(session_id
),
463 "%08x%08x", (int)random(), (int)random());
465 /* choose a port if none given */
466 if (stream
->multicast_port
== 0) {
467 stream
->multicast_port
= default_port
;
471 dest_addr
.sin_family
= AF_INET
;
472 dest_addr
.sin_addr
= stream
->multicast_ip
;
473 dest_addr
.sin_port
= htons(stream
->multicast_port
);
475 rtp_c
= rtp_new_connection(&dest_addr
, stream
, session_id
);
479 if (open_input_stream(rtp_c
, "") < 0) {
480 fprintf(stderr
, "Could not open input stream for stream '%s'\n",
485 rtp_c
->rtp_protocol
= RTSP_PROTOCOL_RTP_UDP_MULTICAST
;
487 /* open each RTP stream */
488 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
490 dest_addr
.sin_port
= htons(stream
->multicast_port
+
492 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
) < 0) {
493 fprintf(stderr
, "Could not open output stream '%s/streamid=%d'\n",
494 stream
->filename
, stream_index
);
499 /* change state to send data */
500 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
505 /* main loop of the http server */
506 static int http_server(void)
508 int server_fd
, ret
, rtsp_server_fd
, delay
, delay1
;
509 struct pollfd poll_table
[HTTP_MAX_CONNECTIONS
+ 2], *poll_entry
;
510 HTTPContext
*c
, *c_next
;
512 server_fd
= socket_open_listen(&my_http_addr
);
516 rtsp_server_fd
= socket_open_listen(&my_rtsp_addr
);
517 if (rtsp_server_fd
< 0)
520 http_log("ffserver started.\n");
522 start_children(first_feed
);
524 first_http_ctx
= NULL
;
526 first_http_ctx
= NULL
;
531 poll_entry
= poll_table
;
532 poll_entry
->fd
= server_fd
;
533 poll_entry
->events
= POLLIN
;
536 poll_entry
->fd
= rtsp_server_fd
;
537 poll_entry
->events
= POLLIN
;
540 /* wait for events on each HTTP handle */
547 case HTTPSTATE_SEND_HEADER
:
548 case RTSPSTATE_SEND_REPLY
:
549 c
->poll_entry
= poll_entry
;
551 poll_entry
->events
= POLLOUT
;
554 case HTTPSTATE_SEND_DATA_HEADER
:
555 case HTTPSTATE_SEND_DATA
:
556 case HTTPSTATE_SEND_DATA_TRAILER
:
557 if (!c
->is_packetized
) {
558 /* for TCP, we output as much as we can (may need to put a limit) */
559 c
->poll_entry
= poll_entry
;
561 poll_entry
->events
= POLLOUT
;
564 /* not strictly correct, but currently cannot add
565 more than one fd in poll entry */
569 case HTTPSTATE_WAIT_REQUEST
:
570 case HTTPSTATE_RECEIVE_DATA
:
571 case HTTPSTATE_WAIT_FEED
:
572 case RTSPSTATE_WAIT_REQUEST
:
573 /* need to catch errors */
574 c
->poll_entry
= poll_entry
;
576 poll_entry
->events
= POLLIN
;/* Maybe this will work */
580 c
->poll_entry
= NULL
;
581 delay1
= compute_send_delay(c
);
585 case HTTPSTATE_WAIT_SHORT
:
586 c
->poll_entry
= NULL
;
587 delay1
= 10; /* one tick wait XXX: 10 ms assumed */
592 c
->poll_entry
= NULL
;
598 /* wait for an event on one connection. We poll at least every
599 second to handle timeouts */
601 ret
= poll(poll_table
, poll_entry
- poll_table
, delay
);
604 cur_time
= gettime_ms();
606 if (need_to_start_children
) {
607 need_to_start_children
= 0;
608 start_children(first_feed
);
611 /* now handle the events */
612 for(c
= first_http_ctx
; c
!= NULL
; c
= c_next
) {
614 if (handle_connection(c
) < 0) {
615 /* close and free the connection */
621 poll_entry
= poll_table
;
622 /* new HTTP connection request ? */
623 if (poll_entry
->revents
& POLLIN
) {
624 new_connection(server_fd
, 0);
627 /* new RTSP connection request ? */
628 if (poll_entry
->revents
& POLLIN
) {
629 new_connection(rtsp_server_fd
, 1);
634 /* start waiting for a new HTTP/RTSP request */
635 static void start_wait_request(HTTPContext
*c
, int is_rtsp
)
637 c
->buffer_ptr
= c
->buffer
;
638 c
->buffer_end
= c
->buffer
+ c
->buffer_size
- 1; /* leave room for '\0' */
641 c
->timeout
= cur_time
+ RTSP_REQUEST_TIMEOUT
;
642 c
->state
= RTSPSTATE_WAIT_REQUEST
;
644 c
->timeout
= cur_time
+ HTTP_REQUEST_TIMEOUT
;
645 c
->state
= HTTPSTATE_WAIT_REQUEST
;
649 static void new_connection(int server_fd
, int is_rtsp
)
651 struct sockaddr_in from_addr
;
653 HTTPContext
*c
= NULL
;
655 len
= sizeof(from_addr
);
656 fd
= accept(server_fd
, (struct sockaddr
*)&from_addr
,
660 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
662 /* XXX: should output a warning page when coming
663 close to the connection limit */
664 if (nb_connections
>= nb_max_connections
)
667 /* add a new connection */
668 c
= av_mallocz(sizeof(HTTPContext
));
672 c
->next
= first_http_ctx
;
675 c
->poll_entry
= NULL
;
676 c
->from_addr
= from_addr
;
677 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
678 c
->buffer
= av_malloc(c
->buffer_size
);
683 start_wait_request(c
, is_rtsp
);
695 static void close_connection(HTTPContext
*c
)
697 HTTPContext
**cp
, *c1
;
699 AVFormatContext
*ctx
;
703 /* remove connection from list */
704 cp
= &first_http_ctx
;
705 while ((*cp
) != NULL
) {
714 /* remove connection associated resources */
718 /* close each frame parser */
719 for(i
=0;i
<c
->fmt_in
->nb_streams
;i
++) {
720 st
= c
->fmt_in
->streams
[i
];
721 if (st
->codec
.codec
) {
722 avcodec_close(&st
->codec
);
725 av_close_input_file(c
->fmt_in
);
728 /* free RTP output streams if any */
731 nb_streams
= c
->stream
->nb_streams
;
733 for(i
=0;i
<nb_streams
;i
++) {
736 av_write_trailer(ctx
);
739 h
= c
->rtp_handles
[i
];
746 current_bandwidth
-= c
->stream
->bandwidth
;
747 av_freep(&c
->pb_buffer
);
753 static int handle_connection(HTTPContext
*c
)
758 case HTTPSTATE_WAIT_REQUEST
:
759 case RTSPSTATE_WAIT_REQUEST
:
761 if ((c
->timeout
- cur_time
) < 0)
763 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
766 /* no need to read if no events */
767 if (!(c
->poll_entry
->revents
& POLLIN
))
770 len
= read(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
772 if (errno
!= EAGAIN
&& errno
!= EINTR
)
774 } else if (len
== 0) {
777 /* search for end of request. XXX: not fully correct since garbage could come after the end */
779 c
->buffer_ptr
+= len
;
781 if ((ptr
>= c
->buffer
+ 2 && !memcmp(ptr
-2, "\n\n", 2)) ||
782 (ptr
>= c
->buffer
+ 4 && !memcmp(ptr
-4, "\r\n\r\n", 4))) {
783 /* request found : parse it and reply */
784 if (c
->state
== HTTPSTATE_WAIT_REQUEST
) {
785 ret
= http_parse_request(c
);
787 ret
= rtsp_parse_request(c
);
791 } else if (ptr
>= c
->buffer_end
) {
792 /* request too long: cannot do anything */
798 case HTTPSTATE_SEND_HEADER
:
799 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
802 /* no need to write if no events */
803 if (!(c
->poll_entry
->revents
& POLLOUT
))
805 len
= write(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
807 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
808 /* error : close connection */
809 av_freep(&c
->pb_buffer
);
813 c
->buffer_ptr
+= len
;
815 c
->stream
->bytes_served
+= len
;
816 c
->data_count
+= len
;
817 if (c
->buffer_ptr
>= c
->buffer_end
) {
818 av_freep(&c
->pb_buffer
);
823 /* all the buffer was sent : synchronize to the incoming stream */
824 c
->state
= HTTPSTATE_SEND_DATA_HEADER
;
825 c
->buffer_ptr
= c
->buffer_end
= c
->buffer
;
830 case HTTPSTATE_SEND_DATA
:
831 case HTTPSTATE_SEND_DATA_HEADER
:
832 case HTTPSTATE_SEND_DATA_TRAILER
:
833 /* for packetized output, we consider we can always write (the
834 input streams sets the speed). It may be better to verify
835 that we do not rely too much on the kernel queues */
836 if (!c
->is_packetized
) {
837 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
840 /* no need to read if no events */
841 if (!(c
->poll_entry
->revents
& POLLOUT
))
844 if (http_send_data(c
) < 0)
847 case HTTPSTATE_RECEIVE_DATA
:
848 /* no need to read if no events */
849 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
851 if (!(c
->poll_entry
->revents
& POLLIN
))
853 if (http_receive_data(c
) < 0)
856 case HTTPSTATE_WAIT_FEED
:
857 /* no need to read if no events */
858 if (c
->poll_entry
->revents
& (POLLIN
| POLLERR
| POLLHUP
))
861 /* nothing to do, we'll be waken up by incoming feed packets */
865 /* if the delay expired, we can send new packets */
866 if (compute_send_delay(c
) <= 0)
867 c
->state
= HTTPSTATE_SEND_DATA
;
869 case HTTPSTATE_WAIT_SHORT
:
870 /* just return back to send data */
871 c
->state
= HTTPSTATE_SEND_DATA
;
874 case RTSPSTATE_SEND_REPLY
:
875 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
)) {
876 av_freep(&c
->pb_buffer
);
879 /* no need to write if no events */
880 if (!(c
->poll_entry
->revents
& POLLOUT
))
882 len
= write(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
884 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
885 /* error : close connection */
886 av_freep(&c
->pb_buffer
);
890 c
->buffer_ptr
+= len
;
891 c
->data_count
+= len
;
892 if (c
->buffer_ptr
>= c
->buffer_end
) {
893 /* all the buffer was sent : wait for a new request */
894 av_freep(&c
->pb_buffer
);
895 start_wait_request(c
, 1);
899 case HTTPSTATE_READY
:
908 static int extract_rates(char *rates
, int ratelen
, const char *request
)
912 for (p
= request
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
913 if (strncasecmp(p
, "Pragma:", 7) == 0) {
914 const char *q
= p
+ 7;
916 while (*q
&& *q
!= '\n' && isspace(*q
))
919 if (strncasecmp(q
, "stream-switch-entry=", 20) == 0) {
925 memset(rates
, 0xff, ratelen
);
928 while (*q
&& *q
!= '\n' && *q
!= ':')
931 if (sscanf(q
, ":%d:%d", &stream_no
, &rate_no
) != 2) {
935 if (stream_no
< ratelen
&& stream_no
>= 0) {
936 rates
[stream_no
] = rate_no
;
939 while (*q
&& *q
!= '\n' && !isspace(*q
))
956 static int find_stream_in_feed(FFStream
*feed
, AVCodecContext
*codec
, int bit_rate
)
959 int best_bitrate
= 100000000;
962 for (i
= 0; i
< feed
->nb_streams
; i
++) {
963 AVCodecContext
*feed_codec
= &feed
->streams
[i
]->codec
;
965 if (feed_codec
->codec_id
!= codec
->codec_id
||
966 feed_codec
->sample_rate
!= codec
->sample_rate
||
967 feed_codec
->width
!= codec
->width
||
968 feed_codec
->height
!= codec
->height
) {
972 /* Potential stream */
974 /* We want the fastest stream less than bit_rate, or the slowest
975 * faster than bit_rate
978 if (feed_codec
->bit_rate
<= bit_rate
) {
979 if (best_bitrate
> bit_rate
|| feed_codec
->bit_rate
> best_bitrate
) {
980 best_bitrate
= feed_codec
->bit_rate
;
984 if (feed_codec
->bit_rate
< best_bitrate
) {
985 best_bitrate
= feed_codec
->bit_rate
;
994 static int modify_current_stream(HTTPContext
*c
, char *rates
)
997 FFStream
*req
= c
->stream
;
998 int action_required
= 0;
1000 /* Not much we can do for a feed */
1004 for (i
= 0; i
< req
->nb_streams
; i
++) {
1005 AVCodecContext
*codec
= &req
->streams
[i
]->codec
;
1009 c
->switch_feed_streams
[i
] = req
->feed_streams
[i
];
1012 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 2);
1015 /* Wants off or slow */
1016 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 4);
1018 /* This doesn't work well when it turns off the only stream! */
1019 c
->switch_feed_streams
[i
] = -2;
1020 c
->feed_streams
[i
] = -2;
1025 if (c
->switch_feed_streams
[i
] >= 0 && c
->switch_feed_streams
[i
] != c
->feed_streams
[i
])
1026 action_required
= 1;
1029 return action_required
;
1033 static void do_switch_stream(HTTPContext
*c
, int i
)
1035 if (c
->switch_feed_streams
[i
] >= 0) {
1037 c
->feed_streams
[i
] = c
->switch_feed_streams
[i
];
1040 /* Now update the stream */
1042 c
->switch_feed_streams
[i
] = -1;
1045 /* XXX: factorize in utils.c ? */
1046 /* XXX: take care with different space meaning */
1047 static void skip_spaces(const char **pp
)
1051 while (*p
== ' ' || *p
== '\t')
1056 static void get_word(char *buf
, int buf_size
, const char **pp
)
1064 while (!isspace(*p
) && *p
!= '\0') {
1065 if ((q
- buf
) < buf_size
- 1)
1074 static int validate_acl(FFStream
*stream
, HTTPContext
*c
)
1076 enum IPAddressAction last_action
= IP_DENY
;
1078 struct in_addr
*src
= &c
->from_addr
.sin_addr
;
1080 for (acl
= stream
->acl
; acl
; acl
= acl
->next
) {
1081 if (src
->s_addr
>= acl
->first
.s_addr
&& src
->s_addr
<= acl
->last
.s_addr
) {
1082 return (acl
->action
== IP_ALLOW
) ?
1 : 0;
1084 last_action
= acl
->action
;
1087 /* Nothing matched, so return not the last action */
1088 return (last_action
== IP_DENY
) ?
1 : 0;
1091 /* compute the real filename of a file by matching it without its
1092 extensions to all the stream filenames */
1093 static void compute_real_filename(char *filename
, int max_size
)
1100 /* compute filename by matching without the file extensions */
1101 pstrcpy(file1
, sizeof(file1
), filename
);
1102 p
= strrchr(file1
, '.');
1105 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
1106 pstrcpy(file2
, sizeof(file2
), stream
->filename
);
1107 p
= strrchr(file2
, '.');
1110 if (!strcmp(file1
, file2
)) {
1111 pstrcpy(filename
, max_size
, stream
->filename
);
1126 /* parse http request and prepare header */
1127 static int http_parse_request(HTTPContext
*c
)
1131 enum RedirType redir_type
;
1133 char info
[1024], *filename
;
1137 const char *mime_type
;
1141 char *useragent
= 0;
1144 get_word(cmd
, sizeof(cmd
), (const char **)&p
);
1145 pstrcpy(c
->method
, sizeof(c
->method
), cmd
);
1147 if (!strcmp(cmd
, "GET"))
1149 else if (!strcmp(cmd
, "POST"))
1154 get_word(url
, sizeof(url
), (const char **)&p
);
1155 pstrcpy(c
->url
, sizeof(c
->url
), url
);
1157 get_word(protocol
, sizeof(protocol
), (const char **)&p
);
1158 if (strcmp(protocol
, "HTTP/1.0") && strcmp(protocol
, "HTTP/1.1"))
1161 pstrcpy(c
->protocol
, sizeof(c
->protocol
), protocol
);
1163 /* find the filename and the optional info string in the request */
1170 pstrcpy(info
, sizeof(info
), p
);
1176 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1177 if (strncasecmp(p
, "User-Agent:", 11) == 0) {
1179 if (*useragent
&& *useragent
!= '\n' && isspace(*useragent
))
1183 p
= strchr(p
, '\n');
1190 redir_type
= REDIR_NONE
;
1191 if (match_ext(filename
, "asx")) {
1192 redir_type
= REDIR_ASX
;
1193 filename
[strlen(filename
)-1] = 'f';
1194 } else if (match_ext(filename
, "asf") &&
1195 (!useragent
|| strncasecmp(useragent
, "NSPlayer", 8) != 0)) {
1196 /* if this isn't WMP or lookalike, return the redirector file */
1197 redir_type
= REDIR_ASF
;
1198 } else if (match_ext(filename
, "rpm,ram")) {
1199 redir_type
= REDIR_RAM
;
1200 strcpy(filename
+ strlen(filename
)-2, "m");
1201 } else if (match_ext(filename
, "rtsp")) {
1202 redir_type
= REDIR_RTSP
;
1203 compute_real_filename(filename
, sizeof(url
) - 1);
1204 } else if (match_ext(filename
, "sdp")) {
1205 redir_type
= REDIR_SDP
;
1206 compute_real_filename(filename
, sizeof(url
) - 1);
1209 stream
= first_stream
;
1210 while (stream
!= NULL
) {
1211 if (!strcmp(stream
->filename
, filename
) && validate_acl(stream
, c
))
1213 stream
= stream
->next
;
1215 if (stream
== NULL
) {
1216 sprintf(msg
, "File '%s' not found", url
);
1221 memcpy(c
->feed_streams
, stream
->feed_streams
, sizeof(c
->feed_streams
));
1222 memset(c
->switch_feed_streams
, -1, sizeof(c
->switch_feed_streams
));
1224 if (stream
->stream_type
== STREAM_TYPE_REDIRECT
) {
1225 c
->http_error
= 301;
1227 q
+= sprintf(q
, "HTTP/1.0 301 Moved\r\n");
1228 q
+= sprintf(q
, "Location: %s\r\n", stream
->feed_filename
);
1229 q
+= sprintf(q
, "Content-type: text/html\r\n");
1230 q
+= sprintf(q
, "\r\n");
1231 q
+= sprintf(q
, "<html><head><title>Moved</title></head><body>\r\n");
1232 q
+= sprintf(q
, "You should be <a href=\"%s\">redirected</a>.\r\n", stream
->feed_filename
);
1233 q
+= sprintf(q
, "</body></html>\r\n");
1235 /* prepare output buffer */
1236 c
->buffer_ptr
= c
->buffer
;
1238 c
->state
= HTTPSTATE_SEND_HEADER
;
1242 /* If this is WMP, get the rate information */
1243 if (extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1244 if (modify_current_stream(c
, ratebuf
)) {
1245 for (i
= 0; i
< sizeof(c
->feed_streams
) / sizeof(c
->feed_streams
[0]); i
++) {
1246 if (c
->switch_feed_streams
[i
] >= 0)
1247 do_switch_stream(c
, i
);
1252 if (post
== 0 && stream
->stream_type
== STREAM_TYPE_LIVE
) {
1253 current_bandwidth
+= stream
->bandwidth
;
1256 if (post
== 0 && max_bandwidth
< current_bandwidth
) {
1257 c
->http_error
= 200;
1259 q
+= sprintf(q
, "HTTP/1.0 200 Server too busy\r\n");
1260 q
+= sprintf(q
, "Content-type: text/html\r\n");
1261 q
+= sprintf(q
, "\r\n");
1262 q
+= sprintf(q
, "<html><head><title>Too busy</title></head><body>\r\n");
1263 q
+= sprintf(q
, "The server is too busy to serve your request at this time.<p>\r\n");
1264 q
+= sprintf(q
, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
1265 current_bandwidth
, max_bandwidth
);
1266 q
+= sprintf(q
, "</body></html>\r\n");
1268 /* prepare output buffer */
1269 c
->buffer_ptr
= c
->buffer
;
1271 c
->state
= HTTPSTATE_SEND_HEADER
;
1275 if (redir_type
!= REDIR_NONE
) {
1278 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1279 if (strncasecmp(p
, "Host:", 5) == 0) {
1283 p
= strchr(p
, '\n');
1294 while (isspace(*hostinfo
))
1297 eoh
= strchr(hostinfo
, '\n');
1299 if (eoh
[-1] == '\r')
1302 if (eoh
- hostinfo
< sizeof(hostbuf
) - 1) {
1303 memcpy(hostbuf
, hostinfo
, eoh
- hostinfo
);
1304 hostbuf
[eoh
- hostinfo
] = 0;
1306 c
->http_error
= 200;
1308 switch(redir_type
) {
1310 q
+= sprintf(q
, "HTTP/1.0 200 ASX Follows\r\n");
1311 q
+= sprintf(q
, "Content-type: video/x-ms-asf\r\n");
1312 q
+= sprintf(q
, "\r\n");
1313 q
+= sprintf(q
, "<ASX Version=\"3\">\r\n");
1314 q
+= sprintf(q
, "<!-- Autogenerated by ffserver -->\r\n");
1315 q
+= sprintf(q
, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1316 hostbuf
, filename
, info
);
1317 q
+= sprintf(q
, "</ASX>\r\n");
1320 q
+= sprintf(q
, "HTTP/1.0 200 RAM Follows\r\n");
1321 q
+= sprintf(q
, "Content-type: audio/x-pn-realaudio\r\n");
1322 q
+= sprintf(q
, "\r\n");
1323 q
+= sprintf(q
, "# Autogenerated by ffserver\r\n");
1324 q
+= sprintf(q
, "http://%s/%s%s\r\n",
1325 hostbuf
, filename
, info
);
1328 q
+= sprintf(q
, "HTTP/1.0 200 ASF Redirect follows\r\n");
1329 q
+= sprintf(q
, "Content-type: video/x-ms-asf\r\n");
1330 q
+= sprintf(q
, "\r\n");
1331 q
+= sprintf(q
, "[Reference]\r\n");
1332 q
+= sprintf(q
, "Ref1=http://%s/%s%s\r\n",
1333 hostbuf
, filename
, info
);
1337 char hostname
[256], *p
;
1338 /* extract only hostname */
1339 pstrcpy(hostname
, sizeof(hostname
), hostbuf
);
1340 p
= strrchr(hostname
, ':');
1343 q
+= sprintf(q
, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1344 /* XXX: incorrect mime type ? */
1345 q
+= sprintf(q
, "Content-type: application/x-rtsp\r\n");
1346 q
+= sprintf(q
, "\r\n");
1347 q
+= sprintf(q
, "rtsp://%s:%d/%s\r\n",
1348 hostname
, ntohs(my_rtsp_addr
.sin_port
),
1355 int sdp_data_size
, len
;
1356 struct sockaddr_in my_addr
;
1358 q
+= sprintf(q
, "HTTP/1.0 200 OK\r\n");
1359 q
+= sprintf(q
, "Content-type: application/sdp\r\n");
1360 q
+= sprintf(q
, "\r\n");
1362 len
= sizeof(my_addr
);
1363 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
1365 /* XXX: should use a dynamic buffer */
1366 sdp_data_size
= prepare_sdp_description(stream
,
1369 if (sdp_data_size
> 0) {
1370 memcpy(q
, sdp_data
, sdp_data_size
);
1382 /* prepare output buffer */
1383 c
->buffer_ptr
= c
->buffer
;
1385 c
->state
= HTTPSTATE_SEND_HEADER
;
1391 sprintf(msg
, "ASX/RAM file not handled");
1395 stream
->conns_served
++;
1397 /* XXX: add there authenticate and IP match */
1400 /* if post, it means a feed is being sent */
1401 if (!stream
->is_feed
) {
1402 /* However it might be a status report from WMP! Lets log the data
1403 * as it might come in handy one day
1408 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
1409 if (strncasecmp(p
, "Pragma: log-line=", 17) == 0) {
1413 if (strncasecmp(p
, "Pragma: client-id=", 18) == 0) {
1414 client_id
= strtol(p
+ 18, 0, 10);
1416 p
= strchr(p
, '\n');
1424 char *eol
= strchr(logline
, '\n');
1429 if (eol
[-1] == '\r')
1431 http_log("%.*s\n", eol
- logline
, logline
);
1432 c
->suppress_log
= 1;
1437 http_log("\nGot request:\n%s\n", c
->buffer
);
1440 if (client_id
&& extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1443 /* Now we have to find the client_id */
1444 for (wmpc
= first_http_ctx
; wmpc
; wmpc
= wmpc
->next
) {
1445 if (wmpc
->wmp_client_id
== client_id
)
1450 if (modify_current_stream(wmpc
, ratebuf
)) {
1451 wmpc
->switch_pending
= 1;
1456 sprintf(msg
, "POST command not handled");
1459 if (http_start_receive_data(c
) < 0) {
1460 sprintf(msg
, "could not open feed");
1464 c
->state
= HTTPSTATE_RECEIVE_DATA
;
1469 if (strcmp(stream
->filename
+ strlen(stream
->filename
) - 4, ".asf") == 0) {
1470 http_log("\nGot request:\n%s\n", c
->buffer
);
1474 if (c
->stream
->stream_type
== STREAM_TYPE_STATUS
)
1477 /* open input stream */
1478 if (open_input_stream(c
, info
) < 0) {
1479 sprintf(msg
, "Input stream corresponding to '%s' not found", url
);
1483 /* prepare http header */
1485 q
+= sprintf(q
, "HTTP/1.0 200 OK\r\n");
1486 mime_type
= c
->stream
->fmt
->mime_type
;
1488 mime_type
= "application/x-octet_stream";
1489 q
+= sprintf(q
, "Pragma: no-cache\r\n");
1491 /* for asf, we need extra headers */
1492 if (!strcmp(c
->stream
->fmt
->name
,"asf_stream")) {
1493 /* Need to allocate a client id */
1495 c
->wmp_client_id
= random() & 0x7fffffff;
1497 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
);
1499 q
+= sprintf(q
, "Content-Type: %s\r\n", mime_type
);
1500 q
+= sprintf(q
, "\r\n");
1502 /* prepare output buffer */
1504 c
->buffer_ptr
= c
->buffer
;
1506 c
->state
= HTTPSTATE_SEND_HEADER
;
1509 c
->http_error
= 404;
1511 q
+= sprintf(q
, "HTTP/1.0 404 Not Found\r\n");
1512 q
+= sprintf(q
, "Content-type: %s\r\n", "text/html");
1513 q
+= sprintf(q
, "\r\n");
1514 q
+= sprintf(q
, "<HTML>\n");
1515 q
+= sprintf(q
, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1516 q
+= sprintf(q
, "<BODY>%s</BODY>\n", msg
);
1517 q
+= sprintf(q
, "</HTML>\n");
1519 /* prepare output buffer */
1520 c
->buffer_ptr
= c
->buffer
;
1522 c
->state
= HTTPSTATE_SEND_HEADER
;
1526 c
->http_error
= 200; /* horrible : we use this value to avoid
1527 going to the send data state */
1528 c
->state
= HTTPSTATE_SEND_HEADER
;
1532 static void fmt_bytecount(ByteIOContext
*pb
, INT64 count
)
1534 static const char *suffix
= " kMGTP";
1537 for (s
= suffix
; count
>= 100000 && s
[1]; count
/= 1000, s
++) {
1540 url_fprintf(pb
, "%lld%c", count
, *s
);
1543 static void compute_stats(HTTPContext
*c
)
1550 ByteIOContext pb1
, *pb
= &pb1
;
1552 if (url_open_dyn_buf(pb
) < 0) {
1553 /* XXX: return an error ? */
1554 c
->buffer_ptr
= c
->buffer
;
1555 c
->buffer_end
= c
->buffer
;
1559 url_fprintf(pb
, "HTTP/1.0 200 OK\r\n");
1560 url_fprintf(pb
, "Content-type: %s\r\n", "text/html");
1561 url_fprintf(pb
, "Pragma: no-cache\r\n");
1562 url_fprintf(pb
, "\r\n");
1564 url_fprintf(pb
, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1565 if (c
->stream
->feed_filename
) {
1566 url_fprintf(pb
, "<link rel=\"shortcut icon\" href=\"%s\">\n", c
->stream
->feed_filename
);
1568 url_fprintf(pb
, "</HEAD>\n<BODY>");
1569 url_fprintf(pb
, "<H1>FFServer Status</H1>\n");
1571 url_fprintf(pb
, "<H2>Available Streams</H2>\n");
1572 url_fprintf(pb
, "<TABLE cellspacing=0 cellpadding=4>\n");
1573 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");
1574 stream
= first_stream
;
1575 while (stream
!= NULL
) {
1576 char sfilename
[1024];
1579 if (stream
->feed
!= stream
) {
1580 pstrcpy(sfilename
, sizeof(sfilename
) - 10, stream
->filename
);
1581 eosf
= sfilename
+ strlen(sfilename
);
1582 if (eosf
- sfilename
>= 4) {
1583 if (strcmp(eosf
- 4, ".asf") == 0) {
1584 strcpy(eosf
- 4, ".asx");
1585 } else if (strcmp(eosf
- 3, ".rm") == 0) {
1586 strcpy(eosf
- 3, ".ram");
1587 } else if (stream
->fmt
== &rtp_mux
) {
1588 /* generate a sample RTSP director if
1589 unicast. Generate an SDP redirector if
1591 eosf
= strrchr(sfilename
, '.');
1593 eosf
= sfilename
+ strlen(sfilename
);
1594 if (stream
->is_multicast
)
1595 strcpy(eosf
, ".sdp");
1597 strcpy(eosf
, ".rtsp");
1601 url_fprintf(pb
, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1602 sfilename
, stream
->filename
);
1603 url_fprintf(pb
, "<td align=right> %d <td align=right> ",
1604 stream
->conns_served
);
1605 fmt_bytecount(pb
, stream
->bytes_served
);
1606 switch(stream
->stream_type
) {
1607 case STREAM_TYPE_LIVE
:
1609 int audio_bit_rate
= 0;
1610 int video_bit_rate
= 0;
1611 const char *audio_codec_name
= "";
1612 const char *video_codec_name
= "";
1613 const char *audio_codec_name_extra
= "";
1614 const char *video_codec_name_extra
= "";
1616 for(i
=0;i
<stream
->nb_streams
;i
++) {
1617 AVStream
*st
= stream
->streams
[i
];
1618 AVCodec
*codec
= avcodec_find_encoder(st
->codec
.codec_id
);
1619 switch(st
->codec
.codec_type
) {
1620 case CODEC_TYPE_AUDIO
:
1621 audio_bit_rate
+= st
->codec
.bit_rate
;
1623 if (*audio_codec_name
)
1624 audio_codec_name_extra
= "...";
1625 audio_codec_name
= codec
->name
;
1628 case CODEC_TYPE_VIDEO
:
1629 video_bit_rate
+= st
->codec
.bit_rate
;
1631 if (*video_codec_name
)
1632 video_codec_name_extra
= "...";
1633 video_codec_name
= codec
->name
;
1640 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",
1643 video_bit_rate
/ 1000, video_codec_name
, video_codec_name_extra
,
1644 audio_bit_rate
/ 1000, audio_codec_name
, audio_codec_name_extra
);
1646 url_fprintf(pb
, "<TD>%s", stream
->feed
->filename
);
1648 url_fprintf(pb
, "<TD>%s", stream
->feed_filename
);
1650 url_fprintf(pb
, "\n");
1654 url_fprintf(pb
, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1658 stream
= stream
->next
;
1660 url_fprintf(pb
, "</TABLE>\n");
1662 stream
= first_stream
;
1663 while (stream
!= NULL
) {
1664 if (stream
->feed
== stream
) {
1665 url_fprintf(pb
, "<h2>Feed %s</h2>", stream
->filename
);
1667 url_fprintf(pb
, "Running as pid %d.\n", stream
->pid
);
1669 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1674 /* This is somewhat linux specific I guess */
1675 snprintf(ps_cmd
, sizeof(ps_cmd
),
1676 "ps -o \"%%cpu,cputime\" --no-headers %d",
1679 pid_stat
= popen(ps_cmd
, "r");
1684 if (fscanf(pid_stat
, "%10s %64s", cpuperc
,
1686 url_fprintf(pb
, "Currently using %s%% of the cpu. Total time used %s.\n",
1694 url_fprintf(pb
, "<p>");
1696 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");
1698 for (i
= 0; i
< stream
->nb_streams
; i
++) {
1699 AVStream
*st
= stream
->streams
[i
];
1700 AVCodec
*codec
= avcodec_find_encoder(st
->codec
.codec_id
);
1701 char *type
= "unknown";
1702 char parameters
[64];
1706 switch(st
->codec
.codec_type
) {
1707 case CODEC_TYPE_AUDIO
:
1710 case CODEC_TYPE_VIDEO
:
1712 sprintf(parameters
, "%dx%d, q=%d-%d, fps=%d", st
->codec
.width
, st
->codec
.height
,
1713 st
->codec
.qmin
, st
->codec
.qmax
, st
->codec
.frame_rate
/ FRAME_RATE_BASE
);
1718 url_fprintf(pb
, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1719 i
, type
, st
->codec
.bit_rate
/1000, codec ? codec
->name
: "", parameters
);
1721 url_fprintf(pb
, "</table>\n");
1724 stream
= stream
->next
;
1730 AVCodecContext
*enc
;
1734 stream
= first_feed
;
1735 while (stream
!= NULL
) {
1736 url_fprintf(pb
, "<H1>Feed '%s'</H1>\n", stream
->filename
);
1737 url_fprintf(pb
, "<TABLE>\n");
1738 url_fprintf(pb
, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1739 for(i
=0;i
<stream
->nb_streams
;i
++) {
1740 AVStream
*st
= stream
->streams
[i
];
1741 FeedData
*fdata
= st
->priv_data
;
1744 avcodec_string(buf
, sizeof(buf
), enc
);
1745 avg
= fdata
->avg_frame_size
* (float)enc
->rate
* 8.0;
1746 if (enc
->codec
->type
== CODEC_TYPE_AUDIO
&& enc
->frame_size
> 0)
1747 avg
/= enc
->frame_size
;
1748 url_fprintf(pb
, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1749 buf
, enc
->frame_number
, fdata
->data_count
, avg
/ 1000.0);
1751 url_fprintf(pb
, "</TABLE>\n");
1752 stream
= stream
->next_feed
;
1757 /* connection status */
1758 url_fprintf(pb
, "<H2>Connection Status</H2>\n");
1760 url_fprintf(pb
, "Number of connections: %d / %d<BR>\n",
1761 nb_connections
, nb_max_connections
);
1763 url_fprintf(pb
, "Bandwidth in use: %dk / %dk<BR>\n",
1764 current_bandwidth
, max_bandwidth
);
1766 url_fprintf(pb
, "<TABLE>\n");
1767 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");
1768 c1
= first_http_ctx
;
1770 while (c1
!= NULL
) {
1776 for (j
= 0; j
< c1
->stream
->nb_streams
; j
++) {
1777 if (!c1
->stream
->feed
) {
1778 bitrate
+= c1
->stream
->streams
[j
]->codec
.bit_rate
;
1780 if (c1
->feed_streams
[j
] >= 0) {
1781 bitrate
+= c1
->stream
->feed
->streams
[c1
->feed_streams
[j
]]->codec
.bit_rate
;
1788 p
= inet_ntoa(c1
->from_addr
.sin_addr
);
1789 url_fprintf(pb
, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1791 c1
->stream ? c1
->stream
->filename
: "",
1792 c1
->state
== HTTPSTATE_RECEIVE_DATA ?
"(input)" : "",
1795 http_state
[c1
->state
]);
1796 fmt_bytecount(pb
, bitrate
);
1797 url_fprintf(pb
, "<td align=right>");
1798 fmt_bytecount(pb
, compute_datarate(&c1
->datarate
, c1
->data_count
) * 8);
1799 url_fprintf(pb
, "<td align=right>");
1800 fmt_bytecount(pb
, c1
->data_count
);
1801 url_fprintf(pb
, "\n");
1804 url_fprintf(pb
, "</TABLE>\n");
1809 url_fprintf(pb
, "<HR size=1 noshade>Generated at %s", p
);
1810 url_fprintf(pb
, "</BODY>\n</HTML>\n");
1812 len
= url_close_dyn_buf(pb
, &c
->pb_buffer
);
1813 c
->buffer_ptr
= c
->pb_buffer
;
1814 c
->buffer_end
= c
->pb_buffer
+ len
;
1817 /* check if the parser needs to be opened for stream i */
1818 static void open_parser(AVFormatContext
*s
, int i
)
1820 AVStream
*st
= s
->streams
[i
];
1823 if (!st
->codec
.codec
) {
1824 codec
= avcodec_find_decoder(st
->codec
.codec_id
);
1825 if (codec
&& (codec
->capabilities
& CODEC_CAP_PARSE_ONLY
)) {
1826 st
->codec
.parse_only
= 1;
1827 if (avcodec_open(&st
->codec
, codec
) < 0) {
1828 st
->codec
.parse_only
= 0;
1834 static int open_input_stream(HTTPContext
*c
, const char *info
)
1837 char input_filename
[1024];
1842 /* find file name */
1843 if (c
->stream
->feed
) {
1844 strcpy(input_filename
, c
->stream
->feed
->feed_filename
);
1845 buf_size
= FFM_PACKET_SIZE
;
1846 /* compute position (absolute time) */
1847 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1848 stream_pos
= parse_date(buf
, 0);
1849 } else if (find_info_tag(buf
, sizeof(buf
), "buffer", info
)) {
1850 int prebuffer
= strtol(buf
, 0, 10);
1851 stream_pos
= av_gettime() - prebuffer
* (INT64
)1000000;
1853 stream_pos
= av_gettime() - c
->stream
->prebuffer
* (INT64
)1000;
1856 strcpy(input_filename
, c
->stream
->feed_filename
);
1858 /* compute position (relative time) */
1859 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1860 stream_pos
= parse_date(buf
, 1);
1865 if (input_filename
[0] == '\0')
1869 { time_t when
= stream_pos
/ 1000000;
1870 http_log("Stream pos = %lld, time=%s", stream_pos
, ctime(&when
));
1875 if (av_open_input_file(&s
, input_filename
, NULL
, buf_size
, NULL
) < 0) {
1876 http_log("%s not found", input_filename
);
1881 /* open each parser */
1882 for(i
=0;i
<s
->nb_streams
;i
++)
1885 /* choose stream as clock source (we favorize video stream if
1886 present) for packet sending */
1887 c
->pts_stream_index
= 0;
1888 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
1889 if (c
->pts_stream_index
== 0 &&
1890 c
->stream
->streams
[i
]->codec
.codec_type
== CODEC_TYPE_VIDEO
) {
1891 c
->pts_stream_index
= i
;
1895 if (c
->fmt_in
->iformat
->read_seek
) {
1896 c
->fmt_in
->iformat
->read_seek(c
->fmt_in
, stream_pos
);
1898 /* set the start time (needed for maxtime and RTP packet timing) */
1899 c
->start_time
= cur_time
;
1900 c
->first_pts
= AV_NOPTS_VALUE
;
1904 /* currently desactivated because the new PTS handling is not
1906 //#define AV_READ_FRAME
1907 #ifdef AV_READ_FRAME
1909 /* XXX: generalize that in ffmpeg for picture/audio/data. Currently
1910 the return packet MUST NOT be freed */
1911 int av_read_frame(AVFormatContext
*s
, AVPacket
*pkt
)
1914 int len
, ret
, old_nb_streams
, i
;
1916 /* see if remaining frames must be parsed */
1918 if (s
->cur_len
> 0) {
1919 st
= s
->streams
[s
->cur_pkt
.stream_index
];
1920 len
= avcodec_parse_frame(&st
->codec
, &pkt
->data
, &pkt
->size
,
1921 s
->cur_ptr
, s
->cur_len
);
1923 /* error: get next packet */
1929 /* init pts counter if not done */
1930 if (st
->pts
.den
== 0) {
1931 switch(st
->codec
.codec_type
) {
1932 case CODEC_TYPE_AUDIO
:
1933 st
->pts_incr
= (INT64
)s
->pts_den
;
1934 av_frac_init(&st
->pts
, st
->pts
.val
, 0,
1935 (INT64
)s
->pts_num
* st
->codec
.sample_rate
);
1937 case CODEC_TYPE_VIDEO
:
1938 st
->pts_incr
= (INT64
)s
->pts_den
* FRAME_RATE_BASE
;
1939 av_frac_init(&st
->pts
, st
->pts
.val
, 0,
1940 (INT64
)s
->pts_num
* st
->codec
.frame_rate
);
1947 /* a frame was read: return it */
1948 pkt
->pts
= st
->pts
.val
;
1950 printf("add pts=%Lx num=%Lx den=%Lx incr=%Lx\n",
1951 st
->pts
.val
, st
->pts
.num
, st
->pts
.den
, st
->pts_incr
);
1953 switch(st
->codec
.codec_type
) {
1954 case CODEC_TYPE_AUDIO
:
1955 av_frac_add(&st
->pts
, st
->pts_incr
* st
->codec
.frame_size
);
1957 case CODEC_TYPE_VIDEO
:
1958 av_frac_add(&st
->pts
, st
->pts_incr
);
1963 pkt
->stream_index
= s
->cur_pkt
.stream_index
;
1964 /* we use the codec indication because it is
1965 more accurate than the demux flags */
1967 if (st
->codec
.coded_frame
->key_frame
)
1968 pkt
->flags
|= PKT_FLAG_KEY
;
1973 /* free previous packet */
1974 av_free_packet(&s
->cur_pkt
);
1976 old_nb_streams
= s
->nb_streams
;
1977 ret
= av_read_packet(s
, &s
->cur_pkt
);
1980 /* open parsers for each new streams */
1981 for(i
= old_nb_streams
; i
< s
->nb_streams
; i
++)
1983 st
= s
->streams
[s
->cur_pkt
.stream_index
];
1985 /* update current pts (XXX: dts handling) from packet, or
1986 use current pts if none given */
1987 if (s
->cur_pkt
.pts
!= AV_NOPTS_VALUE
) {
1988 av_frac_set(&st
->pts
, s
->cur_pkt
.pts
);
1990 s
->cur_pkt
.pts
= st
->pts
.val
;
1992 if (!st
->codec
.codec
) {
1993 /* no codec opened: just return the raw packet */
1996 /* no codec opened: just update the pts by considering we
1997 have one frame and free the packet */
1998 if (st
->pts
.den
== 0) {
1999 switch(st
->codec
.codec_type
) {
2000 case CODEC_TYPE_AUDIO
:
2001 st
->pts_incr
= (INT64
)s
->pts_den
* st
->codec
.frame_size
;
2002 av_frac_init(&st
->pts
, st
->pts
.val
, 0,
2003 (INT64
)s
->pts_num
* st
->codec
.sample_rate
);
2005 case CODEC_TYPE_VIDEO
:
2006 st
->pts_incr
= (INT64
)s
->pts_den
* FRAME_RATE_BASE
;
2007 av_frac_init(&st
->pts
, st
->pts
.val
, 0,
2008 (INT64
)s
->pts_num
* st
->codec
.frame_rate
);
2014 av_frac_add(&st
->pts
, st
->pts_incr
);
2017 s
->cur_ptr
= s
->cur_pkt
.data
;
2018 s
->cur_len
= s
->cur_pkt
.size
;
2024 static int compute_send_delay(HTTPContext
*c
)
2026 INT64 cur_pts
, delta_pts
, next_pts
;
2029 /* compute current pts value from system time */
2030 cur_pts
= ((INT64
)(cur_time
- c
->start_time
) * c
->fmt_in
->pts_den
) /
2031 (c
->fmt_in
->pts_num
* 1000LL);
2032 /* compute the delta from the stream we choose as
2033 main clock (we do that to avoid using explicit
2034 buffers to do exact packet reordering for each
2036 /* XXX: really need to fix the number of streams */
2037 if (c
->pts_stream_index
>= c
->fmt_in
->nb_streams
)
2040 next_pts
= c
->fmt_in
->streams
[c
->pts_stream_index
]->pts
.val
;
2041 delta_pts
= next_pts
- cur_pts
;
2042 if (delta_pts
<= 0) {
2045 delay1
= (delta_pts
* 1000 * c
->fmt_in
->pts_num
) / c
->fmt_in
->pts_den
;
2051 /* just fall backs */
2052 int av_read_frame(AVFormatContext
*s
, AVPacket
*pkt
)
2054 return av_read_packet(s
, pkt
);
2057 static int compute_send_delay(HTTPContext
*c
)
2059 int datarate
= 8 * get_longterm_datarate(&c
->datarate
, c
->data_count
);
2061 if (datarate
> c
->stream
->bandwidth
* 2000) {
2069 static int http_prepare_data(HTTPContext
*c
)
2072 AVFormatContext
*ctx
;
2075 case HTTPSTATE_SEND_DATA_HEADER
:
2076 memset(&c
->fmt_ctx
, 0, sizeof(c
->fmt_ctx
));
2077 pstrcpy(c
->fmt_ctx
.author
, sizeof(c
->fmt_ctx
.author
),
2079 pstrcpy(c
->fmt_ctx
.comment
, sizeof(c
->fmt_ctx
.comment
),
2080 c
->stream
->comment
);
2081 pstrcpy(c
->fmt_ctx
.copyright
, sizeof(c
->fmt_ctx
.copyright
),
2082 c
->stream
->copyright
);
2083 pstrcpy(c
->fmt_ctx
.title
, sizeof(c
->fmt_ctx
.title
),
2086 /* open output stream by using specified codecs */
2087 c
->fmt_ctx
.oformat
= c
->stream
->fmt
;
2088 c
->fmt_ctx
.nb_streams
= c
->stream
->nb_streams
;
2089 for(i
=0;i
<c
->fmt_ctx
.nb_streams
;i
++) {
2091 st
= av_mallocz(sizeof(AVStream
));
2092 c
->fmt_ctx
.streams
[i
] = st
;
2093 /* if file or feed, then just take streams from FFStream struct */
2094 if (!c
->stream
->feed
||
2095 c
->stream
->feed
== c
->stream
)
2096 memcpy(st
, c
->stream
->streams
[i
], sizeof(AVStream
));
2098 memcpy(st
, c
->stream
->feed
->streams
[c
->stream
->feed_streams
[i
]],
2100 st
->codec
.frame_number
= 0; /* XXX: should be done in
2101 AVStream, not in codec */
2102 /* I'm pretty sure that this is not correct...
2103 * However, without it, we crash
2105 st
->codec
.coded_frame
= &dummy_frame
;
2107 c
->got_key_frame
= 0;
2109 /* prepare header and save header data in a stream */
2110 if (url_open_dyn_buf(&c
->fmt_ctx
.pb
) < 0) {
2111 /* XXX: potential leak */
2114 c
->fmt_ctx
.pb
.is_streamed
= 1;
2116 av_set_parameters(&c
->fmt_ctx
, NULL
);
2117 av_write_header(&c
->fmt_ctx
);
2119 len
= url_close_dyn_buf(&c
->fmt_ctx
.pb
, &c
->pb_buffer
);
2120 c
->buffer_ptr
= c
->pb_buffer
;
2121 c
->buffer_end
= c
->pb_buffer
+ len
;
2123 c
->state
= HTTPSTATE_SEND_DATA
;
2124 c
->last_packet_sent
= 0;
2126 case HTTPSTATE_SEND_DATA
:
2127 /* find a new packet */
2131 /* read a packet from the input stream */
2132 if (c
->stream
->feed
) {
2133 ffm_set_write_index(c
->fmt_in
,
2134 c
->stream
->feed
->feed_write_index
,
2135 c
->stream
->feed
->feed_size
);
2138 if (c
->stream
->max_time
&&
2139 c
->stream
->max_time
+ c
->start_time
- cur_time
< 0) {
2140 /* We have timed out */
2141 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2143 if (1 || c
->is_packetized
) {
2144 if (compute_send_delay(c
) > 0) {
2145 c
->state
= HTTPSTATE_WAIT
;
2146 return 1; /* state changed */
2150 if (av_read_frame(c
->fmt_in
, &pkt
) < 0) {
2151 if (c
->stream
->feed
&& c
->stream
->feed
->feed_opened
) {
2152 /* if coming from feed, it means we reached the end of the
2153 ffm file, so must wait for more data */
2154 c
->state
= HTTPSTATE_WAIT_FEED
;
2155 return 1; /* state changed */
2157 if (c
->stream
->loop
) {
2158 av_close_input_file(c
->fmt_in
);
2160 if (open_input_stream(c
, "") < 0)
2165 /* must send trailer now because eof or error */
2166 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2170 /* update first pts if needed */
2171 if (c
->first_pts
== AV_NOPTS_VALUE
)
2172 c
->first_pts
= pkt
.pts
;
2174 /* send it to the appropriate stream */
2175 if (c
->stream
->feed
) {
2176 /* if coming from a feed, select the right stream */
2177 if (c
->switch_pending
) {
2178 c
->switch_pending
= 0;
2179 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2180 if (c
->switch_feed_streams
[i
] == pkt
.stream_index
) {
2181 if (pkt
.flags
& PKT_FLAG_KEY
) {
2182 do_switch_stream(c
, i
);
2185 if (c
->switch_feed_streams
[i
] >= 0) {
2186 c
->switch_pending
= 1;
2190 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
2191 if (c
->feed_streams
[i
] == pkt
.stream_index
) {
2192 pkt
.stream_index
= i
;
2193 if (pkt
.flags
& PKT_FLAG_KEY
) {
2194 c
->got_key_frame
|= 1 << i
;
2196 /* See if we have all the key frames, then
2197 * we start to send. This logic is not quite
2198 * right, but it works for the case of a
2199 * single video stream with one or more
2200 * audio streams (for which every frame is
2201 * typically a key frame).
2203 if (!c
->stream
->send_on_key
||
2204 ((c
->got_key_frame
+ 1) >> c
->stream
->nb_streams
)) {
2210 AVCodecContext
*codec
;
2213 /* specific handling for RTP: we use several
2214 output stream (one for each RTP
2215 connection). XXX: need more abstract handling */
2216 if (c
->is_packetized
) {
2217 c
->packet_stream_index
= pkt
.stream_index
;
2218 ctx
= c
->rtp_ctx
[c
->packet_stream_index
];
2219 codec
= &ctx
->streams
[0]->codec
;
2220 /* only one stream per RTP connection */
2221 pkt
.stream_index
= 0;
2225 codec
= &ctx
->streams
[pkt
.stream_index
]->codec
;
2228 codec
->coded_frame
->key_frame
= ((pkt
.flags
& PKT_FLAG_KEY
) != 0);
2231 if (codec
->codec_type
== CODEC_TYPE_AUDIO
) {
2232 codec
->frame_size
= (codec
->sample_rate
* pkt
.duration
+ 500000) / 1000000;
2233 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
2237 if (c
->is_packetized
) {
2238 ret
= url_open_dyn_packet_buf(&ctx
->pb
,
2239 url_get_max_packet_size(c
->rtp_handles
[c
->packet_stream_index
]));
2240 c
->packet_byte_count
= 0;
2241 c
->packet_start_time_us
= av_gettime();
2243 ret
= url_open_dyn_buf(&ctx
->pb
);
2246 /* XXX: potential leak */
2249 if (av_write_frame(ctx
, pkt
.stream_index
, pkt
.data
, pkt
.size
)) {
2250 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
2253 len
= url_close_dyn_buf(&ctx
->pb
, &c
->pb_buffer
);
2254 c
->buffer_ptr
= c
->pb_buffer
;
2255 c
->buffer_end
= c
->pb_buffer
+ len
;
2257 codec
->frame_number
++;
2259 #ifndef AV_READ_FRAME
2260 av_free_packet(&pkt
);
2267 case HTTPSTATE_SEND_DATA_TRAILER
:
2268 /* last packet test ? */
2269 if (c
->last_packet_sent
|| c
->is_packetized
)
2272 /* prepare header */
2273 if (url_open_dyn_buf(&ctx
->pb
) < 0) {
2274 /* XXX: potential leak */
2277 av_write_trailer(ctx
);
2278 len
= url_close_dyn_buf(&ctx
->pb
, &c
->pb_buffer
);
2279 c
->buffer_ptr
= c
->pb_buffer
;
2280 c
->buffer_end
= c
->pb_buffer
+ len
;
2282 c
->last_packet_sent
= 1;
2289 #define SHORT_TERM_BANDWIDTH 8000000
2291 /* should convert the format at the same time */
2292 static int http_send_data(HTTPContext
*c
)
2296 while (c
->buffer_ptr
>= c
->buffer_end
) {
2297 av_freep(&c
->pb_buffer
);
2298 ret
= http_prepare_data(c
);
2301 else if (ret
== 0) {
2304 /* state change requested */
2309 if (c
->buffer_ptr
< c
->buffer_end
) {
2310 if (c
->is_packetized
) {
2311 /* RTP/UDP data output */
2312 len
= c
->buffer_end
- c
->buffer_ptr
;
2314 /* fail safe - should never happen */
2316 c
->buffer_ptr
= c
->buffer_end
;
2319 len
= (c
->buffer_ptr
[0] << 24) |
2320 (c
->buffer_ptr
[1] << 16) |
2321 (c
->buffer_ptr
[2] << 8) |
2323 if (len
> (c
->buffer_end
- c
->buffer_ptr
))
2326 /* short term bandwidth limitation */
2327 dt
= av_gettime() - c
->packet_start_time_us
;
2331 if ((c
->packet_byte_count
+ len
) * (INT64
)1000000 >=
2332 (SHORT_TERM_BANDWIDTH
/ 8) * (INT64
)dt
) {
2333 /* bandwidth overflow : wait at most one tick and retry */
2334 c
->state
= HTTPSTATE_WAIT_SHORT
;
2339 url_write(c
->rtp_handles
[c
->packet_stream_index
],
2340 c
->buffer_ptr
, len
);
2341 c
->buffer_ptr
+= len
;
2342 c
->packet_byte_count
+= len
;
2344 /* TCP data output */
2345 len
= write(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
2347 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
2348 /* error : close connection */
2354 c
->buffer_ptr
+= len
;
2357 c
->data_count
+= len
;
2358 update_datarate(&c
->datarate
, c
->data_count
);
2360 c
->stream
->bytes_served
+= len
;
2365 static int http_start_receive_data(HTTPContext
*c
)
2369 if (c
->stream
->feed_opened
)
2373 fd
= open(c
->stream
->feed_filename
, O_RDWR
);
2378 c
->stream
->feed_write_index
= ffm_read_write_index(fd
);
2379 c
->stream
->feed_size
= lseek(fd
, 0, SEEK_END
);
2380 lseek(fd
, 0, SEEK_SET
);
2382 /* init buffer input */
2383 c
->buffer_ptr
= c
->buffer
;
2384 c
->buffer_end
= c
->buffer
+ FFM_PACKET_SIZE
;
2385 c
->stream
->feed_opened
= 1;
2389 static int http_receive_data(HTTPContext
*c
)
2393 if (c
->buffer_end
> c
->buffer_ptr
) {
2396 len
= read(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
2398 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
2399 /* error : close connection */
2402 } else if (len
== 0) {
2403 /* end of connection : close it */
2406 c
->buffer_ptr
+= len
;
2407 c
->data_count
+= len
;
2408 update_datarate(&c
->datarate
, c
->data_count
);
2412 if (c
->buffer_ptr
>= c
->buffer_end
) {
2413 FFStream
*feed
= c
->stream
;
2414 /* a packet has been received : write it in the store, except
2416 if (c
->data_count
> FFM_PACKET_SIZE
) {
2418 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2419 /* XXX: use llseek or url_seek */
2420 lseek(c
->feed_fd
, feed
->feed_write_index
, SEEK_SET
);
2421 write(c
->feed_fd
, c
->buffer
, FFM_PACKET_SIZE
);
2423 feed
->feed_write_index
+= FFM_PACKET_SIZE
;
2424 /* update file size */
2425 if (feed
->feed_write_index
> c
->stream
->feed_size
)
2426 feed
->feed_size
= feed
->feed_write_index
;
2428 /* handle wrap around if max file size reached */
2429 if (feed
->feed_write_index
>= c
->stream
->feed_max_size
)
2430 feed
->feed_write_index
= FFM_PACKET_SIZE
;
2433 ffm_write_write_index(c
->feed_fd
, feed
->feed_write_index
);
2435 /* wake up any waiting connections */
2436 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
2437 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
2438 c1
->stream
->feed
== c
->stream
->feed
) {
2439 c1
->state
= HTTPSTATE_SEND_DATA
;
2443 /* We have a header in our hands that contains useful data */
2445 AVInputFormat
*fmt_in
;
2446 ByteIOContext
*pb
= &s
.pb
;
2449 memset(&s
, 0, sizeof(s
));
2451 url_open_buf(pb
, c
->buffer
, c
->buffer_end
- c
->buffer
, URL_RDONLY
);
2452 pb
->buf_end
= c
->buffer_end
; /* ?? */
2453 pb
->is_streamed
= 1;
2455 /* use feed output format name to find corresponding input format */
2456 fmt_in
= av_find_input_format(feed
->fmt
->name
);
2460 s
.priv_data
= av_mallocz(fmt_in
->priv_data_size
);
2464 if (fmt_in
->read_header(&s
, 0) < 0) {
2465 av_freep(&s
.priv_data
);
2469 /* Now we have the actual streams */
2470 if (s
.nb_streams
!= feed
->nb_streams
) {
2471 av_freep(&s
.priv_data
);
2474 for (i
= 0; i
< s
.nb_streams
; i
++) {
2475 memcpy(&feed
->streams
[i
]->codec
,
2476 &s
.streams
[i
]->codec
, sizeof(AVCodecContext
));
2478 av_freep(&s
.priv_data
);
2480 c
->buffer_ptr
= c
->buffer
;
2485 c
->stream
->feed_opened
= 0;
2490 /********************************************************************/
2493 static void rtsp_reply_header(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2500 switch(error_number
) {
2501 #define DEF(n, c, s) case c: str = s; break;
2502 #include "rtspcodes.h"
2505 str
= "Unknown Error";
2509 url_fprintf(c
->pb
, "RTSP/1.0 %d %s\r\n", error_number
, str
);
2510 url_fprintf(c
->pb
, "CSeq: %d\r\n", c
->seq
);
2512 /* output GMT time */
2516 p
= buf2
+ strlen(p
) - 1;
2519 url_fprintf(c
->pb
, "Date: %s GMT\r\n", buf2
);
2522 static void rtsp_reply_error(HTTPContext
*c
, enum RTSPStatusCode error_number
)
2524 rtsp_reply_header(c
, error_number
);
2525 url_fprintf(c
->pb
, "\r\n");
2528 static int rtsp_parse_request(HTTPContext
*c
)
2530 const char *p
, *p1
, *p2
;
2537 RTSPHeader header1
, *header
= &header1
;
2539 c
->buffer_ptr
[0] = '\0';
2542 get_word(cmd
, sizeof(cmd
), &p
);
2543 get_word(url
, sizeof(url
), &p
);
2544 get_word(protocol
, sizeof(protocol
), &p
);
2546 pstrcpy(c
->method
, sizeof(c
->method
), cmd
);
2547 pstrcpy(c
->url
, sizeof(c
->url
), url
);
2548 pstrcpy(c
->protocol
, sizeof(c
->protocol
), protocol
);
2551 if (url_open_dyn_buf(c
->pb
) < 0) {
2552 /* XXX: cannot do more */
2553 c
->pb
= NULL
; /* safety */
2557 /* check version name */
2558 if (strcmp(protocol
, "RTSP/1.0") != 0) {
2559 rtsp_reply_error(c
, RTSP_STATUS_VERSION
);
2563 /* parse each header line */
2564 memset(header
, 0, sizeof(RTSPHeader
));
2565 /* skip to next line */
2566 while (*p
!= '\n' && *p
!= '\0')
2570 while (*p
!= '\0') {
2571 p1
= strchr(p
, '\n');
2575 if (p2
> p
&& p2
[-1] == '\r')
2577 /* skip empty line */
2581 if (len
> sizeof(line
) - 1)
2582 len
= sizeof(line
) - 1;
2583 memcpy(line
, p
, len
);
2585 rtsp_parse_line(header
, line
);
2589 /* handle sequence number */
2590 c
->seq
= header
->seq
;
2592 if (!strcmp(cmd
, "DESCRIBE")) {
2593 rtsp_cmd_describe(c
, url
);
2594 } else if (!strcmp(cmd
, "SETUP")) {
2595 rtsp_cmd_setup(c
, url
, header
);
2596 } else if (!strcmp(cmd
, "PLAY")) {
2597 rtsp_cmd_play(c
, url
, header
);
2598 } else if (!strcmp(cmd
, "PAUSE")) {
2599 rtsp_cmd_pause(c
, url
, header
);
2600 } else if (!strcmp(cmd
, "TEARDOWN")) {
2601 rtsp_cmd_teardown(c
, url
, header
);
2603 rtsp_reply_error(c
, RTSP_STATUS_METHOD
);
2606 len
= url_close_dyn_buf(c
->pb
, &c
->pb_buffer
);
2607 c
->pb
= NULL
; /* safety */
2609 /* XXX: cannot do more */
2612 c
->buffer_ptr
= c
->pb_buffer
;
2613 c
->buffer_end
= c
->pb_buffer
+ len
;
2614 c
->state
= RTSPSTATE_SEND_REPLY
;
2618 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2620 static int prepare_sdp_description(FFStream
*stream
, UINT8
**pbuffer
,
2621 struct in_addr my_ip
)
2623 ByteIOContext pb1
, *pb
= &pb1
;
2624 int i
, payload_type
, port
, private_payload_type
, j
;
2625 const char *ipstr
, *title
, *mediatype
;
2628 if (url_open_dyn_buf(pb
) < 0)
2631 /* general media info */
2633 url_fprintf(pb
, "v=0\n");
2634 ipstr
= inet_ntoa(my_ip
);
2635 url_fprintf(pb
, "o=- 0 0 IN IP4 %s\n", ipstr
);
2636 title
= stream
->title
;
2637 if (title
[0] == '\0')
2639 url_fprintf(pb
, "s=%s\n", title
);
2640 if (stream
->comment
[0] != '\0')
2641 url_fprintf(pb
, "i=%s\n", stream
->comment
);
2642 if (stream
->is_multicast
) {
2643 url_fprintf(pb
, "c=IN IP4 %s\n", inet_ntoa(stream
->multicast_ip
));
2645 /* for each stream, we output the necessary info */
2646 private_payload_type
= 96;
2647 for(i
= 0; i
< stream
->nb_streams
; i
++) {
2648 st
= stream
->streams
[i
];
2649 switch(st
->codec
.codec_type
) {
2650 case CODEC_TYPE_AUDIO
:
2651 mediatype
= "audio";
2653 case CODEC_TYPE_VIDEO
:
2654 mediatype
= "video";
2657 mediatype
= "application";
2660 /* NOTE: the port indication is not correct in case of
2661 unicast. It is not an issue because RTSP gives it */
2662 payload_type
= rtp_get_payload_type(&st
->codec
);
2663 if (payload_type
< 0)
2664 payload_type
= private_payload_type
++;
2665 if (stream
->is_multicast
) {
2666 port
= stream
->multicast_port
+ 2 * i
;
2670 url_fprintf(pb
, "m=%s %d RTP/AVP %d\n",
2671 mediatype
, port
, payload_type
);
2672 if (payload_type
>= 96) {
2673 /* for private payload type, we need to give more info */
2674 switch(st
->codec
.codec_id
) {
2675 case CODEC_ID_MPEG4
:
2678 url_fprintf(pb
, "a=rtpmap:%d MP4V-ES/%d\n",
2679 payload_type
, 90000);
2680 /* we must also add the mpeg4 header */
2681 data
= st
->codec
.extradata
;
2683 url_fprintf(pb
, "a=fmtp:%d config=");
2684 for(j
=0;j
<st
->codec
.extradata_size
;j
++) {
2685 url_fprintf(pb
, "%02x", data
[j
]);
2687 url_fprintf(pb
, "\n");
2692 /* XXX: add other codecs ? */
2696 url_fprintf(pb
, "a=control:streamid=%d\n", i
);
2698 return url_close_dyn_buf(pb
, pbuffer
);
2700 url_close_dyn_buf(pb
, pbuffer
);
2705 static void rtsp_cmd_describe(HTTPContext
*c
, const char *url
)
2711 int content_length
, len
;
2712 struct sockaddr_in my_addr
;
2714 /* find which url is asked */
2715 url_split(NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2720 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2721 if (!stream
->is_feed
&& stream
->fmt
== &rtp_mux
&&
2722 !strcmp(path
, stream
->filename
)) {
2726 /* no stream found */
2727 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2731 /* prepare the media description in sdp format */
2733 /* get the host IP */
2734 len
= sizeof(my_addr
);
2735 getsockname(c
->fd
, (struct sockaddr
*)&my_addr
, &len
);
2737 content_length
= prepare_sdp_description(stream
, &content
, my_addr
.sin_addr
);
2738 if (content_length
< 0) {
2739 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2742 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2743 url_fprintf(c
->pb
, "Content-Type: application/sdp\r\n");
2744 url_fprintf(c
->pb
, "Content-Length: %d\r\n", content_length
);
2745 url_fprintf(c
->pb
, "\r\n");
2746 put_buffer(c
->pb
, content
, content_length
);
2749 static HTTPContext
*find_rtp_session(const char *session_id
)
2753 if (session_id
[0] == '\0')
2756 for(c
= first_http_ctx
; c
!= NULL
; c
= c
->next
) {
2757 if (!strcmp(c
->session_id
, session_id
))
2763 RTSPTransportField
*find_transport(RTSPHeader
*h
, enum RTSPProtocol protocol
)
2765 RTSPTransportField
*th
;
2768 for(i
=0;i
<h
->nb_transports
;i
++) {
2769 th
= &h
->transports
[i
];
2770 if (th
->protocol
== protocol
)
2776 static void rtsp_cmd_setup(HTTPContext
*c
, const char *url
,
2780 int stream_index
, port
;
2785 RTSPTransportField
*th
;
2786 struct sockaddr_in dest_addr
;
2787 RTSPActionServerSetup setup
;
2789 /* find which url is asked */
2790 url_split(NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2795 /* now check each stream */
2796 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
2797 if (!stream
->is_feed
&& stream
->fmt
== &rtp_mux
) {
2798 /* accept aggregate filenames only if single stream */
2799 if (!strcmp(path
, stream
->filename
)) {
2800 if (stream
->nb_streams
!= 1) {
2801 rtsp_reply_error(c
, RTSP_STATUS_AGGREGATE
);
2808 for(stream_index
= 0; stream_index
< stream
->nb_streams
;
2810 snprintf(buf
, sizeof(buf
), "%s/streamid=%d",
2811 stream
->filename
, stream_index
);
2812 if (!strcmp(path
, buf
))
2817 /* no stream found */
2818 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
); /* XXX: right error ? */
2822 /* generate session id if needed */
2823 if (h
->session_id
[0] == '\0') {
2824 snprintf(h
->session_id
, sizeof(h
->session_id
),
2825 "%08x%08x", (int)random(), (int)random());
2828 /* find rtp session, and create it if none found */
2829 rtp_c
= find_rtp_session(h
->session_id
);
2831 rtp_c
= rtp_new_connection(&c
->from_addr
, stream
, h
->session_id
);
2833 rtsp_reply_error(c
, RTSP_STATUS_BANDWIDTH
);
2837 /* open input stream */
2838 if (open_input_stream(rtp_c
, "") < 0) {
2839 rtsp_reply_error(c
, RTSP_STATUS_INTERNAL
);
2843 /* always prefer UDP */
2844 th
= find_transport(h
, RTSP_PROTOCOL_RTP_UDP
);
2846 th
= find_transport(h
, RTSP_PROTOCOL_RTP_TCP
);
2848 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2852 rtp_c
->rtp_protocol
= th
->protocol
;
2855 /* test if stream is OK (test needed because several SETUP needs
2856 to be done for a given file) */
2857 if (rtp_c
->stream
!= stream
) {
2858 rtsp_reply_error(c
, RTSP_STATUS_SERVICE
);
2862 /* test if stream is already set up */
2863 if (rtp_c
->rtp_ctx
[stream_index
]) {
2864 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2868 /* check transport */
2869 th
= find_transport(h
, rtp_c
->rtp_protocol
);
2870 if (!th
|| (th
->protocol
== RTSP_PROTOCOL_RTP_UDP
&&
2871 th
->client_port_min
<= 0)) {
2872 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2876 /* setup default options */
2877 setup
.transport_option
[0] = '\0';
2878 dest_addr
= rtp_c
->from_addr
;
2879 dest_addr
.sin_port
= htons(th
->client_port_min
);
2881 /* add transport option if needed */
2882 if (ff_rtsp_callback
) {
2883 setup
.ipaddr
= ntohl(dest_addr
.sin_addr
.s_addr
);
2884 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP
, rtp_c
->session_id
,
2885 (char *)&setup
, sizeof(setup
),
2886 stream
->rtsp_option
) < 0) {
2887 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2890 dest_addr
.sin_addr
.s_addr
= htonl(setup
.ipaddr
);
2894 if (rtp_new_av_stream(rtp_c
, stream_index
, &dest_addr
) < 0) {
2895 rtsp_reply_error(c
, RTSP_STATUS_TRANSPORT
);
2899 /* now everything is OK, so we can send the connection parameters */
2900 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2902 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2904 switch(rtp_c
->rtp_protocol
) {
2905 case RTSP_PROTOCOL_RTP_UDP
:
2906 port
= rtp_get_local_port(rtp_c
->rtp_handles
[stream_index
]);
2907 url_fprintf(c
->pb
, "Transport: RTP/AVP/UDP;unicast;"
2908 "client_port=%d-%d;server_port=%d-%d",
2909 th
->client_port_min
, th
->client_port_min
+ 1,
2912 case RTSP_PROTOCOL_RTP_TCP
:
2913 url_fprintf(c
->pb
, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2914 stream_index
* 2, stream_index
* 2 + 1);
2919 if (setup
.transport_option
[0] != '\0') {
2920 url_fprintf(c
->pb
, ";%s", setup
.transport_option
);
2922 url_fprintf(c
->pb
, "\r\n");
2925 url_fprintf(c
->pb
, "\r\n");
2929 /* find an rtp connection by using the session ID. Check consistency
2931 static HTTPContext
*find_rtp_session_with_url(const char *url
,
2932 const char *session_id
)
2938 rtp_c
= find_rtp_session(session_id
);
2942 /* find which url is asked */
2943 url_split(NULL
, 0, NULL
, 0, NULL
, path1
, sizeof(path1
), url
);
2947 if (strcmp(path
, rtp_c
->stream
->filename
) != 0)
2952 static void rtsp_cmd_play(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
2956 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
2958 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
2962 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
2963 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
&&
2964 rtp_c
->state
!= HTTPSTATE_READY
) {
2965 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2969 rtp_c
->state
= HTTPSTATE_SEND_DATA
;
2971 /* now everything is OK, so we can send the connection parameters */
2972 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2974 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
2975 url_fprintf(c
->pb
, "\r\n");
2978 static void rtsp_cmd_pause(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
2982 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
2984 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
2988 if (rtp_c
->state
!= HTTPSTATE_SEND_DATA
&&
2989 rtp_c
->state
!= HTTPSTATE_WAIT_FEED
) {
2990 rtsp_reply_error(c
, RTSP_STATUS_STATE
);
2994 rtp_c
->state
= HTTPSTATE_READY
;
2996 /* now everything is OK, so we can send the connection parameters */
2997 rtsp_reply_header(c
, RTSP_STATUS_OK
);
2999 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3000 url_fprintf(c
->pb
, "\r\n");
3003 static void rtsp_cmd_teardown(HTTPContext
*c
, const char *url
, RTSPHeader
*h
)
3007 rtp_c
= find_rtp_session_with_url(url
, h
->session_id
);
3009 rtsp_reply_error(c
, RTSP_STATUS_SESSION
);
3013 /* abort the session */
3014 close_connection(rtp_c
);
3016 if (ff_rtsp_callback
) {
3017 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN
, rtp_c
->session_id
,
3019 rtp_c
->stream
->rtsp_option
);
3022 /* now everything is OK, so we can send the connection parameters */
3023 rtsp_reply_header(c
, RTSP_STATUS_OK
);
3025 url_fprintf(c
->pb
, "Session: %s\r\n", rtp_c
->session_id
);
3026 url_fprintf(c
->pb
, "\r\n");
3030 /********************************************************************/
3033 static HTTPContext
*rtp_new_connection(struct sockaddr_in
*from_addr
,
3034 FFStream
*stream
, const char *session_id
)
3036 HTTPContext
*c
= NULL
;
3038 /* XXX: should output a warning page when coming
3039 close to the connection limit */
3040 if (nb_connections
>= nb_max_connections
)
3043 /* add a new connection */
3044 c
= av_mallocz(sizeof(HTTPContext
));
3049 c
->poll_entry
= NULL
;
3050 c
->from_addr
= *from_addr
;
3051 c
->buffer_size
= IOBUFFER_INIT_SIZE
;
3052 c
->buffer
= av_malloc(c
->buffer_size
);
3057 pstrcpy(c
->session_id
, sizeof(c
->session_id
), session_id
);
3058 c
->state
= HTTPSTATE_READY
;
3059 c
->is_packetized
= 1;
3060 /* protocol is shown in statistics */
3061 pstrcpy(c
->protocol
, sizeof(c
->protocol
), "RTP");
3063 current_bandwidth
+= stream
->bandwidth
;
3065 c
->next
= first_http_ctx
;
3077 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3078 command). if dest_addr is NULL, then TCP tunneling in RTSP is
3080 static int rtp_new_av_stream(HTTPContext
*c
,
3081 int stream_index
, struct sockaddr_in
*dest_addr
)
3083 AVFormatContext
*ctx
;
3090 /* now we can open the relevant output stream */
3091 ctx
= av_mallocz(sizeof(AVFormatContext
));
3094 ctx
->oformat
= &rtp_mux
;
3096 st
= av_mallocz(sizeof(AVStream
));
3099 ctx
->nb_streams
= 1;
3100 ctx
->streams
[0] = st
;
3102 if (!c
->stream
->feed
||
3103 c
->stream
->feed
== c
->stream
) {
3104 memcpy(st
, c
->stream
->streams
[stream_index
], sizeof(AVStream
));
3107 c
->stream
->feed
->streams
[c
->stream
->feed_streams
[stream_index
]],
3112 /* build destination RTP address */
3113 ipaddr
= inet_ntoa(dest_addr
->sin_addr
);
3115 /* XXX: also pass as parameter to function ? */
3116 if (c
->stream
->is_multicast
) {
3118 ttl
= c
->stream
->multicast_ttl
;
3121 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3122 "rtp://%s:%d?multicast=1&ttl=%d",
3123 ipaddr
, ntohs(dest_addr
->sin_port
), ttl
);
3125 snprintf(ctx
->filename
, sizeof(ctx
->filename
),
3126 "rtp://%s:%d", ipaddr
, ntohs(dest_addr
->sin_port
));
3129 if (url_open(&h
, ctx
->filename
, URL_WRONLY
) < 0)
3131 c
->rtp_handles
[stream_index
] = h
;
3136 http_log("%s:%d - - [%s] \"RTPSTART %s/streamid=%d\"\n",
3137 ipaddr
, ntohs(dest_addr
->sin_port
),
3139 c
->stream
->filename
, stream_index
);
3141 /* normally, no packets should be output here, but the packet size may be checked */
3142 if (url_open_dyn_packet_buf(&ctx
->pb
,
3143 url_get_max_packet_size(h
)) < 0) {
3144 /* XXX: close stream */
3147 av_set_parameters(ctx
, NULL
);
3148 if (av_write_header(ctx
) < 0) {
3155 url_close_dyn_buf(&ctx
->pb
, &dummy_buf
);
3158 c
->rtp_ctx
[stream_index
] = ctx
;
3162 /********************************************************************/
3163 /* ffserver initialization */
3165 AVStream
*add_av_stream1(FFStream
*stream
, AVCodecContext
*codec
)
3169 fst
= av_mallocz(sizeof(AVStream
));
3172 fst
->priv_data
= av_mallocz(sizeof(FeedData
));
3173 memcpy(&fst
->codec
, codec
, sizeof(AVCodecContext
));
3174 fst
->codec
.coded_frame
= &dummy_frame
;
3175 stream
->streams
[stream
->nb_streams
++] = fst
;
3179 /* return the stream number in the feed */
3180 int add_av_stream(FFStream
*feed
,
3184 AVCodecContext
*av
, *av1
;
3188 for(i
=0;i
<feed
->nb_streams
;i
++) {
3189 st
= feed
->streams
[i
];
3191 if (av1
->codec_id
== av
->codec_id
&&
3192 av1
->codec_type
== av
->codec_type
&&
3193 av1
->bit_rate
== av
->bit_rate
) {
3195 switch(av
->codec_type
) {
3196 case CODEC_TYPE_AUDIO
:
3197 if (av1
->channels
== av
->channels
&&
3198 av1
->sample_rate
== av
->sample_rate
)
3201 case CODEC_TYPE_VIDEO
:
3202 if (av1
->width
== av
->width
&&
3203 av1
->height
== av
->height
&&
3204 av1
->frame_rate
== av
->frame_rate
&&
3205 av1
->gop_size
== av
->gop_size
)
3214 fst
= add_av_stream1(feed
, av
);
3217 return feed
->nb_streams
- 1;
3222 void remove_stream(FFStream
*stream
)
3226 while (*ps
!= NULL
) {
3227 if (*ps
== stream
) {
3235 /* specific mpeg4 handling : we extract the raw parameters */
3236 void extract_mpeg4_header(AVFormatContext
*infile
)
3238 int mpeg4_count
, i
, size
;
3244 for(i
=0;i
<infile
->nb_streams
;i
++) {
3245 st
= infile
->streams
[i
];
3246 if (st
->codec
.codec_id
== CODEC_ID_MPEG4
&&
3247 st
->codec
.extradata
== NULL
) {
3254 printf("MPEG4 without extra data: trying to find header\n");
3255 while (mpeg4_count
> 0) {
3256 if (av_read_packet(infile
, &pkt
) < 0)
3258 st
= infile
->streams
[pkt
.stream_index
];
3259 if (st
->codec
.codec_id
== CODEC_ID_MPEG4
&&
3260 st
->codec
.extradata
== NULL
) {
3261 /* fill extradata with the header */
3262 /* XXX: we make hard suppositions here ! */
3264 while (p
< pkt
.data
+ pkt
.size
- 4) {
3265 /* stop when vop header is found */
3266 if (p
[0] == 0x00 && p
[1] == 0x00 &&
3267 p
[2] == 0x01 && p
[3] == 0xb6) {
3268 size
= p
- pkt
.data
;
3269 // av_hex_dump(pkt.data, size);
3270 st
->codec
.extradata
= av_malloc(size
);
3271 st
->codec
.extradata_size
= size
;
3272 memcpy(st
->codec
.extradata
, pkt
.data
, size
);
3279 av_free_packet(&pkt
);
3283 /* compute the needed AVStream for each file */
3284 void build_file_streams(void)
3286 FFStream
*stream
, *stream_next
;
3287 AVFormatContext
*infile
;