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>
32 #include <sys/types.h>
33 #include <sys/socket.h>
35 #include <arpa/inet.h>
40 /* maximum number of simultaneous HTTP connections */
41 #define HTTP_MAX_CONNECTIONS 2000
44 HTTPSTATE_WAIT_REQUEST
,
45 HTTPSTATE_SEND_HEADER
,
46 HTTPSTATE_SEND_DATA_HEADER
,
48 HTTPSTATE_SEND_DATA_TRAILER
,
49 HTTPSTATE_RECEIVE_DATA
,
53 const char *http_state
[] = {
63 #define IOBUFFER_INIT_SIZE 8192
64 #define PBUFFER_INIT_SIZE 8192
66 /* coef for exponential mean for bitrate estimation in statistics */
69 /* timeouts are in ms */
70 #define REQUEST_TIMEOUT (15 * 1000)
71 #define SYNC_TIMEOUT (10 * 1000)
78 /* context associated with one connection */
79 typedef struct HTTPContext
{
81 int fd
; /* socket file descriptor */
82 struct sockaddr_in from_addr
; /* origin */
83 struct pollfd
*poll_entry
; /* used when polling */
85 UINT8
*buffer_ptr
, *buffer_end
;
87 struct HTTPContext
*next
;
88 int got_key_frame
; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
92 /* input format handling */
93 AVFormatContext
*fmt_in
;
94 /* output format handling */
95 struct FFStream
*stream
;
96 /* -1 is invalid stream */
97 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
98 int switch_feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
100 AVFormatContext fmt_ctx
;
101 int last_packet_sent
; /* true if last data packet was sent */
104 long start_time
; /* In milliseconds - this wraps fairly often */
105 DataRateData datarate
;
116 /* each generated stream is described here */
120 STREAM_TYPE_REDIRECT
,
123 /* description of each stream of the ffserver.conf file */
124 typedef struct FFStream
{
125 enum StreamType stream_type
;
126 char filename
[1024]; /* stream filename */
127 struct FFStream
*feed
;
130 int prebuffer
; /* Number of millseconds early to start */
131 long max_time
; /* Number of milliseconds to run */
133 AVStream
*streams
[MAX_STREAMS
];
134 int feed_streams
[MAX_STREAMS
]; /* index of streams in the feed */
135 char feed_filename
[1024]; /* file name of the feed storage, or
136 input file name for a stream */
141 pid_t pid
; /* Of ffmpeg process */
142 time_t pid_start
; /* Of ffmpeg process */
144 struct FFStream
*next
;
146 int feed_opened
; /* true if someone if writing to feed */
147 int is_feed
; /* true if it is a feed */
150 INT64 feed_max_size
; /* maximum storage size */
151 INT64 feed_write_index
; /* current write position in feed (it wraps round) */
152 INT64 feed_size
; /* current size of feed */
153 struct FFStream
*next_feed
;
156 typedef struct FeedData
{
157 long long data_count
;
158 float avg_frame_size
; /* frame size averraged over last frames with exponential mean */
161 struct sockaddr_in my_addr
;
162 char logfilename
[1024];
163 HTTPContext
*first_http_ctx
;
164 FFStream
*first_feed
; /* contains only feeds */
165 FFStream
*first_stream
; /* contains all streams, including feeds */
167 static int handle_http(HTTPContext
*c
);
168 static int http_parse_request(HTTPContext
*c
);
169 static int http_send_data(HTTPContext
*c
);
170 static void compute_stats(HTTPContext
*c
);
171 static int open_input_stream(HTTPContext
*c
, const char *info
);
172 static int http_start_receive_data(HTTPContext
*c
);
173 static int http_receive_data(HTTPContext
*c
);
175 static const char *my_program_name
;
177 static int ffserver_debug
;
178 static int no_launch
;
179 static int need_to_start_children
;
181 int nb_max_connections
;
184 int nb_max_bandwidth
;
187 static long cur_time
; // Making this global saves on passing it around everywhere
189 static long gettime_ms(void)
193 gettimeofday(&tv
,NULL
);
194 return (long long)tv
.tv_sec
* 1000 + (tv
.tv_usec
/ 1000);
197 static FILE *logfile
= NULL
;
199 static void http_log(char *fmt
, ...)
205 vfprintf(logfile
, fmt
, ap
);
211 static void log_connection(HTTPContext
*c
)
213 char buf1
[32], buf2
[32], *p
;
219 /* XXX: reentrant function ? */
220 p
= inet_ntoa(c
->from_addr
.sin_addr
);
225 p
= buf2
+ strlen(p
) - 1;
228 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
229 buf1
, buf2
, c
->method
, c
->url
, c
->protocol
, (c
->http_error ? c
->http_error
: 200), c
->data_count
);
232 static void update_datarate(DataRateData
*drd
, INT64 count
)
234 if (!drd
->time1
&& !drd
->count1
) {
235 drd
->time1
= drd
->time2
= cur_time
;
236 drd
->count1
= drd
->count2
= count
;
238 if (cur_time
- drd
->time2
> 5000) {
239 drd
->time1
= drd
->time2
;
240 drd
->count1
= drd
->count2
;
241 drd
->time2
= cur_time
;
247 /* In bytes per second */
248 static int compute_datarate(DataRateData
*drd
, INT64 count
)
250 if (cur_time
== drd
->time1
)
253 return ((count
- drd
->count1
) * 1000) / (cur_time
- drd
->time1
);
256 static void start_children(FFStream
*feed
)
261 for (; feed
; feed
= feed
->next
) {
262 if (feed
->child_argv
&& !feed
->pid
) {
263 feed
->pid_start
= time(0);
268 fprintf(stderr
, "Unable to create children\n");
277 for (i
= 3; i
< 256; i
++) {
281 if (!ffserver_debug
) {
282 i
= open("/dev/null", O_RDWR
);
291 pstrcpy(pathname
, sizeof(pathname
), my_program_name
);
293 slash
= strrchr(pathname
, '/');
299 strcpy(slash
, "ffmpeg");
301 execvp(pathname
, feed
->child_argv
);
309 /* main loop of the http server */
310 static int http_server(struct sockaddr_in my_addr
)
312 int server_fd
, tmp
, ret
;
313 struct sockaddr_in from_addr
;
314 struct pollfd poll_table
[HTTP_MAX_CONNECTIONS
+ 1], *poll_entry
;
315 HTTPContext
*c
, **cp
;
317 server_fd
= socket(AF_INET
,SOCK_STREAM
,0);
324 setsockopt(server_fd
, SOL_SOCKET
, SO_REUSEADDR
, &tmp
, sizeof(tmp
));
326 if (bind (server_fd
, (struct sockaddr
*) &my_addr
, sizeof (my_addr
)) < 0) {
332 if (listen (server_fd
, 5) < 0) {
338 http_log("ffserver started.\n");
340 start_children(first_feed
);
342 fcntl(server_fd
, F_SETFL
, O_NONBLOCK
);
343 first_http_ctx
= NULL
;
345 first_http_ctx
= NULL
;
347 poll_entry
= poll_table
;
348 poll_entry
->fd
= server_fd
;
349 poll_entry
->events
= POLLIN
;
352 /* wait for events on each HTTP handle */
358 case HTTPSTATE_WAIT_REQUEST
:
359 c
->poll_entry
= poll_entry
;
361 poll_entry
->events
= POLLIN
;
364 case HTTPSTATE_SEND_HEADER
:
365 case HTTPSTATE_SEND_DATA_HEADER
:
366 case HTTPSTATE_SEND_DATA
:
367 case HTTPSTATE_SEND_DATA_TRAILER
:
368 c
->poll_entry
= poll_entry
;
370 poll_entry
->events
= POLLOUT
;
373 case HTTPSTATE_RECEIVE_DATA
:
374 c
->poll_entry
= poll_entry
;
376 poll_entry
->events
= POLLIN
;
379 case HTTPSTATE_WAIT_FEED
:
380 /* need to catch errors */
381 c
->poll_entry
= poll_entry
;
383 poll_entry
->events
= POLLIN
;/* Maybe this will work */
387 c
->poll_entry
= NULL
;
393 /* wait for an event on one connection. We poll at least every
394 second to handle timeouts */
396 ret
= poll(poll_table
, poll_entry
- poll_table
, 1000);
399 cur_time
= gettime_ms();
401 if (need_to_start_children
) {
402 need_to_start_children
= 0;
403 start_children(first_feed
);
406 /* now handle the events */
408 cp
= &first_http_ctx
;
409 while ((*cp
) != NULL
) {
411 if (handle_http (c
) < 0) {
412 /* close and free the connection */
416 av_close_input_file(c
->fmt_in
);
418 nb_bandwidth
-= c
->bandwidth
;
428 /* new connection request ? */
429 poll_entry
= poll_table
;
430 if (poll_entry
->revents
& POLLIN
) {
433 len
= sizeof(from_addr
);
434 fd
= accept(server_fd
, (struct sockaddr
*)&from_addr
,
437 fcntl(fd
, F_SETFL
, O_NONBLOCK
);
438 /* XXX: should output a warning page when coming
439 close to the connection limit */
440 if (nb_connections
>= nb_max_connections
) {
443 /* add a new connection */
444 c
= av_mallocz(sizeof(HTTPContext
));
446 c
->next
= first_http_ctx
;
449 c
->poll_entry
= NULL
;
450 c
->from_addr
= from_addr
;
451 c
->state
= HTTPSTATE_WAIT_REQUEST
;
452 c
->buffer
= av_malloc(c
->buffer_size
= IOBUFFER_INIT_SIZE
);
453 c
->pbuffer
= av_malloc(c
->pbuffer_size
= PBUFFER_INIT_SIZE
);
454 if (!c
->buffer
|| !c
->pbuffer
) {
459 c
->buffer_ptr
= c
->buffer
;
460 c
->buffer_end
= c
->buffer
+ c
->buffer_size
;
461 c
->timeout
= cur_time
+ REQUEST_TIMEOUT
;
462 c
->start_time
= cur_time
;
476 static int handle_http(HTTPContext
*c
)
481 case HTTPSTATE_WAIT_REQUEST
:
483 if ((c
->timeout
- cur_time
) < 0)
485 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
488 /* no need to read if no events */
489 if (!(c
->poll_entry
->revents
& POLLIN
))
492 len
= read(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
494 if (errno
!= EAGAIN
&& errno
!= EINTR
)
496 } else if (len
== 0) {
499 /* search for end of request. XXX: not fully correct since garbage could come after the end */
501 c
->buffer_ptr
+= len
;
503 if ((ptr
>= c
->buffer
+ 2 && !memcmp(ptr
-2, "\n\n", 2)) ||
504 (ptr
>= c
->buffer
+ 4 && !memcmp(ptr
-4, "\r\n\r\n", 4))) {
505 /* request found : parse it and reply */
506 if (http_parse_request(c
) < 0)
508 } else if (ptr
>= c
->buffer_end
) {
509 /* request too long: cannot do anything */
515 case HTTPSTATE_SEND_HEADER
:
516 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
519 /* no need to read if no events */
520 if (!(c
->poll_entry
->revents
& POLLOUT
))
522 len
= write(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
524 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
525 /* error : close connection */
529 c
->buffer_ptr
+= len
;
531 c
->stream
->bytes_served
+= len
;
532 c
->data_count
+= len
;
533 if (c
->buffer_ptr
>= c
->buffer_end
) {
537 /* all the buffer was send : synchronize to the incoming stream */
538 c
->state
= HTTPSTATE_SEND_DATA_HEADER
;
539 c
->buffer_ptr
= c
->buffer_end
= c
->buffer
;
544 case HTTPSTATE_SEND_DATA
:
545 case HTTPSTATE_SEND_DATA_HEADER
:
546 case HTTPSTATE_SEND_DATA_TRAILER
:
547 /* no need to read if no events */
548 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
551 if (!(c
->poll_entry
->revents
& POLLOUT
))
553 if (http_send_data(c
) < 0)
556 case HTTPSTATE_RECEIVE_DATA
:
557 /* no need to read if no events */
558 if (c
->poll_entry
->revents
& (POLLERR
| POLLHUP
))
560 if (!(c
->poll_entry
->revents
& POLLIN
))
562 if (http_receive_data(c
) < 0)
565 case HTTPSTATE_WAIT_FEED
:
566 /* no need to read if no events */
567 if (c
->poll_entry
->revents
& (POLLIN
| POLLERR
| POLLHUP
))
570 /* nothing to do, we'll be waken up by incoming feed packets */
578 static int extract_rates(char *rates
, int ratelen
, const char *request
)
582 for (p
= request
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
583 if (strncasecmp(p
, "Pragma:", 7) == 0) {
584 const char *q
= p
+ 7;
586 while (*q
&& *q
!= '\n' && isspace(*q
))
589 if (strncasecmp(q
, "stream-switch-entry=", 20) == 0) {
595 memset(rates
, 0xff, ratelen
);
598 while (*q
&& *q
!= '\n' && *q
!= ':')
601 if (sscanf(q
, ":%d:%d", &stream_no
, &rate_no
) != 2) {
605 if (stream_no
< ratelen
&& stream_no
>= 0) {
606 rates
[stream_no
] = rate_no
;
609 while (*q
&& *q
!= '\n' && !isspace(*q
))
626 static int find_stream_in_feed(FFStream
*feed
, AVCodecContext
*codec
, int bit_rate
)
629 int best_bitrate
= 100000000;
632 for (i
= 0; i
< feed
->nb_streams
; i
++) {
633 AVCodecContext
*feed_codec
= &feed
->streams
[i
]->codec
;
635 if (feed_codec
->codec_id
!= codec
->codec_id
||
636 feed_codec
->sample_rate
!= codec
->sample_rate
||
637 feed_codec
->width
!= codec
->width
||
638 feed_codec
->height
!= codec
->height
) {
642 /* Potential stream */
644 /* We want the fastest stream less than bit_rate, or the slowest
645 * faster than bit_rate
648 if (feed_codec
->bit_rate
<= bit_rate
) {
649 if (best_bitrate
> bit_rate
|| feed_codec
->bit_rate
> best_bitrate
) {
650 best_bitrate
= feed_codec
->bit_rate
;
654 if (feed_codec
->bit_rate
< best_bitrate
) {
655 best_bitrate
= feed_codec
->bit_rate
;
664 static int modify_current_stream(HTTPContext
*c
, char *rates
)
667 FFStream
*req
= c
->stream
;
668 int action_required
= 0;
670 for (i
= 0; i
< req
->nb_streams
; i
++) {
671 AVCodecContext
*codec
= &req
->streams
[i
]->codec
;
675 c
->switch_feed_streams
[i
] = req
->feed_streams
[i
];
678 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 2);
681 /* Wants off or slow */
682 c
->switch_feed_streams
[i
] = find_stream_in_feed(req
->feed
, codec
, codec
->bit_rate
/ 4);
684 /* This doesn't work well when it turns off the only stream! */
685 c
->switch_feed_streams
[i
] = -2;
686 c
->feed_streams
[i
] = -2;
691 if (c
->switch_feed_streams
[i
] >= 0 && c
->switch_feed_streams
[i
] != c
->feed_streams
[i
])
695 return action_required
;
699 static void do_switch_stream(HTTPContext
*c
, int i
)
701 if (c
->switch_feed_streams
[i
] >= 0) {
703 c
->feed_streams
[i
] = c
->switch_feed_streams
[i
];
706 /* Now update the stream */
708 c
->switch_feed_streams
[i
] = -1;
711 /* parse http request and prepare header */
712 static int http_parse_request(HTTPContext
*c
)
717 int doing_asf_redirector
;
720 char info
[1024], *filename
;
724 const char *mime_type
;
732 while (!isspace(*p
) && *p
!= '\0') {
733 if ((q
- cmd
) < sizeof(cmd
) - 1)
739 pstrcpy(c
->method
, sizeof(c
->method
), cmd
);
741 if (!strcmp(cmd
, "GET"))
743 else if (!strcmp(cmd
, "POST"))
748 while (isspace(*p
)) p
++;
750 while (!isspace(*p
) && *p
!= '\0') {
751 if ((q
- url
) < sizeof(url
) - 1)
757 pstrcpy(c
->url
, sizeof(c
->url
), url
);
759 while (isspace(*p
)) p
++;
761 while (!isspace(*p
) && *p
!= '\0') {
762 if ((q
- protocol
) < sizeof(protocol
) - 1)
767 if (strcmp(protocol
, "HTTP/1.0") && strcmp(protocol
, "HTTP/1.1"))
770 pstrcpy(c
->protocol
, sizeof(c
->protocol
), protocol
);
772 /* find the filename and the optional info string in the request */
779 pstrcpy(info
, sizeof(info
), p
);
785 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
786 if (strncasecmp(p
, "User-Agent:", 11) == 0) {
788 if (*useragent
&& *useragent
!= '\n' && isspace(*useragent
))
799 if (strlen(filename
) > 4 && strcmp(".asx", filename
+ strlen(filename
) - 4) == 0) {
801 filename
[strlen(filename
)-1] = 'f';
806 if (strlen(filename
) > 4 && strcmp(".asf", filename
+ strlen(filename
) - 4) == 0 &&
807 (!useragent
|| strncasecmp(useragent
, "NSPlayer", 8) != 0)) {
808 /* if this isn't WMP or lookalike, return the redirector file */
809 doing_asf_redirector
= 1;
811 doing_asf_redirector
= 0;
814 if (strlen(filename
) > 4 &&
815 (strcmp(".rpm", filename
+ strlen(filename
) - 4) == 0 ||
816 strcmp(".ram", filename
+ strlen(filename
) - 4) == 0)) {
818 strcpy(filename
+ strlen(filename
)-2, "m");
823 stream
= first_stream
;
824 while (stream
!= NULL
) {
825 if (!strcmp(stream
->filename
, filename
))
827 stream
= stream
->next
;
829 if (stream
== NULL
) {
830 sprintf(msg
, "File '%s' not found", url
);
835 memcpy(c
->feed_streams
, stream
->feed_streams
, sizeof(c
->feed_streams
));
836 memset(c
->switch_feed_streams
, -1, sizeof(c
->switch_feed_streams
));
838 if (stream
->stream_type
== STREAM_TYPE_REDIRECT
) {
841 q
+= sprintf(q
, "HTTP/1.0 301 Moved\r\n");
842 q
+= sprintf(q
, "Location: %s\r\n", stream
->feed_filename
);
843 q
+= sprintf(q
, "Content-type: text/html\r\n");
844 q
+= sprintf(q
, "\r\n");
845 q
+= sprintf(q
, "<html><head><title>Moved</title></head><body>\r\n");
846 q
+= sprintf(q
, "You should be <a href=\"%s\">redirected</a>.\r\n", stream
->feed_filename
);
847 q
+= sprintf(q
, "</body></html>\r\n");
849 /* prepare output buffer */
850 c
->buffer_ptr
= c
->buffer
;
852 c
->state
= HTTPSTATE_SEND_HEADER
;
856 /* If this is WMP, get the rate information */
857 if (extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
858 if (modify_current_stream(c
, ratebuf
)) {
859 for (i
= 0; i
< sizeof(c
->feed_streams
) / sizeof(c
->feed_streams
[0]); i
++) {
860 if (c
->switch_feed_streams
[i
] >= 0)
861 do_switch_stream(c
, i
);
866 if (post
== 0 && stream
->stream_type
== STREAM_TYPE_LIVE
) {
867 /* See if we meet the bandwidth requirements */
868 for(i
=0;i
<stream
->nb_streams
;i
++) {
869 AVStream
*st
= stream
->streams
[i
];
870 switch(st
->codec
.codec_type
) {
871 case CODEC_TYPE_AUDIO
:
872 c
->bandwidth
+= st
->codec
.bit_rate
;
874 case CODEC_TYPE_VIDEO
:
875 c
->bandwidth
+= st
->codec
.bit_rate
;
883 c
->bandwidth
/= 1000;
884 nb_bandwidth
+= c
->bandwidth
;
886 if (post
== 0 && nb_max_bandwidth
< nb_bandwidth
) {
889 q
+= sprintf(q
, "HTTP/1.0 200 Server too busy\r\n");
890 q
+= sprintf(q
, "Content-type: text/html\r\n");
891 q
+= sprintf(q
, "\r\n");
892 q
+= sprintf(q
, "<html><head><title>Too busy</title></head><body>\r\n");
893 q
+= sprintf(q
, "The server is too busy to serve your request at this time.<p>\r\n");
894 q
+= sprintf(q
, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
895 nb_bandwidth
, nb_max_bandwidth
);
896 q
+= sprintf(q
, "</body></html>\r\n");
898 /* prepare output buffer */
899 c
->buffer_ptr
= c
->buffer
;
901 c
->state
= HTTPSTATE_SEND_HEADER
;
905 if (doing_asx
|| doing_ram
|| doing_asf_redirector
) {
908 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
909 if (strncasecmp(p
, "Host:", 5) == 0) {
924 while (isspace(*hostinfo
))
927 eoh
= strchr(hostinfo
, '\n');
932 if (eoh
- hostinfo
< sizeof(hostbuf
) - 1) {
933 memcpy(hostbuf
, hostinfo
, eoh
- hostinfo
);
934 hostbuf
[eoh
- hostinfo
] = 0;
939 q
+= sprintf(q
, "HTTP/1.0 200 ASX Follows\r\n");
940 q
+= sprintf(q
, "Content-type: video/x-ms-asf\r\n");
941 q
+= sprintf(q
, "\r\n");
942 q
+= sprintf(q
, "<ASX Version=\"3\">\r\n");
943 q
+= sprintf(q
, "<!-- Autogenerated by ffserver -->\r\n");
944 q
+= sprintf(q
, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
945 hostbuf
, filename
, info
);
946 q
+= sprintf(q
, "</ASX>\r\n");
947 } else if (doing_ram
) {
948 q
+= sprintf(q
, "HTTP/1.0 200 RAM Follows\r\n");
949 q
+= sprintf(q
, "Content-type: audio/x-pn-realaudio\r\n");
950 q
+= sprintf(q
, "\r\n");
951 q
+= sprintf(q
, "# Autogenerated by ffserver\r\n");
952 q
+= sprintf(q
, "http://%s/%s%s\r\n",
953 hostbuf
, filename
, info
);
954 } else if (doing_asf_redirector
) {
955 q
+= sprintf(q
, "HTTP/1.0 200 ASF Redirect follows\r\n");
956 q
+= sprintf(q
, "Content-type: video/x-ms-asf\r\n");
957 q
+= sprintf(q
, "\r\n");
958 q
+= sprintf(q
, "[Reference]\r\n");
959 q
+= sprintf(q
, "Ref1=http://%s/%s%s\r\n",
960 hostbuf
, filename
, info
);
964 /* prepare output buffer */
965 c
->buffer_ptr
= c
->buffer
;
967 c
->state
= HTTPSTATE_SEND_HEADER
;
973 sprintf(msg
, "ASX/RAM file not handled");
977 stream
->conns_served
++;
979 /* XXX: add there authenticate and IP match */
982 /* if post, it means a feed is being sent */
983 if (!stream
->is_feed
) {
984 /* However it might be a status report from WMP! Lets log the data
985 * as it might come in handy one day
990 for (p
= c
->buffer
; *p
&& *p
!= '\r' && *p
!= '\n'; ) {
991 if (strncasecmp(p
, "Pragma: log-line=", 17) == 0) {
995 if (strncasecmp(p
, "Pragma: client-id=", 18) == 0) {
996 client_id
= strtol(p
+ 18, 0, 10);
1006 char *eol
= strchr(logline
, '\n');
1011 if (eol
[-1] == '\r')
1013 http_log("%.*s\n", eol
- logline
, logline
);
1014 c
->suppress_log
= 1;
1019 http_log("\nGot request:\n%s\n", c
->buffer
);
1022 if (client_id
&& extract_rates(ratebuf
, sizeof(ratebuf
), c
->buffer
)) {
1025 /* Now we have to find the client_id */
1026 for (wmpc
= first_http_ctx
; wmpc
; wmpc
= wmpc
->next
) {
1027 if (wmpc
->wmp_client_id
== client_id
)
1032 if (modify_current_stream(wmpc
, ratebuf
)) {
1033 wmpc
->switch_pending
= 1;
1038 sprintf(msg
, "POST command not handled");
1041 if (http_start_receive_data(c
) < 0) {
1042 sprintf(msg
, "could not open feed");
1046 c
->state
= HTTPSTATE_RECEIVE_DATA
;
1051 if (strcmp(stream
->filename
+ strlen(stream
->filename
) - 4, ".asf") == 0) {
1052 http_log("\nGot request:\n%s\n", c
->buffer
);
1056 if (c
->stream
->stream_type
== STREAM_TYPE_STATUS
)
1059 /* open input stream */
1060 if (open_input_stream(c
, info
) < 0) {
1061 sprintf(msg
, "Input stream corresponding to '%s' not found", url
);
1065 /* prepare http header */
1067 q
+= sprintf(q
, "HTTP/1.0 200 OK\r\n");
1068 mime_type
= c
->stream
->fmt
->mime_type
;
1070 mime_type
= "application/x-octet_stream";
1071 q
+= sprintf(q
, "Pragma: no-cache\r\n");
1073 /* for asf, we need extra headers */
1074 if (!strcmp(c
->stream
->fmt
->name
,"asf")) {
1075 /* Need to allocate a client id */
1076 static int wmp_session
;
1079 wmp_session
= time(0) & 0xffffff;
1081 c
->wmp_client_id
= ++wmp_session
;
1083 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
);
1084 mime_type
= "application/octet-stream";
1086 q
+= sprintf(q
, "Content-Type: %s\r\n", mime_type
);
1087 q
+= sprintf(q
, "\r\n");
1089 /* prepare output buffer */
1091 c
->buffer_ptr
= c
->buffer
;
1093 c
->state
= HTTPSTATE_SEND_HEADER
;
1096 c
->http_error
= 404;
1098 q
+= sprintf(q
, "HTTP/1.0 404 Not Found\r\n");
1099 q
+= sprintf(q
, "Content-type: %s\r\n", "text/html");
1100 q
+= sprintf(q
, "\r\n");
1101 q
+= sprintf(q
, "<HTML>\n");
1102 q
+= sprintf(q
, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1103 q
+= sprintf(q
, "<BODY>%s</BODY>\n", msg
);
1104 q
+= sprintf(q
, "</HTML>\n");
1106 /* prepare output buffer */
1107 c
->buffer_ptr
= c
->buffer
;
1109 c
->state
= HTTPSTATE_SEND_HEADER
;
1113 c
->http_error
= 200; /* horrible : we use this value to avoid
1114 going to the send data state */
1115 c
->state
= HTTPSTATE_SEND_HEADER
;
1119 static int fmt_bytecount(char *q
, INT64 count
)
1121 static const char *suffix
= " kMGTP";
1124 for (s
= suffix
; count
>= 100000 && s
[1]; count
/= 1000, s
++) {
1127 return sprintf(q
, "%lld%c", count
, *s
);
1130 static void compute_stats(HTTPContext
*c
)
1139 new_buffer
= av_malloc(65536);
1142 c
->buffer_size
= 65536;
1143 c
->buffer
= new_buffer
;
1144 c
->buffer_ptr
= c
->buffer
;
1145 c
->buffer_end
= c
->buffer
+ c
->buffer_size
;
1149 q
+= sprintf(q
, "HTTP/1.0 200 OK\r\n");
1150 q
+= sprintf(q
, "Content-type: %s\r\n", "text/html");
1151 q
+= sprintf(q
, "Pragma: no-cache\r\n");
1152 q
+= sprintf(q
, "\r\n");
1154 q
+= sprintf(q
, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1155 if (c
->stream
->feed_filename
) {
1156 q
+= sprintf(q
, "<link rel=\"shortcut icon\" href=\"%s\">\n", c
->stream
->feed_filename
);
1158 q
+= sprintf(q
, "</HEAD>\n<BODY>");
1159 q
+= sprintf(q
, "<H1>FFServer Status</H1>\n");
1161 q
+= sprintf(q
, "<H2>Available Streams</H2>\n");
1162 q
+= sprintf(q
, "<TABLE cellspacing=0 cellpadding=4>\n");
1163 q
+= sprintf(q
, "<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");
1164 stream
= first_stream
;
1165 while (stream
!= NULL
) {
1166 char sfilename
[1024];
1169 if (stream
->feed
!= stream
) {
1170 pstrcpy(sfilename
, sizeof(sfilename
) - 1, stream
->filename
);
1171 eosf
= sfilename
+ strlen(sfilename
);
1172 if (eosf
- sfilename
>= 4) {
1173 if (strcmp(eosf
- 4, ".asf") == 0) {
1174 strcpy(eosf
- 4, ".asx");
1175 } else if (strcmp(eosf
- 3, ".rm") == 0) {
1176 strcpy(eosf
- 3, ".ram");
1180 q
+= sprintf(q
, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1181 sfilename
, stream
->filename
);
1182 q
+= sprintf(q
, "<td align=right> %d <td align=right> ",
1183 stream
->conns_served
);
1184 q
+= fmt_bytecount(q
, stream
->bytes_served
);
1185 switch(stream
->stream_type
) {
1186 case STREAM_TYPE_LIVE
:
1188 int audio_bit_rate
= 0;
1189 int video_bit_rate
= 0;
1190 char *audio_codec_name
= "";
1191 char *video_codec_name
= "";
1192 char *audio_codec_name_extra
= "";
1193 char *video_codec_name_extra
= "";
1195 for(i
=0;i
<stream
->nb_streams
;i
++) {
1196 AVStream
*st
= stream
->streams
[i
];
1197 AVCodec
*codec
= avcodec_find_encoder(st
->codec
.codec_id
);
1198 switch(st
->codec
.codec_type
) {
1199 case CODEC_TYPE_AUDIO
:
1200 audio_bit_rate
+= st
->codec
.bit_rate
;
1202 if (*audio_codec_name
)
1203 audio_codec_name_extra
= "...";
1204 audio_codec_name
= codec
->name
;
1207 case CODEC_TYPE_VIDEO
:
1208 video_bit_rate
+= st
->codec
.bit_rate
;
1210 if (*video_codec_name
)
1211 video_codec_name_extra
= "...";
1212 video_codec_name
= codec
->name
;
1219 q
+= sprintf(q
, "<TD align=center> %s <TD align=right> %d <TD align=right> %d <TD> %s %s <TD align=right> %d <TD> %s %s",
1221 (audio_bit_rate
+ video_bit_rate
) / 1000,
1222 video_bit_rate
/ 1000, video_codec_name
, video_codec_name_extra
,
1223 audio_bit_rate
/ 1000, audio_codec_name
, audio_codec_name_extra
);
1225 q
+= sprintf(q
, "<TD>%s", stream
->feed
->filename
);
1227 q
+= sprintf(q
, "<TD>%s", stream
->feed_filename
);
1229 q
+= sprintf(q
, "\n");
1233 q
+= sprintf(q
, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1237 stream
= stream
->next
;
1239 q
+= sprintf(q
, "</TABLE>\n");
1241 stream
= first_stream
;
1242 while (stream
!= NULL
) {
1243 if (stream
->feed
== stream
) {
1244 q
+= sprintf(q
, "<h2>Feed %s</h2>", stream
->filename
);
1249 q
+= sprintf(q
, "Running as pid %d.\n", stream
->pid
);
1252 /* This is somewhat linux specific I guess */
1253 snprintf(ps_cmd
, sizeof(ps_cmd
), "ps -o \"%%cpu,bsdtime\" --no-headers %d", stream
->pid
);
1255 pid_stat
= popen(ps_cmd
, "r");
1260 if (fscanf(pid_stat
, "%10s %64s", cpuperc
, cpuused
) == 2) {
1261 q
+= sprintf(q
, "Currently using %s%% of the cpu. Total time used %s.\n",
1268 q
+= sprintf(q
, "<p>");
1270 q
+= sprintf(q
, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
1272 for (i
= 0; i
< stream
->nb_streams
; i
++) {
1273 AVStream
*st
= stream
->streams
[i
];
1274 AVCodec
*codec
= avcodec_find_encoder(st
->codec
.codec_id
);
1275 char *type
= "unknown";
1276 char parameters
[64];
1280 switch(st
->codec
.codec_type
) {
1281 case CODEC_TYPE_AUDIO
:
1284 case CODEC_TYPE_VIDEO
:
1286 sprintf(parameters
, "%dx%d, q=%d-%d, fps=%d", st
->codec
.width
, st
->codec
.height
,
1287 st
->codec
.qmin
, st
->codec
.qmax
, st
->codec
.frame_rate
/ FRAME_RATE_BASE
);
1292 q
+= sprintf(q
, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1293 i
, type
, st
->codec
.bit_rate
/1000, codec ? codec
->name
: "", parameters
);
1295 q
+= sprintf(q
, "</table>\n");
1298 stream
= stream
->next
;
1304 AVCodecContext
*enc
;
1308 stream
= first_feed
;
1309 while (stream
!= NULL
) {
1310 q
+= sprintf(q
, "<H1>Feed '%s'</H1>\n", stream
->filename
);
1311 q
+= sprintf(q
, "<TABLE>\n");
1312 q
+= sprintf(q
, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1313 for(i
=0;i
<stream
->nb_streams
;i
++) {
1314 AVStream
*st
= stream
->streams
[i
];
1315 FeedData
*fdata
= st
->priv_data
;
1318 avcodec_string(buf
, sizeof(buf
), enc
);
1319 avg
= fdata
->avg_frame_size
* (float)enc
->rate
* 8.0;
1320 if (enc
->codec
->type
== CODEC_TYPE_AUDIO
&& enc
->frame_size
> 0)
1321 avg
/= enc
->frame_size
;
1322 q
+= sprintf(q
, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1323 buf
, enc
->frame_number
, fdata
->data_count
, avg
/ 1000.0);
1325 q
+= sprintf(q
, "</TABLE>\n");
1326 stream
= stream
->next_feed
;
1331 /* connection status */
1332 q
+= sprintf(q
, "<H2>Connection Status</H2>\n");
1334 q
+= sprintf(q
, "Number of connections: %d / %d<BR>\n",
1335 nb_connections
, nb_max_connections
);
1337 q
+= sprintf(q
, "Bandwidth in use: %dk / %dk<BR>\n",
1338 nb_bandwidth
, nb_max_bandwidth
);
1340 q
+= sprintf(q
, "<TABLE>\n");
1341 q
+= sprintf(q
, "<TR><Th>#<Th>File<Th>IP<Th>State<Th>Target bits/sec<Th>Actual bits/sec<Th>Bytes transferred\n");
1342 c1
= first_http_ctx
;
1344 while (c1
!= NULL
&& q
< (char *) c
->buffer
+ c
->buffer_size
- 2048) {
1349 for (j
= 0; j
< c1
->stream
->nb_streams
; j
++) {
1350 if (c1
->feed_streams
[j
] >= 0) {
1351 bitrate
+= c1
->stream
->feed
->streams
[c1
->feed_streams
[j
]]->codec
.bit_rate
;
1356 p
= inet_ntoa(c1
->from_addr
.sin_addr
);
1357 q
+= sprintf(q
, "<TR><TD><B>%d</B><TD>%s%s <TD> %s <TD> %s <td align=right>",
1358 i
, c1
->stream
->filename
,
1359 c1
->state
== HTTPSTATE_RECEIVE_DATA ?
"(input)" : "",
1361 http_state
[c1
->state
]);
1362 q
+= fmt_bytecount(q
, bitrate
);
1363 q
+= sprintf(q
, "<td align=right>");
1364 q
+= fmt_bytecount(q
, compute_datarate(&c1
->datarate
, c1
->data_count
) * 8);
1365 q
+= sprintf(q
, "<td align=right>");
1366 q
+= fmt_bytecount(q
, c1
->data_count
);
1370 q
+= sprintf(q
, "</TABLE>\n");
1375 q
+= sprintf(q
, "<HR size=1 noshade>Generated at %s", p
);
1376 q
+= sprintf(q
, "</BODY>\n</HTML>\n");
1378 c
->buffer_ptr
= c
->buffer
;
1383 static void http_write_packet(void *opaque
,
1384 unsigned char *buf
, int size
)
1386 HTTPContext
*c
= opaque
;
1388 if (c
->buffer_ptr
== c
->buffer_end
|| !c
->buffer_ptr
)
1389 c
->buffer_ptr
= c
->buffer_end
= c
->buffer
;
1391 if (c
->buffer_end
- c
->buffer
+ size
> c
->buffer_size
) {
1392 int new_buffer_size
= c
->buffer_size
* 2;
1395 if (new_buffer_size
<= c
->buffer_end
- c
->buffer
+ size
) {
1396 new_buffer_size
= c
->buffer_end
- c
->buffer
+ size
+ c
->buffer_size
;
1399 new_buffer
= av_malloc(new_buffer_size
);
1401 memcpy(new_buffer
, c
->buffer
, c
->buffer_end
- c
->buffer
);
1402 c
->buffer_end
+= (new_buffer
- c
->buffer
);
1403 c
->buffer_ptr
+= (new_buffer
- c
->buffer
);
1405 c
->buffer
= new_buffer
;
1406 c
->buffer_size
= new_buffer_size
;
1412 memcpy(c
->buffer_end
, buf
, size
);
1413 c
->buffer_end
+= size
;
1416 static int open_input_stream(HTTPContext
*c
, const char *info
)
1419 char input_filename
[1024];
1424 /* find file name */
1425 if (c
->stream
->feed
) {
1426 strcpy(input_filename
, c
->stream
->feed
->feed_filename
);
1427 buf_size
= FFM_PACKET_SIZE
;
1428 /* compute position (absolute time) */
1429 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1430 stream_pos
= parse_date(buf
, 0);
1431 } else if (find_info_tag(buf
, sizeof(buf
), "buffer", info
)) {
1432 int prebuffer
= strtol(buf
, 0, 10);
1433 stream_pos
= gettime() - prebuffer
* 1000000;
1435 stream_pos
= gettime() - c
->stream
->prebuffer
* 1000;
1438 strcpy(input_filename
, c
->stream
->feed_filename
);
1440 /* compute position (relative time) */
1441 if (find_info_tag(buf
, sizeof(buf
), "date", info
)) {
1442 stream_pos
= parse_date(buf
, 1);
1447 if (input_filename
[0] == '\0')
1451 if (av_open_input_file(&s
, input_filename
, NULL
, buf_size
, NULL
) < 0)
1455 if (c
->fmt_in
->iformat
->read_seek
) {
1456 c
->fmt_in
->iformat
->read_seek(c
->fmt_in
, stream_pos
);
1459 // printf("stream %s opened pos=%0.6f\n", input_filename, stream_pos / 1000000.0);
1463 static int http_prepare_data(HTTPContext
*c
)
1468 case HTTPSTATE_SEND_DATA_HEADER
:
1469 memset(&c
->fmt_ctx
, 0, sizeof(c
->fmt_ctx
));
1470 pstrcpy(c
->fmt_ctx
.author
, sizeof(c
->fmt_ctx
.author
), c
->stream
->author
);
1471 pstrcpy(c
->fmt_ctx
.comment
, sizeof(c
->fmt_ctx
.comment
), c
->stream
->comment
);
1472 pstrcpy(c
->fmt_ctx
.copyright
, sizeof(c
->fmt_ctx
.copyright
), c
->stream
->copyright
);
1473 pstrcpy(c
->fmt_ctx
.title
, sizeof(c
->fmt_ctx
.title
), c
->stream
->title
);
1475 if (c
->stream
->feed
) {
1476 /* open output stream by using specified codecs */
1477 c
->fmt_ctx
.oformat
= c
->stream
->fmt
;
1478 c
->fmt_ctx
.nb_streams
= c
->stream
->nb_streams
;
1479 for(i
=0;i
<c
->fmt_ctx
.nb_streams
;i
++) {
1481 st
= av_mallocz(sizeof(AVStream
));
1482 c
->fmt_ctx
.streams
[i
] = st
;
1483 if (c
->stream
->feed
== c
->stream
)
1484 memcpy(st
, c
->stream
->streams
[i
], sizeof(AVStream
));
1486 memcpy(st
, c
->stream
->feed
->streams
[c
->stream
->feed_streams
[i
]], sizeof(AVStream
));
1488 st
->codec
.frame_number
= 0; /* XXX: should be done in
1489 AVStream, not in codec */
1491 c
->got_key_frame
= 0;
1493 /* open output stream by using codecs in specified file */
1494 c
->fmt_ctx
.oformat
= c
->stream
->fmt
;
1495 c
->fmt_ctx
.nb_streams
= c
->fmt_in
->nb_streams
;
1496 for(i
=0;i
<c
->fmt_ctx
.nb_streams
;i
++) {
1498 st
= av_mallocz(sizeof(AVStream
));
1499 c
->fmt_ctx
.streams
[i
] = st
;
1500 memcpy(st
, c
->fmt_in
->streams
[i
], sizeof(AVStream
));
1501 st
->codec
.frame_number
= 0; /* XXX: should be done in
1502 AVStream, not in codec */
1504 c
->got_key_frame
= 0;
1506 init_put_byte(&c
->fmt_ctx
.pb
, c
->pbuffer
, c
->pbuffer_size
,
1507 1, c
, NULL
, http_write_packet
, NULL
);
1508 c
->fmt_ctx
.pb
.is_streamed
= 1;
1509 /* prepare header */
1510 av_write_header(&c
->fmt_ctx
);
1511 c
->state
= HTTPSTATE_SEND_DATA
;
1512 c
->last_packet_sent
= 0;
1514 case HTTPSTATE_SEND_DATA
:
1515 /* find a new packet */
1517 fifo_total_size
= http_fifo_write_count
- c
->last_http_fifo_write_count
;
1518 if (fifo_total_size
>= ((3 * FIFO_MAX_SIZE
) / 4)) {
1519 /* overflow : resync. We suppose that wptr is at this
1520 point a pointer to a valid packet */
1521 c
->rptr
= http_fifo
.wptr
;
1522 c
->got_key_frame
= 0;
1525 start_rptr
= c
->rptr
;
1526 if (fifo_read(&http_fifo
, (UINT8
*)&hdr
, sizeof(hdr
), &c
->rptr
) < 0)
1528 payload_size
= ntohs(hdr
.payload_size
);
1529 payload
= av_malloc(payload_size
);
1530 if (fifo_read(&http_fifo
, payload
, payload_size
, &c
->rptr
) < 0) {
1531 /* cannot read all the payload */
1533 c
->rptr
= start_rptr
;
1537 c
->last_http_fifo_write_count
= http_fifo_write_count
-
1538 fifo_size(&http_fifo
, c
->rptr
);
1540 if (c
->stream
->stream_type
!= STREAM_TYPE_MASTER
) {
1541 /* test if the packet can be handled by this format */
1543 for(i
=0;i
<c
->fmt_ctx
.nb_streams
;i
++) {
1544 AVStream
*st
= c
->fmt_ctx
.streams
[i
];
1545 if (test_header(&hdr
, &st
->codec
)) {
1546 /* only begin sending when got a key frame */
1547 if (st
->codec
.key_frame
)
1548 c
->got_key_frame
|= 1 << i
;
1549 if (c
->got_key_frame
& (1 << i
)) {
1550 ret
= c
->fmt_ctx
.format
->write_packet(&c
->fmt_ctx
, i
,
1551 payload
, payload_size
);
1557 /* must send trailer now */
1558 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
1561 /* master case : send everything */
1564 memcpy(q
, &hdr
, sizeof(hdr
));
1566 memcpy(q
, payload
, payload_size
);
1568 c
->buffer_ptr
= c
->buffer
;
1576 /* read a packet from the input stream */
1577 if (c
->stream
->feed
) {
1578 ffm_set_write_index(c
->fmt_in
,
1579 c
->stream
->feed
->feed_write_index
,
1580 c
->stream
->feed
->feed_size
);
1583 if (c
->stream
->max_time
&&
1584 c
->stream
->max_time
+ c
->start_time
- cur_time
< 0) {
1585 /* We have timed out */
1586 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
1587 } else if (av_read_packet(c
->fmt_in
, &pkt
) < 0) {
1588 if (c
->stream
->feed
&& c
->stream
->feed
->feed_opened
) {
1589 /* if coming from feed, it means we reached the end of the
1590 ffm file, so must wait for more data */
1591 c
->state
= HTTPSTATE_WAIT_FEED
;
1592 return 1; /* state changed */
1594 /* must send trailer now because eof or error */
1595 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
1598 /* send it to the appropriate stream */
1599 if (c
->stream
->feed
) {
1600 /* if coming from a feed, select the right stream */
1601 if (c
->switch_pending
) {
1602 c
->switch_pending
= 0;
1603 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
1604 if (c
->switch_feed_streams
[i
] == pkt
.stream_index
) {
1605 if (pkt
.flags
& PKT_FLAG_KEY
) {
1606 do_switch_stream(c
, i
);
1609 if (c
->switch_feed_streams
[i
] >= 0) {
1610 c
->switch_pending
= 1;
1614 for(i
=0;i
<c
->stream
->nb_streams
;i
++) {
1615 if (c
->feed_streams
[i
] == pkt
.stream_index
) {
1616 pkt
.stream_index
= i
;
1617 if (pkt
.flags
& PKT_FLAG_KEY
) {
1618 c
->got_key_frame
|= 1 << i
;
1620 /* See if we have all the key frames, then
1621 * we start to send. This logic is not quite
1622 * right, but it works for the case of a
1623 * single video stream with one or more
1624 * audio streams (for which every frame is
1625 * typically a key frame).
1627 if (!c
->stream
->send_on_key
|| ((c
->got_key_frame
+ 1) >> c
->stream
->nb_streams
)) {
1633 AVCodecContext
*codec
;
1636 codec
= &c
->fmt_ctx
.streams
[pkt
.stream_index
]->codec
;
1638 codec
->key_frame
= ((pkt
.flags
& PKT_FLAG_KEY
) != 0);
1641 if (codec
->codec_type
== CODEC_TYPE_AUDIO
) {
1642 codec
->frame_size
= (codec
->sample_rate
* pkt
.duration
+ 500000) / 1000000;
1643 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
1647 if (av_write_packet(&c
->fmt_ctx
, &pkt
, 0))
1648 c
->state
= HTTPSTATE_SEND_DATA_TRAILER
;
1650 codec
->frame_number
++;
1653 av_free_packet(&pkt
);
1658 case HTTPSTATE_SEND_DATA_TRAILER
:
1659 /* last packet test ? */
1660 if (c
->last_packet_sent
)
1662 /* prepare header */
1663 av_write_trailer(&c
->fmt_ctx
);
1664 c
->last_packet_sent
= 1;
1670 /* should convert the format at the same time */
1671 static int http_send_data(HTTPContext
*c
)
1675 while (c
->buffer_ptr
>= c
->buffer_end
) {
1676 ret
= http_prepare_data(c
);
1679 else if (ret
== 0) {
1682 /* state change requested */
1687 if (c
->buffer_end
> c
->buffer_ptr
) {
1688 len
= write(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
1690 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
1691 /* error : close connection */
1695 c
->buffer_ptr
+= len
;
1696 c
->data_count
+= len
;
1697 update_datarate(&c
->datarate
, c
->data_count
);
1699 c
->stream
->bytes_served
+= len
;
1705 static int http_start_receive_data(HTTPContext
*c
)
1709 if (c
->stream
->feed_opened
)
1713 fd
= open(c
->stream
->feed_filename
, O_RDWR
);
1718 c
->stream
->feed_write_index
= ffm_read_write_index(fd
);
1719 c
->stream
->feed_size
= lseek(fd
, 0, SEEK_END
);
1720 lseek(fd
, 0, SEEK_SET
);
1722 /* init buffer input */
1723 c
->buffer_ptr
= c
->buffer
;
1724 c
->buffer_end
= c
->buffer
+ FFM_PACKET_SIZE
;
1725 c
->stream
->feed_opened
= 1;
1729 static int http_receive_data(HTTPContext
*c
)
1733 if (c
->buffer_end
> c
->buffer_ptr
) {
1736 len
= read(c
->fd
, c
->buffer_ptr
, c
->buffer_end
- c
->buffer_ptr
);
1738 if (errno
!= EAGAIN
&& errno
!= EINTR
) {
1739 /* error : close connection */
1742 } else if (len
== 0) {
1743 /* end of connection : close it */
1746 c
->buffer_ptr
+= len
;
1747 c
->data_count
+= len
;
1748 update_datarate(&c
->datarate
, c
->data_count
);
1752 if (c
->buffer_ptr
>= c
->buffer_end
) {
1753 FFStream
*feed
= c
->stream
;
1754 /* a packet has been received : write it in the store, except
1756 if (c
->data_count
> FFM_PACKET_SIZE
) {
1758 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
1759 /* XXX: use llseek or url_seek */
1760 lseek(c
->feed_fd
, feed
->feed_write_index
, SEEK_SET
);
1761 write(c
->feed_fd
, c
->buffer
, FFM_PACKET_SIZE
);
1763 feed
->feed_write_index
+= FFM_PACKET_SIZE
;
1764 /* update file size */
1765 if (feed
->feed_write_index
> c
->stream
->feed_size
)
1766 feed
->feed_size
= feed
->feed_write_index
;
1768 /* handle wrap around if max file size reached */
1769 if (feed
->feed_write_index
>= c
->stream
->feed_max_size
)
1770 feed
->feed_write_index
= FFM_PACKET_SIZE
;
1773 ffm_write_write_index(c
->feed_fd
, feed
->feed_write_index
);
1775 /* wake up any waiting connections */
1776 for(c1
= first_http_ctx
; c1
!= NULL
; c1
= c1
->next
) {
1777 if (c1
->state
== HTTPSTATE_WAIT_FEED
&&
1778 c1
->stream
->feed
== c
->stream
->feed
) {
1779 c1
->state
= HTTPSTATE_SEND_DATA
;
1783 /* We have a header in our hands that contains useful data */
1785 AVInputFormat
*fmt_in
;
1786 ByteIOContext
*pb
= &s
.pb
;
1789 memset(&s
, 0, sizeof(s
));
1791 url_open_buf(pb
, c
->buffer
, c
->buffer_end
- c
->buffer
, URL_RDONLY
);
1792 pb
->buf_end
= c
->buffer_end
; /* ?? */
1793 pb
->is_streamed
= 1;
1795 /* use feed output format name to find corresponding input format */
1796 fmt_in
= av_find_input_format(feed
->fmt
->name
);
1800 s
.priv_data
= av_mallocz(fmt_in
->priv_data_size
);
1804 if (fmt_in
->read_header(&s
, 0) < 0) {
1805 av_freep(&s
.priv_data
);
1809 /* Now we have the actual streams */
1810 if (s
.nb_streams
!= feed
->nb_streams
) {
1811 av_freep(&s
.priv_data
);
1814 for (i
= 0; i
< s
.nb_streams
; i
++) {
1815 memcpy(&feed
->streams
[i
]->codec
,
1816 &s
.streams
[i
]->codec
, sizeof(AVCodecContext
));
1818 av_freep(&s
.priv_data
);
1820 c
->buffer_ptr
= c
->buffer
;
1825 c
->stream
->feed_opened
= 0;
1830 /* return the stream number in the feed */
1831 int add_av_stream(FFStream
*feed
,
1835 AVCodecContext
*av
, *av1
;
1839 for(i
=0;i
<feed
->nb_streams
;i
++) {
1840 st
= feed
->streams
[i
];
1842 if (av1
->codec_id
== av
->codec_id
&&
1843 av1
->codec_type
== av
->codec_type
&&
1844 av1
->bit_rate
== av
->bit_rate
) {
1846 switch(av
->codec_type
) {
1847 case CODEC_TYPE_AUDIO
:
1848 if (av1
->channels
== av
->channels
&&
1849 av1
->sample_rate
== av
->sample_rate
)
1852 case CODEC_TYPE_VIDEO
:
1853 if (av1
->width
== av
->width
&&
1854 av1
->height
== av
->height
&&
1855 av1
->frame_rate
== av
->frame_rate
&&
1856 av1
->gop_size
== av
->gop_size
)
1865 fst
= av_mallocz(sizeof(AVStream
));
1868 fst
->priv_data
= av_mallocz(sizeof(FeedData
));
1869 memcpy(&fst
->codec
, av
, sizeof(AVCodecContext
));
1870 feed
->streams
[feed
->nb_streams
++] = fst
;
1871 return feed
->nb_streams
- 1;
1876 /* compute the needed AVStream for each feed */
1877 void build_feed_streams(void)
1879 FFStream
*stream
, *feed
;
1882 /* gather all streams */
1883 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
1884 feed
= stream
->feed
;
1886 if (!stream
->is_feed
) {
1887 for(i
=0;i
<stream
->nb_streams
;i
++) {
1888 stream
->feed_streams
[i
] = add_av_stream(feed
, stream
->streams
[i
]);
1894 /* gather all streams */
1895 for(stream
= first_stream
; stream
!= NULL
; stream
= stream
->next
) {
1896 feed
= stream
->feed
;
1898 if (stream
->is_feed
) {
1899 for(i
=0;i
<stream
->nb_streams
;i
++) {
1900 stream
->feed_streams
[i
] = i
;
1906 /* create feed files if needed */
1907 for(feed
= first_feed
; feed
!= NULL
; feed
= feed
->next_feed
) {
1910 if (!url_exist(feed
->feed_filename
)) {
1911 AVFormatContext s1
, *s
= &s1
;
1913 /* only write the header of the ffm file */
1914 if (url_fopen(&s
->pb
, feed
->feed_filename
, URL_WRONLY
) < 0) {
1915 fprintf(stderr
, "Could not open output feed file '%s'\n",
1916 feed
->feed_filename
);
1919 s
->oformat
= feed
->fmt
;
1920 s
->nb_streams
= feed
->nb_streams
;
1921 for(i
=0;i
<s
->nb_streams
;i
++) {
1923 st
= feed
->streams
[i
];
1927 /* XXX: need better api */
1928 av_freep(&s
->priv_data
);
1931 /* get feed size and write index */
1932 fd
= open(feed
->feed_filename
, O_RDONLY
);
1934 fprintf(stderr
, "Could not open output feed file '%s'\n",
1935 feed
->feed_filename
);
1939 feed
->feed_write_index
= ffm_read_write_index(fd
);
1940 feed
->feed_size
= lseek(fd
, 0, SEEK_END
);
1941 /* ensure that we do not wrap before the end of file */
1942 if (feed
->feed_max_size
< feed
->feed_size
)
1943 feed
->feed_max_size
= feed
->feed_size
;
1949 static void get_arg(char *buf
, int buf_size
, const char **pp
)
1956 while (isspace(*p
)) p
++;
1959 if (*p
== '\"' || *p
== '\'')
1971 if ((q
- buf
) < buf_size
- 1)
1976 if (quote
&& *p
== quote
)
1981 /* add a codec and set the default parameters */
1982 void add_codec(FFStream
*stream
, AVCodecContext
*av
)
1986 /* compute default parameters */
1987 switch(av
->codec_type
) {
1988 case CODEC_TYPE_AUDIO
:
1989 if (av
->bit_rate
== 0)
1990 av
->bit_rate
= 64000;
1991 if (av
->sample_rate
== 0)
1992 av
->sample_rate
= 22050;
1993 if (av
->channels
== 0)
1996 case CODEC_TYPE_VIDEO
:
1997 if (av
->bit_rate
== 0)
1998 av
->bit_rate
= 64000;
1999 if (av
->frame_rate
== 0)
2000 av
->frame_rate
= 5 * FRAME_RATE_BASE
;
2001 if (av
->width
== 0 || av
->height
== 0) {
2005 /* Bitrate tolerance is less for streaming */
2006 if (av
->bit_rate_tolerance
== 0)
2007 av
->bit_rate_tolerance
= av
->bit_rate
/ 4;
2012 if (av
->max_qdiff
== 0)
2014 av
->qcompress
= 0.5;
2022 st
= av_mallocz(sizeof(AVStream
));
2025 stream
->streams
[stream
->nb_streams
++] = st
;
2026 memcpy(&st
->codec
, av
, sizeof(AVCodecContext
));
2029 int opt_audio_codec(const char *arg
)
2035 if (!strcmp(p
->name
, arg
) && p
->type
== CODEC_TYPE_AUDIO
)
2040 return CODEC_ID_NONE
;
2046 int opt_video_codec(const char *arg
)
2052 if (!strcmp(p
->name
, arg
) && p
->type
== CODEC_TYPE_VIDEO
)
2057 return CODEC_ID_NONE
;
2063 int parse_ffconfig(const char *filename
)
2070 int val
, errors
, line_num
;
2071 FFStream
**last_stream
, *stream
, *redirect
;
2072 FFStream
**last_feed
, *feed
;
2073 AVCodecContext audio_enc
, video_enc
;
2074 int audio_id
, video_id
;
2076 f
= fopen(filename
, "r");
2084 first_stream
= NULL
;
2085 last_stream
= &first_stream
;
2087 last_feed
= &first_feed
;
2091 audio_id
= CODEC_ID_NONE
;
2092 video_id
= CODEC_ID_NONE
;
2094 if (fgets(line
, sizeof(line
), f
) == NULL
)
2100 if (*p
== '\0' || *p
== '#')
2103 get_arg(cmd
, sizeof(cmd
), &p
);
2105 if (!strcasecmp(cmd
, "Port")) {
2106 get_arg(arg
, sizeof(arg
), &p
);
2107 my_addr
.sin_port
= htons (atoi(arg
));
2108 } else if (!strcasecmp(cmd
, "BindAddress")) {
2109 get_arg(arg
, sizeof(arg
), &p
);
2110 if (!inet_aton(arg
, &my_addr
.sin_addr
)) {
2111 fprintf(stderr
, "%s:%d: Invalid IP address: %s\n",
2112 filename
, line_num
, arg
);
2115 } else if (!strcasecmp(cmd
, "MaxClients")) {
2116 get_arg(arg
, sizeof(arg
), &p
);
2118 if (val
< 1 || val
> HTTP_MAX_CONNECTIONS
) {
2119 fprintf(stderr
, "%s:%d: Invalid MaxClients: %s\n",
2120 filename
, line_num
, arg
);
2123 nb_max_connections
= val
;
2125 } else if (!strcasecmp(cmd
, "MaxBandwidth")) {
2126 get_arg(arg
, sizeof(arg
), &p
);
2128 if (val
< 10 || val
> 100000) {
2129 fprintf(stderr
, "%s:%d: Invalid MaxBandwidth: %s\n",
2130 filename
, line_num
, arg
);
2133 nb_max_bandwidth
= val
;
2135 } else if (!strcasecmp(cmd
, "CustomLog")) {
2136 get_arg(logfilename
, sizeof(logfilename
), &p
);
2137 } else if (!strcasecmp(cmd
, "<Feed")) {
2138 /*********************************************/
2139 /* Feed related options */
2141 if (stream
|| feed
) {
2142 fprintf(stderr
, "%s:%d: Already in a tag\n",
2143 filename
, line_num
);
2145 feed
= av_mallocz(sizeof(FFStream
));
2146 /* add in stream list */
2147 *last_stream
= feed
;
2148 last_stream
= &feed
->next
;
2149 /* add in feed list */
2151 last_feed
= &feed
->next_feed
;
2153 get_arg(feed
->filename
, sizeof(feed
->filename
), &p
);
2154 q
= strrchr(feed
->filename
, '>');
2157 feed
->fmt
= guess_format("ffm", NULL
, NULL
);
2158 /* defaut feed file */
2159 snprintf(feed
->feed_filename
, sizeof(feed
->feed_filename
),
2160 "/tmp/%s.ffm", feed
->filename
);
2161 feed
->feed_max_size
= 5 * 1024 * 1024;
2163 feed
->feed
= feed
; /* self feeding :-) */
2165 } else if (!strcasecmp(cmd
, "Launch")) {
2169 feed
->child_argv
= (char **) av_mallocz(64 * sizeof(char *));
2171 feed
->child_argv
[0] = av_malloc(7);
2172 strcpy(feed
->child_argv
[0], "ffmpeg");
2174 for (i
= 1; i
< 62; i
++) {
2177 get_arg(argbuf
, sizeof(argbuf
), &p
);
2181 feed
->child_argv
[i
] = av_malloc(strlen(argbuf
+ 1));
2182 strcpy(feed
->child_argv
[i
], argbuf
);
2185 feed
->child_argv
[i
] = av_malloc(30 + strlen(feed
->filename
));
2187 snprintf(feed
->child_argv
[i
], 256, "http://127.0.0.1:%d/%s",
2188 ntohs(my_addr
.sin_port
), feed
->filename
);
2190 } else if (!strcasecmp(cmd
, "File")) {
2192 get_arg(feed
->feed_filename
, sizeof(feed
->feed_filename
), &p
);
2193 } else if (stream
) {
2194 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
2196 } else if (!strcasecmp(cmd
, "FileMaxSize")) {
2201 get_arg(arg
, sizeof(arg
), &p
);
2203 fsize
= strtod(p1
, (char **)&p1
);
2204 switch(toupper(*p1
)) {
2209 fsize
*= 1024 * 1024;
2212 fsize
*= 1024 * 1024 * 1024;
2215 feed
->feed_max_size
= (INT64
)fsize
;
2217 } else if (!strcasecmp(cmd
, "</Feed>")) {
2219 fprintf(stderr
, "%s:%d: No corresponding <Feed> for </Feed>\n",
2220 filename
, line_num
);
2223 /* Make sure that we start out clean */
2224 if (unlink(feed
->feed_filename
) < 0
2225 && errno
!= ENOENT
) {
2226 fprintf(stderr
, "%s:%d: Unable to clean old feed file '%s': %s\n",
2227 filename
, line_num
, feed
->feed_filename
, strerror(errno
));
2232 } else if (!strcasecmp(cmd
, "<Stream")) {
2233 /*********************************************/
2234 /* Stream related options */
2236 if (stream
|| feed
) {
2237 fprintf(stderr
, "%s:%d: Already in a tag\n",
2238 filename
, line_num
);
2240 stream
= av_mallocz(sizeof(FFStream
));
2241 *last_stream
= stream
;
2242 last_stream
= &stream
->next
;
2244 get_arg(stream
->filename
, sizeof(stream
->filename
), &p
);
2245 q
= strrchr(stream
->filename
, '>');
2248 stream
->fmt
= guess_format(NULL
, stream
->filename
, NULL
);
2249 memset(&audio_enc
, 0, sizeof(AVCodecContext
));
2250 memset(&video_enc
, 0, sizeof(AVCodecContext
));
2251 audio_id
= CODEC_ID_NONE
;
2252 video_id
= CODEC_ID_NONE
;
2254 audio_id
= stream
->fmt
->audio_codec
;
2255 video_id
= stream
->fmt
->video_codec
;
2258 } else if (!strcasecmp(cmd
, "Feed")) {
2259 get_arg(arg
, sizeof(arg
), &p
);
2264 while (sfeed
!= NULL
) {
2265 if (!strcmp(sfeed
->filename
, arg
))
2267 sfeed
= sfeed
->next_feed
;
2270 fprintf(stderr
, "%s:%d: feed '%s' not defined\n",
2271 filename
, line_num
, arg
);
2273 stream
->feed
= sfeed
;
2276 } else if (!strcasecmp(cmd
, "Format")) {
2277 get_arg(arg
, sizeof(arg
), &p
);
2278 if (!strcmp(arg
, "status")) {
2279 stream
->stream_type
= STREAM_TYPE_STATUS
;
2282 stream
->stream_type
= STREAM_TYPE_LIVE
;
2283 /* jpeg cannot be used here, so use single frame jpeg */
2284 if (!strcmp(arg
, "jpeg"))
2285 strcpy(arg
, "singlejpeg");
2286 stream
->fmt
= guess_format(arg
, NULL
, NULL
);
2288 fprintf(stderr
, "%s:%d: Unknown Format: %s\n",
2289 filename
, line_num
, arg
);
2294 audio_id
= stream
->fmt
->audio_codec
;
2295 video_id
= stream
->fmt
->video_codec
;
2297 } else if (!strcasecmp(cmd
, "FaviconURL")) {
2298 if (stream
&& stream
->stream_type
== STREAM_TYPE_STATUS
) {
2299 get_arg(stream
->feed_filename
, sizeof(stream
->feed_filename
), &p
);
2301 fprintf(stderr
, "%s:%d: FaviconURL only permitted for status streams\n",
2302 filename
, line_num
);
2305 } else if (!strcasecmp(cmd
, "Author")) {
2307 get_arg(stream
->author
, sizeof(stream
->author
), &p
);
2309 } else if (!strcasecmp(cmd
, "Comment")) {
2311 get_arg(stream
->comment
, sizeof(stream
->comment
), &p
);
2313 } else if (!strcasecmp(cmd
, "Copyright")) {
2315 get_arg(stream
->copyright
, sizeof(stream
->copyright
), &p
);
2317 } else if (!strcasecmp(cmd
, "Title")) {
2319 get_arg(stream
->title
, sizeof(stream
->title
), &p
);
2321 } else if (!strcasecmp(cmd
, "Preroll")) {
2322 get_arg(arg
, sizeof(arg
), &p
);
2324 stream
->prebuffer
= atoi(arg
) * 1000;
2326 } else if (!strcasecmp(cmd
, "StartSendOnKey")) {
2328 stream
->send_on_key
= 1;
2330 } else if (!strcasecmp(cmd
, "AudioCodec")) {
2331 get_arg(arg
, sizeof(arg
), &p
);
2332 audio_id
= opt_audio_codec(arg
);
2333 if (audio_id
== CODEC_ID_NONE
) {
2334 fprintf(stderr
, "%s:%d: Unknown AudioCodec: %s\n",
2335 filename
, line_num
, arg
);
2338 } else if (!strcasecmp(cmd
, "VideoCodec")) {
2339 get_arg(arg
, sizeof(arg
), &p
);
2340 video_id
= opt_video_codec(arg
);
2341 if (video_id
== CODEC_ID_NONE
) {
2342 fprintf(stderr
, "%s:%d: Unknown VideoCodec: %s\n",
2343 filename
, line_num
, arg
);
2346 } else if (!strcasecmp(cmd
, "MaxTime")) {
2347 get_arg(arg
, sizeof(arg
), &p
);
2349 stream
->max_time
= atoi(arg
) * 1000;
2351 } else if (!strcasecmp(cmd
, "AudioBitRate")) {
2352 get_arg(arg
, sizeof(arg
), &p
);
2354 audio_enc
.bit_rate
= atoi(arg
) * 1000;
2356 } else if (!strcasecmp(cmd
, "AudioChannels")) {
2357 get_arg(arg
, sizeof(arg
), &p
);
2359 audio_enc
.channels
= atoi(arg
);
2361 } else if (!strcasecmp(cmd
, "AudioSampleRate")) {
2362 get_arg(arg
, sizeof(arg
), &p
);
2364 audio_enc
.sample_rate
= atoi(arg
);
2366 } else if (!strcasecmp(cmd
, "VideoBitRate")) {
2367 get_arg(arg
, sizeof(arg
), &p
);
2369 video_enc
.bit_rate
= atoi(arg
) * 1000;
2371 } else if (!strcasecmp(cmd
, "VideoSize")) {
2372 get_arg(arg
, sizeof(arg
), &p
);
2374 parse_image_size(&video_enc
.width
, &video_enc
.height
, arg
);
2375 if ((video_enc
.width
% 16) != 0 ||
2376 (video_enc
.height
% 16) != 0) {
2377 fprintf(stderr
, "%s:%d: Image size must be a multiple of 16\n",
2378 filename
, line_num
);
2382 } else if (!strcasecmp(cmd
, "VideoFrameRate")) {
2383 get_arg(arg
, sizeof(arg
), &p
);
2385 video_enc
.frame_rate
= (int)(strtod(arg
, NULL
) * FRAME_RATE_BASE
);
2387 } else if (!strcasecmp(cmd
, "VideoGopSize")) {
2388 get_arg(arg
, sizeof(arg
), &p
);
2390 video_enc
.gop_size
= atoi(arg
);
2392 } else if (!strcasecmp(cmd
, "VideoIntraOnly")) {
2394 video_enc
.gop_size
= 1;
2396 } else if (!strcasecmp(cmd
, "VideoHighQuality")) {
2398 video_enc
.flags
|= CODEC_FLAG_HQ
;
2400 } else if (!strcasecmp(cmd
, "VideoQDiff")) {
2402 video_enc
.max_qdiff
= atoi(arg
);
2403 if (video_enc
.max_qdiff
< 1 || video_enc
.max_qdiff
> 31) {
2404 fprintf(stderr
, "%s:%d: VideoQDiff out of range\n",
2405 filename
, line_num
);
2409 } else if (!strcasecmp(cmd
, "VideoQMax")) {
2411 video_enc
.qmax
= atoi(arg
);
2412 if (video_enc
.qmax
< 1 || video_enc
.qmax
> 31) {
2413 fprintf(stderr
, "%s:%d: VideoQMax out of range\n",
2414 filename
, line_num
);
2418 } else if (!strcasecmp(cmd
, "VideoQMin")) {
2420 video_enc
.qmin
= atoi(arg
);
2421 if (video_enc
.qmin
< 1 || video_enc
.qmin
> 31) {
2422 fprintf(stderr
, "%s:%d: VideoQMin out of range\n",
2423 filename
, line_num
);
2427 } else if (!strcasecmp(cmd
, "NoVideo")) {
2428 video_id
= CODEC_ID_NONE
;
2429 } else if (!strcasecmp(cmd
, "NoAudio")) {
2430 audio_id
= CODEC_ID_NONE
;
2431 } else if (!strcasecmp(cmd
, "</Stream>")) {
2433 fprintf(stderr
, "%s:%d: No corresponding <Stream> for </Stream>\n",
2434 filename
, line_num
);
2437 if (stream
->feed
&& stream
->fmt
&& strcmp(stream
->fmt
->name
, "ffm") != 0) {
2438 if (audio_id
!= CODEC_ID_NONE
) {
2439 audio_enc
.codec_type
= CODEC_TYPE_AUDIO
;
2440 audio_enc
.codec_id
= audio_id
;
2441 add_codec(stream
, &audio_enc
);
2443 if (video_id
!= CODEC_ID_NONE
) {
2444 video_enc
.codec_type
= CODEC_TYPE_VIDEO
;
2445 video_enc
.codec_id
= video_id
;
2446 add_codec(stream
, &video_enc
);
2450 } else if (!strcasecmp(cmd
, "<Redirect")) {
2451 /*********************************************/
2453 if (stream
|| feed
|| redirect
) {
2454 fprintf(stderr
, "%s:%d: Already in a tag\n",
2455 filename
, line_num
);
2458 redirect
= av_mallocz(sizeof(FFStream
));
2459 *last_stream
= redirect
;
2460 last_stream
= &redirect
->next
;
2462 get_arg(redirect
->filename
, sizeof(redirect
->filename
), &p
);
2463 q
= strrchr(redirect
->filename
, '>');
2466 redirect
->stream_type
= STREAM_TYPE_REDIRECT
;
2468 } else if (!strcasecmp(cmd
, "URL")) {
2470 get_arg(redirect
->feed_filename
, sizeof(redirect
->feed_filename
), &p
);
2472 } else if (!strcasecmp(cmd
, "</Redirect>")) {
2474 fprintf(stderr
, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
2475 filename
, line_num
);
2478 if (!redirect
->feed_filename
[0]) {
2479 fprintf(stderr
, "%s:%d: No URL found for <Redirect>\n",
2480 filename
, line_num
);
2485 fprintf(stderr
, "%s:%d: Incorrect keyword: '%s'\n",
2486 filename
, line_num
, cmd
);
2499 void *http_server_thread(void *arg
)
2501 http_server(my_addr
);
2506 static void write_packet(FFCodec
*ffenc
,
2507 UINT8
*buf
, int size
)
2510 AVCodecContext
*enc
= &ffenc
->enc
;
2512 mk_header(&hdr
, enc
, size
);
2513 wptr
= http_fifo
.wptr
;
2514 fifo_write(&http_fifo
, (UINT8
*)&hdr
, sizeof(hdr
), &wptr
);
2515 fifo_write(&http_fifo
, buf
, size
, &wptr
);
2516 /* atomic modification of wptr */
2517 http_fifo
.wptr
= wptr
;
2518 ffenc
->data_count
+= size
;
2519 ffenc
->avg_frame_size
= ffenc
->avg_frame_size
* AVG_COEF
+ size
* (1.0 - AVG_COEF
);
2525 printf("ffserver version " FFMPEG_VERSION
", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
2526 "usage: ffserver [-L] [-h] [-f configfile]\n"
2527 "Hyper fast multi format Audio/Video streaming server\n"
2529 "-L : print the LICENCE\n"
2531 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
2538 "ffserver version " FFMPEG_VERSION
"\n"
2539 "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
2540 "This library is free software; you can redistribute it and/or\n"
2541 "modify it under the terms of the GNU Lesser General Public\n"
2542 "License as published by the Free Software Foundation; either\n"
2543 "version 2 of the License, or (at your option) any later version.\n"
2545 "This library is distributed in the hope that it will be useful,\n"
2546 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
2547 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
2548 "Lesser General Public License for more details.\n"
2550 "You should have received a copy of the GNU Lesser General Public\n"
2551 "License along with this library; if not, write to the Free Software\n"
2552 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
2556 static void handle_child_exit(int sig
)
2561 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0) {
2564 for (feed
= first_feed
; feed
; feed
= feed
->next
) {
2565 if (feed
->pid
== pid
) {
2566 int uptime
= time(0) - feed
->pid_start
;
2569 fprintf(stderr
, "%s: Pid %d exited with status %d after %d seconds\n", feed
->filename
, pid
, status
, uptime
);
2572 /* Turn off any more restarts */
2573 feed
->child_argv
= 0;
2579 need_to_start_children
= 1;
2582 int main(int argc
, char **argv
)
2584 const char *config_filename
;
2586 struct sigaction sigact
;
2590 config_filename
= "/etc/ffserver.conf";
2592 my_program_name
= argv
[0];
2595 c
= getopt_long_only(argc
, argv
, "ndLh?f:", NULL
, NULL
);
2613 config_filename
= optarg
;
2620 putenv("http_proxy"); /* Kill the http_proxy */
2622 /* address on which the server will handle connections */
2623 my_addr
.sin_family
= AF_INET
;
2624 my_addr
.sin_port
= htons (8080);
2625 my_addr
.sin_addr
.s_addr
= htonl (INADDR_ANY
);
2626 nb_max_connections
= 5;
2627 nb_max_bandwidth
= 1000;
2628 first_stream
= NULL
;
2629 logfilename
[0] = '\0';
2631 memset(&sigact
, 0, sizeof(sigact
));
2632 sigact
.sa_handler
= handle_child_exit
;
2633 sigact
.sa_flags
= SA_NOCLDSTOP
| SA_RESTART
;
2634 sigaction(SIGCHLD
, &sigact
, 0);
2636 if (parse_ffconfig(config_filename
) < 0) {
2637 fprintf(stderr
, "Incorrect config file - exiting.\n");
2641 build_feed_streams();
2644 signal(SIGPIPE
, SIG_IGN
);
2646 /* open log file if needed */
2647 if (logfilename
[0] != '\0') {
2648 if (!strcmp(logfilename
, "-"))
2651 logfile
= fopen(logfilename
, "w");
2654 if (http_server(my_addr
) < 0) {
2655 fprintf(stderr
, "Could not start http server\n");