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>
34 #include <arpa/inet.h>
39 /* maximum number of simultaneous HTTP connections */
40 #define HTTP_MAX_CONNECTIONS 2000
43 HTTPSTATE_WAIT_REQUEST,
44 HTTPSTATE_SEND_HEADER,
45 HTTPSTATE_SEND_DATA_HEADER,
47 HTTPSTATE_SEND_DATA_TRAILER,
48 HTTPSTATE_RECEIVE_DATA,
52 const char *http_state[] = {
62 #define IOBUFFER_MAX_SIZE 32768
63 #define PACKET_MAX_SIZE 16384
65 /* coef for exponential mean for bitrate estimation in statistics */
68 /* timeouts are in ms */
69 #define REQUEST_TIMEOUT (15 * 1000)
70 #define SYNC_TIMEOUT (10 * 1000)
72 /* context associated with one connection */
73 typedef struct HTTPContext {
75 int fd; /* socket file descriptor */
76 struct sockaddr_in from_addr; /* origin */
77 struct pollfd *poll_entry; /* used when polling */
79 UINT8 *buffer_ptr, *buffer_end;
81 struct HTTPContext *next;
82 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
86 /* input format handling */
87 AVFormatContext *fmt_in;
88 /* output format handling */
89 struct FFStream *stream;
90 AVFormatContext fmt_ctx;
91 int last_packet_sent; /* true if last data packet was sent */
99 UINT8 buffer[IOBUFFER_MAX_SIZE];
100 UINT8 pbuffer[PACKET_MAX_SIZE];
103 /* each generated stream is described here */
109 /* description of each stream of the ffserver.conf file */
110 typedef struct FFStream {
111 enum StreamType stream_type;
112 char filename[1024]; /* stream filename */
113 struct FFStream *feed;
116 int prebuffer; /* Number of millseconds early to start */
119 AVStream *streams[MAX_STREAMS];
120 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
121 char feed_filename[1024]; /* file name of the feed storage, or
122 input file name for a stream */
123 struct FFStream *next;
125 int feed_opened; /* true if someone if writing to feed */
126 int is_feed; /* true if it is a feed */
129 INT64 feed_max_size; /* maximum storage size */
130 INT64 feed_write_index; /* current write position in feed (it wraps round) */
131 INT64 feed_size; /* current size of feed */
132 struct FFStream *next_feed;
135 typedef struct FeedData {
136 long long data_count;
137 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
140 struct sockaddr_in my_addr;
141 char logfilename[1024];
142 HTTPContext *first_http_ctx;
143 FFStream *first_feed; /* contains only feeds */
144 FFStream *first_stream; /* contains all streams, including feeds */
146 static int handle_http(HTTPContext *c, long cur_time);
147 static int http_parse_request(HTTPContext *c);
148 static int http_send_data(HTTPContext *c);
149 static void compute_stats(HTTPContext *c);
150 static int open_input_stream(HTTPContext *c, const char *info);
151 static int http_start_receive_data(HTTPContext *c);
152 static int http_receive_data(HTTPContext *c);
154 int nb_max_connections;
157 int nb_max_bandwidth;
160 static long gettime_ms(void)
164 gettimeofday(&tv,NULL);
165 return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
168 static FILE *logfile = NULL;
170 static void http_log(char *fmt, ...)
176 vfprintf(logfile, fmt, ap);
182 static void log_connection(HTTPContext *c)
184 char buf1[32], buf2[32], *p;
190 /* XXX: reentrant function ? */
191 p = inet_ntoa(c->from_addr.sin_addr);
196 p = buf2 + strlen(p) - 1;
199 http_log("%s - - [%s] \"%s %s %s\" %d %lld %s\n",
200 buf1, buf2, c->method, c->url, c->protocol, (c->http_error ? c->http_error : 200), c->data_count,
201 c->stream ? c->stream->filename : "");
204 /* main loop of the http server */
205 static int http_server(struct sockaddr_in my_addr)
207 int server_fd, tmp, ret;
208 struct sockaddr_in from_addr;
209 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 1], *poll_entry;
210 HTTPContext *c, **cp;
213 server_fd = socket(AF_INET,SOCK_STREAM,0);
220 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
222 if (bind (server_fd, (struct sockaddr *) &my_addr, sizeof (my_addr)) < 0) {
228 if (listen (server_fd, 5) < 0) {
234 http_log("ffserver started.\n");
236 fcntl(server_fd, F_SETFL, O_NONBLOCK);
237 first_http_ctx = NULL;
239 first_http_ctx = NULL;
241 poll_entry = poll_table;
242 poll_entry->fd = server_fd;
243 poll_entry->events = POLLIN;
246 /* wait for events on each HTTP handle */
252 case HTTPSTATE_WAIT_REQUEST:
253 c->poll_entry = poll_entry;
255 poll_entry->events = POLLIN;
258 case HTTPSTATE_SEND_HEADER:
259 case HTTPSTATE_SEND_DATA_HEADER:
260 case HTTPSTATE_SEND_DATA:
261 case HTTPSTATE_SEND_DATA_TRAILER:
262 c->poll_entry = poll_entry;
264 poll_entry->events = POLLOUT;
267 case HTTPSTATE_RECEIVE_DATA:
268 c->poll_entry = poll_entry;
270 poll_entry->events = POLLIN;
273 case HTTPSTATE_WAIT_FEED:
274 /* need to catch errors */
275 c->poll_entry = poll_entry;
277 poll_entry->events = POLLIN;/* Maybe this will work */
281 c->poll_entry = NULL;
287 /* wait for an event on one connection. We poll at least every
288 second to handle timeouts */
290 ret = poll(poll_table, poll_entry - poll_table, 1000);
293 cur_time = gettime_ms();
295 /* now handle the events */
297 cp = &first_http_ctx;
298 while ((*cp) != NULL) {
300 if (handle_http (c, cur_time) < 0) {
301 /* close and free the connection */
305 av_close_input_file(c->fmt_in);
307 nb_bandwidth -= c->bandwidth;
315 /* new connection request ? */
316 poll_entry = poll_table;
317 if (poll_entry->revents & POLLIN) {
320 len = sizeof(from_addr);
321 fd = accept(server_fd, (struct sockaddr *)&from_addr,
324 fcntl(fd, F_SETFL, O_NONBLOCK);
325 /* XXX: should output a warning page when coming
326 close to the connection limit */
327 if (nb_connections >= nb_max_connections) {
330 /* add a new connection */
331 c = av_mallocz(sizeof(HTTPContext));
332 c->next = first_http_ctx;
335 c->poll_entry = NULL;
336 c->from_addr = from_addr;
337 c->state = HTTPSTATE_WAIT_REQUEST;
338 c->buffer_ptr = c->buffer;
339 c->buffer_end = c->buffer + IOBUFFER_MAX_SIZE;
340 c->timeout = cur_time + REQUEST_TIMEOUT;
349 static int handle_http(HTTPContext *c, long cur_time)
354 case HTTPSTATE_WAIT_REQUEST:
356 if ((c->timeout - cur_time) < 0)
358 if (c->poll_entry->revents & (POLLERR | POLLHUP))
361 /* no need to read if no events */
362 if (!(c->poll_entry->revents & POLLIN))
365 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
367 if (errno != EAGAIN && errno != EINTR)
369 } else if (len == 0) {
372 /* search for end of request. XXX: not fully correct since garbage could come after the end */
374 c->buffer_ptr += len;
376 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
377 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
378 /* request found : parse it and reply */
379 if (http_parse_request(c) < 0)
381 } else if (ptr >= c->buffer_end) {
382 /* request too long: cannot do anything */
388 case HTTPSTATE_SEND_HEADER:
389 if (c->poll_entry->revents & (POLLERR | POLLHUP))
392 /* no need to read if no events */
393 if (!(c->poll_entry->revents & POLLOUT))
395 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
397 if (errno != EAGAIN && errno != EINTR) {
398 /* error : close connection */
402 c->buffer_ptr += len;
404 c->stream->bytes_served += len;
405 c->data_count += len;
406 if (c->buffer_ptr >= c->buffer_end) {
410 /* all the buffer was send : synchronize to the incoming stream */
411 c->state = HTTPSTATE_SEND_DATA_HEADER;
412 c->buffer_ptr = c->buffer_end = c->buffer;
417 case HTTPSTATE_SEND_DATA:
418 case HTTPSTATE_SEND_DATA_HEADER:
419 case HTTPSTATE_SEND_DATA_TRAILER:
420 /* no need to read if no events */
421 if (c->poll_entry->revents & (POLLERR | POLLHUP))
424 if (!(c->poll_entry->revents & POLLOUT))
426 if (http_send_data(c) < 0)
429 case HTTPSTATE_RECEIVE_DATA:
430 /* no need to read if no events */
431 if (c->poll_entry->revents & (POLLERR | POLLHUP))
433 if (!(c->poll_entry->revents & POLLIN))
435 if (http_receive_data(c) < 0)
438 case HTTPSTATE_WAIT_FEED:
439 /* no need to read if no events */
440 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
443 /* nothing to do, we'll be waken up by incoming feed packets */
451 static int extract_rates(char *rates, int ratelen, const char *request)
455 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
456 if (strncasecmp(p, "Pragma:", 7) == 0) {
457 const char *q = p + 7;
459 while (*q && *q != '\n' && isspace(*q))
462 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
468 memset(rates, 0, ratelen);
471 while (*q && *q != '\n' && *q != ':')
474 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
478 if (stream_no < ratelen && stream_no >= 0) {
479 rates[stream_no] = rate_no;
482 while (*q && *q != '\n' && !isspace(*q))
499 static FFStream *find_optimal_stream(FFStream *req, char *rates)
504 int want_bitrate = 0;
508 for (i = 0; i < req->nb_streams; i++) {
509 AVCodecContext *codec = &req->streams[i]->codec;
511 req_bitrate += codec->bit_rate;
515 want_bitrate += codec->bit_rate;
518 want_bitrate += codec->bit_rate / 2;
525 best_bitrate = req_bitrate;
526 if (best_bitrate <= want_bitrate)
527 return 0; /* We are OK */
529 /* Now we have the actual rates that we can use. Now find the stream that uses most of it! */
531 for (rover = first_stream; rover; rover = rover->next) {
532 if (rover->feed != req->feed ||
533 rover->fmt != req->fmt ||
534 rover->nb_streams != req->nb_streams ||
539 /* Now see if the codecs all match */
541 for (i = 0; i < req->nb_streams; i++) {
542 AVCodecContext *codec = &req->streams[i]->codec;
543 AVCodecContext *rovercodec = &rover->streams[i]->codec;
545 if (rovercodec->codec_id != codec->codec_id ||
546 rovercodec->sample_rate != codec->sample_rate) {
547 /* Does the video width and height have to match?? */
552 if (i == req->nb_streams) {
553 /* The rovercodec is another possible stream */
554 int rover_bitrate = 0;
556 for (i = 0; i < req->nb_streams; i++) {
557 AVCodecContext *codec = &rover->streams[i]->codec;
559 rover_bitrate += codec->bit_rate;
562 /* We want to choose the largest rover_bitrate <= want_bitrate, or the smallest
563 * rover_bitrate if none <= want_bitrate
565 if (rover_bitrate <= want_bitrate) {
566 if (best_bitrate > want_bitrate || rover_bitrate > best_bitrate) {
567 best_bitrate = rover_bitrate;
571 if (rover_bitrate < best_bitrate) {
572 best_bitrate = rover_bitrate;
582 /* parse http request and prepare header */
583 static int http_parse_request(HTTPContext *c)
590 char info[1024], *filename;
594 const char *mime_type;
601 while (!isspace(*p) && *p != '\0') {
602 if ((q - cmd) < sizeof(cmd) - 1)
608 pstrcpy(c->method, sizeof(c->method), cmd);
610 if (!strcmp(cmd, "GET"))
612 else if (!strcmp(cmd, "POST"))
617 while (isspace(*p)) p++;
619 while (!isspace(*p) && *p != '\0') {
620 if ((q - url) < sizeof(url) - 1)
626 pstrcpy(c->url, sizeof(c->url), url);
628 while (isspace(*p)) p++;
630 while (!isspace(*p) && *p != '\0') {
631 if ((q - protocol) < sizeof(protocol) - 1)
636 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
639 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
641 /* find the filename and the optional info string in the request */
648 pstrcpy(info, sizeof(info), p);
654 if (strlen(filename) > 4 && strcmp(".asx", filename + strlen(filename) - 4) == 0) {
656 filename[strlen(filename)-1] = 'f';
661 if (strlen(filename) > 4 &&
662 (strcmp(".rpm", filename + strlen(filename) - 4) == 0 ||
663 strcmp(".ram", filename + strlen(filename) - 4) == 0)) {
665 strcpy(filename + strlen(filename)-2, "m");
670 stream = first_stream;
671 while (stream != NULL) {
672 if (!strcmp(stream->filename, filename))
674 stream = stream->next;
676 if (stream == NULL) {
677 sprintf(msg, "File '%s' not found", url);
681 /* If this is WMP, get the rate information */
682 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
685 optimal = find_optimal_stream(stream, ratebuf);
690 if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
691 /* See if we meet the bandwidth requirements */
692 for(i=0;i<stream->nb_streams;i++) {
693 AVStream *st = stream->streams[i];
694 switch(st->codec.codec_type) {
695 case CODEC_TYPE_AUDIO:
696 c->bandwidth += st->codec.bit_rate;
698 case CODEC_TYPE_VIDEO:
699 c->bandwidth += st->codec.bit_rate;
707 c->bandwidth /= 1000;
708 nb_bandwidth += c->bandwidth;
710 if (post == 0 && nb_max_bandwidth < nb_bandwidth) {
713 q += sprintf(q, "HTTP/1.0 200 Server too busy\r\n");
714 q += sprintf(q, "Content-type: text/html\r\n");
715 q += sprintf(q, "\r\n");
716 q += sprintf(q, "<html><head><title>Too busy</title></head><body>\r\n");
717 q += sprintf(q, "The server is too busy to serve your request at this time.<p>\r\n");
718 q += sprintf(q, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
719 nb_bandwidth, nb_max_bandwidth);
720 q += sprintf(q, "</body></html>\r\n");
722 /* prepare output buffer */
723 c->buffer_ptr = c->buffer;
725 c->state = HTTPSTATE_SEND_HEADER;
729 if (doing_asx || doing_ram) {
732 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
733 if (strncasecmp(p, "Host:", 5) == 0) {
748 while (isspace(*hostinfo))
751 eoh = strchr(hostinfo, '\n');
756 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
757 memcpy(hostbuf, hostinfo, eoh - hostinfo);
758 hostbuf[eoh - hostinfo] = 0;
763 q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n");
764 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
765 q += sprintf(q, "\r\n");
766 q += sprintf(q, "<ASX Version=\"3\">\r\n");
767 q += sprintf(q, "<!-- Autogenerated by ffserver -->\r\n");
768 q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
769 hostbuf, filename, info);
770 q += sprintf(q, "</ASX>\r\n");
771 } else if (doing_ram) {
772 q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n");
773 q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n");
774 q += sprintf(q, "\r\n");
775 q += sprintf(q, "# Autogenerated by ffserver\r\n");
776 q += sprintf(q, "http://%s/%s%s\r\n",
777 hostbuf, filename, info);
781 /* prepare output buffer */
782 c->buffer_ptr = c->buffer;
784 c->state = HTTPSTATE_SEND_HEADER;
790 sprintf(msg, "ASX/RAM file not handled");
795 stream->conns_served++;
797 /* XXX: add there authenticate and IP match */
800 /* if post, it means a feed is being sent */
801 if (!stream->is_feed) {
802 /* However it might be a status report from WMP! Lets log the data
803 * as it might come in handy one day
808 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
809 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
813 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
814 client_id = strtol(p + 18, 0, 10);
824 char *eol = strchr(logline, '\n');
831 http_log("%.*s\n", eol - logline, logline);
837 fprintf(stderr, "\nGot request:\n%s\n", c->buffer);
840 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
843 /* Now we have to find the client_id */
844 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
845 if (wmpc->wmp_client_id == client_id)
851 optimal = find_optimal_stream(wmpc->stream, ratebuf);
853 fprintf(stderr, "Would like to switch stream from %s to %s\n",
854 wmpc->stream->filename, optimal->filename);
859 sprintf(msg, "POST command not handled");
862 if (http_start_receive_data(c) < 0) {
863 sprintf(msg, "could not open feed");
867 c->state = HTTPSTATE_RECEIVE_DATA;
872 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
873 fprintf(stderr, "\nGot request:\n%s\n", c->buffer);
877 if (c->stream->stream_type == STREAM_TYPE_STATUS)
880 /* open input stream */
881 if (open_input_stream(c, info) < 0) {
882 sprintf(msg, "Input stream corresponding to '%s' not found", url);
886 /* prepare http header */
888 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
889 mime_type = c->stream->fmt->mime_type;
891 mime_type = "application/x-octet_stream";
892 q += sprintf(q, "Pragma: no-cache\r\n");
894 /* for asf, we need extra headers */
895 if (!strcmp(c->stream->fmt->name,"asf")) {
896 /* Need to allocate a client id */
897 static int wmp_session;
900 wmp_session = time(0) & 0xffffff;
902 c->wmp_client_id = ++wmp_session;
904 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);
905 /* mime_type = "application/octet-stream"; */
906 /* video/x-ms-asf seems better -- netscape doesn't crash any more! */
907 mime_type = "video/x-ms-asf";
909 q += sprintf(q, "Content-Type: %s\r\n", mime_type);
910 q += sprintf(q, "\r\n");
912 /* prepare output buffer */
914 c->buffer_ptr = c->buffer;
916 c->state = HTTPSTATE_SEND_HEADER;
921 q += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
922 q += sprintf(q, "Content-type: %s\r\n", "text/html");
923 q += sprintf(q, "\r\n");
924 q += sprintf(q, "<HTML>\n");
925 q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
926 q += sprintf(q, "<BODY>%s</BODY>\n", msg);
927 q += sprintf(q, "</HTML>\n");
929 /* prepare output buffer */
930 c->buffer_ptr = c->buffer;
932 c->state = HTTPSTATE_SEND_HEADER;
936 c->http_error = 200; /* horrible : we use this value to avoid
937 going to the send data state */
938 c->state = HTTPSTATE_SEND_HEADER;
942 static void compute_stats(HTTPContext *c)
951 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
952 q += sprintf(q, "Content-type: %s\r\n", "text/html");
953 q += sprintf(q, "Pragma: no-cache\r\n");
954 q += sprintf(q, "\r\n");
956 q += sprintf(q, "<HEAD><TITLE>FFServer Status</TITLE></HEAD>\n<BODY>");
957 q += sprintf(q, "<H1>FFServer Status</H1>\n");
959 q += sprintf(q, "<H2>Available Streams</H2>\n");
960 q += sprintf(q, "<TABLE cellspacing=0 cellpadding=4>\n");
961 q += sprintf(q, "<TR><Th valign=top>Path<th align=left>Served<br>Conns<Th><br>kbytes<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");
962 stream = first_stream;
963 while (stream != NULL) {
964 char sfilename[1024];
967 if (stream->feed != stream) {
968 pstrcpy(sfilename, sizeof(sfilename) - 1, stream->filename);
969 eosf = sfilename + strlen(sfilename);
970 if (eosf - sfilename >= 4) {
971 if (strcmp(eosf - 4, ".asf") == 0) {
972 strcpy(eosf - 4, ".asx");
973 } else if (strcmp(eosf - 3, ".rm") == 0) {
974 strcpy(eosf - 3, ".ram");
978 q += sprintf(q, "<TR><TD><A HREF=\"/%s\">%s</A> ",
979 sfilename, stream->filename);
980 q += sprintf(q, "<td align=right> %d <td align=right> %lld",
981 stream->conns_served, stream->bytes_served / 1000);
982 switch(stream->stream_type) {
983 case STREAM_TYPE_LIVE:
985 int audio_bit_rate = 0;
986 int video_bit_rate = 0;
987 char *audio_codec_name = "";
988 char *video_codec_name = "";
989 char *audio_codec_name_extra = "";
990 char *video_codec_name_extra = "";
992 for(i=0;i<stream->nb_streams;i++) {
993 AVStream *st = stream->streams[i];
994 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
995 switch(st->codec.codec_type) {
996 case CODEC_TYPE_AUDIO:
997 audio_bit_rate += st->codec.bit_rate;
999 if (*audio_codec_name)
1000 audio_codec_name_extra = "...";
1001 audio_codec_name = codec->name;
1004 case CODEC_TYPE_VIDEO:
1005 video_bit_rate += st->codec.bit_rate;
1007 if (*video_codec_name)
1008 video_codec_name_extra = "...";
1009 video_codec_name = codec->name;
1016 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",
1018 (audio_bit_rate + video_bit_rate) / 1000,
1019 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1020 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1022 q += sprintf(q, "<TD>%s", stream->feed->filename);
1024 q += sprintf(q, "<TD>%s", stream->feed_filename);
1026 q += sprintf(q, "\n");
1030 q += sprintf(q, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1034 stream = stream->next;
1036 q += sprintf(q, "</TABLE>\n");
1038 stream = first_stream;
1039 while (stream != NULL) {
1040 if (stream->feed == stream) {
1041 q += sprintf(q, "<h2>Feed %s</h2>", stream->filename);
1042 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");
1044 for (i = 0; i < stream->nb_streams; i++) {
1045 AVStream *st = stream->streams[i];
1046 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1047 char *type = "unknown";
1048 char parameters[64];
1052 switch(st->codec.codec_type) {
1053 case CODEC_TYPE_AUDIO:
1056 case CODEC_TYPE_VIDEO:
1058 sprintf(parameters, "%dx%d, q=%d-%d", st->codec.width, st->codec.height,
1059 st->codec.qmin, st->codec.qmax);
1064 q += sprintf(q, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1065 i, type, st->codec.bit_rate/1000, codec ? codec->name : "", parameters);
1067 q += sprintf(q, "</table>\n");
1070 stream = stream->next;
1076 AVCodecContext *enc;
1080 stream = first_feed;
1081 while (stream != NULL) {
1082 q += sprintf(q, "<H1>Feed '%s'</H1>\n", stream->filename);
1083 q += sprintf(q, "<TABLE>\n");
1084 q += sprintf(q, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1085 for(i=0;i<stream->nb_streams;i++) {
1086 AVStream *st = stream->streams[i];
1087 FeedData *fdata = st->priv_data;
1090 avcodec_string(buf, sizeof(buf), enc);
1091 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1092 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1093 avg /= enc->frame_size;
1094 q += sprintf(q, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1095 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1097 q += sprintf(q, "</TABLE>\n");
1098 stream = stream->next_feed;
1103 /* connection status */
1104 q += sprintf(q, "<H2>Connection Status</H2>\n");
1106 q += sprintf(q, "Number of connections: %d / %d<BR>\n",
1107 nb_connections, nb_max_connections);
1109 q += sprintf(q, "Bandwidth in use: %dk / %dk<BR>\n",
1110 nb_bandwidth, nb_max_bandwidth);
1112 q += sprintf(q, "<TABLE>\n");
1113 q += sprintf(q, "<TR><TD>#<TD>File<TD>IP<TD>State<TD>Size\n");
1114 c1 = first_http_ctx;
1116 while (c1 != NULL && q < (char *) c->buffer + sizeof(c->buffer) - 2048) {
1118 p = inet_ntoa(c1->from_addr.sin_addr);
1119 q += sprintf(q, "<TR><TD><B>%d</B><TD>%s%s <TD> %s <TD> %s <TD> %Ld\n",
1120 i, c1->stream->filename,
1121 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1123 http_state[c1->state],
1127 q += sprintf(q, "</TABLE>\n");
1132 q += sprintf(q, "<HR size=1 noshade>Generated at %s", p);
1133 q += sprintf(q, "</BODY>\n</HTML>\n");
1135 c->buffer_ptr = c->buffer;
1140 static void http_write_packet(void *opaque,
1141 unsigned char *buf, int size)
1143 HTTPContext *c = opaque;
1145 if (c->buffer_ptr == c->buffer_end || !c->buffer_ptr)
1146 c->buffer_ptr = c->buffer_end = c->buffer;
1148 if (c->buffer_end - c->buffer + size > IOBUFFER_MAX_SIZE)
1151 memcpy(c->buffer_end, buf, size);
1152 c->buffer_end += size;
1155 static int open_input_stream(HTTPContext *c, const char *info)
1158 char input_filename[1024];
1163 /* find file name */
1164 if (c->stream->feed) {
1165 strcpy(input_filename, c->stream->feed->feed_filename);
1166 buf_size = FFM_PACKET_SIZE;
1167 /* compute position (absolute time) */
1168 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1169 stream_pos = parse_date(buf, 0);
1170 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1171 int prebuffer = strtol(buf, 0, 10);
1172 stream_pos = gettime() - prebuffer * 1000000;
1174 stream_pos = gettime() - c->stream->prebuffer * 1000;
1177 strcpy(input_filename, c->stream->feed_filename);
1179 /* compute position (relative time) */
1180 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1181 stream_pos = parse_date(buf, 1);
1186 if (input_filename[0] == '\0')
1190 if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0)
1194 if (c->fmt_in->iformat->read_seek) {
1195 c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
1198 // printf("stream %s opened pos=%0.6f\n", input_filename, stream_pos / 1000000.0);
1202 static int http_prepare_data(HTTPContext *c)
1207 case HTTPSTATE_SEND_DATA_HEADER:
1208 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
1209 if (c->stream->feed) {
1210 /* open output stream by using specified codecs */
1211 c->fmt_ctx.oformat = c->stream->fmt;
1212 c->fmt_ctx.nb_streams = c->stream->nb_streams;
1213 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
1215 st = av_mallocz(sizeof(AVStream));
1216 c->fmt_ctx.streams[i] = st;
1217 if (c->stream->feed == c->stream)
1218 memcpy(st, c->stream->streams[i], sizeof(AVStream));
1220 memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]], sizeof(AVStream));
1222 st->codec.frame_number = 0; /* XXX: should be done in
1223 AVStream, not in codec */
1225 c->got_key_frame = 0;
1227 /* open output stream by using codecs in specified file */
1228 c->fmt_ctx.oformat = c->stream->fmt;
1229 c->fmt_ctx.nb_streams = c->fmt_in->nb_streams;
1230 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
1232 st = av_mallocz(sizeof(AVStream));
1233 c->fmt_ctx.streams[i] = st;
1234 memcpy(st, c->fmt_in->streams[i], sizeof(AVStream));
1235 st->codec.frame_number = 0; /* XXX: should be done in
1236 AVStream, not in codec */
1238 c->got_key_frame = 0;
1240 init_put_byte(&c->fmt_ctx.pb, c->pbuffer, PACKET_MAX_SIZE,
1241 1, c, NULL, http_write_packet, NULL);
1242 c->fmt_ctx.pb.is_streamed = 1;
1243 /* prepare header */
1244 av_write_header(&c->fmt_ctx);
1245 c->state = HTTPSTATE_SEND_DATA;
1246 c->last_packet_sent = 0;
1248 case HTTPSTATE_SEND_DATA:
1249 /* find a new packet */
1251 fifo_total_size = http_fifo_write_count - c->last_http_fifo_write_count;
1252 if (fifo_total_size >= ((3 * FIFO_MAX_SIZE) / 4)) {
1253 /* overflow : resync. We suppose that wptr is at this
1254 point a pointer to a valid packet */
1255 c->rptr = http_fifo.wptr;
1256 c->got_key_frame = 0;
1259 start_rptr = c->rptr;
1260 if (fifo_read(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &c->rptr) < 0)
1262 payload_size = ntohs(hdr.payload_size);
1263 payload = av_malloc(payload_size);
1264 if (fifo_read(&http_fifo, payload, payload_size, &c->rptr) < 0) {
1265 /* cannot read all the payload */
1267 c->rptr = start_rptr;
1271 c->last_http_fifo_write_count = http_fifo_write_count -
1272 fifo_size(&http_fifo, c->rptr);
1274 if (c->stream->stream_type != STREAM_TYPE_MASTER) {
1275 /* test if the packet can be handled by this format */
1277 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
1278 AVStream *st = c->fmt_ctx.streams[i];
1279 if (test_header(&hdr, &st->codec)) {
1280 /* only begin sending when got a key frame */
1281 if (st->codec.key_frame)
1282 c->got_key_frame |= 1 << i;
1283 if (c->got_key_frame & (1 << i)) {
1284 ret = c->fmt_ctx.format->write_packet(&c->fmt_ctx, i,
1285 payload, payload_size);
1291 /* must send trailer now */
1292 c->state = HTTPSTATE_SEND_DATA_TRAILER;
1295 /* master case : send everything */
1298 memcpy(q, &hdr, sizeof(hdr));
1300 memcpy(q, payload, payload_size);
1302 c->buffer_ptr = c->buffer;
1310 /* read a packet from the input stream */
1311 if (c->stream->feed) {
1312 ffm_set_write_index(c->fmt_in,
1313 c->stream->feed->feed_write_index,
1314 c->stream->feed->feed_size);
1317 if (c->stream->max_time &&
1318 c->stream->max_time + c->start_time > time(0)) {
1319 /* We have timed out */
1320 c->state = HTTPSTATE_SEND_DATA_TRAILER;
1321 } else if (av_read_packet(c->fmt_in, &pkt) < 0) {
1322 if (c->stream->feed && c->stream->feed->feed_opened) {
1323 /* if coming from feed, it means we reached the end of the
1324 ffm file, so must wait for more data */
1325 c->state = HTTPSTATE_WAIT_FEED;
1326 return 1; /* state changed */
1328 /* must send trailer now because eof or error */
1329 c->state = HTTPSTATE_SEND_DATA_TRAILER;
1332 /* send it to the appropriate stream */
1333 if (c->stream->feed) {
1334 /* if coming from a feed, select the right stream */
1335 for(i=0;i<c->stream->nb_streams;i++) {
1336 if (c->stream->feed_streams[i] == pkt.stream_index) {
1337 pkt.stream_index = i;
1338 if (pkt.flags & PKT_FLAG_KEY) {
1339 c->got_key_frame |= 1 << i;
1341 /* See if we have all the key frames, then
1342 * we start to send. This logic is not quite
1343 * right, but it works for the case of a
1344 * single video stream with one or more
1345 * audio streams (for which every frame is
1346 * typically a key frame).
1348 if (!c->stream->send_on_key || ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
1354 AVCodecContext *codec;
1357 codec = &c->fmt_ctx.streams[pkt.stream_index]->codec;
1359 codec->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
1362 if (codec->codec_type == CODEC_TYPE_AUDIO) {
1363 codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
1364 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
1368 if (av_write_packet(&c->fmt_ctx, &pkt, 0))
1369 c->state = HTTPSTATE_SEND_DATA_TRAILER;
1371 codec->frame_number++;
1374 av_free_packet(&pkt);
1379 case HTTPSTATE_SEND_DATA_TRAILER:
1380 /* last packet test ? */
1381 if (c->last_packet_sent)
1383 /* prepare header */
1384 av_write_trailer(&c->fmt_ctx);
1385 c->last_packet_sent = 1;
1391 /* should convert the format at the same time */
1392 static int http_send_data(HTTPContext *c)
1396 while (c->buffer_ptr >= c->buffer_end) {
1397 ret = http_prepare_data(c);
1400 else if (ret == 0) {
1403 /* state change requested */
1408 if (c->buffer_end > c->buffer_ptr) {
1409 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
1411 if (errno != EAGAIN && errno != EINTR) {
1412 /* error : close connection */
1416 c->buffer_ptr += len;
1417 c->data_count += len;
1419 c->stream->bytes_served += len;
1425 static int http_start_receive_data(HTTPContext *c)
1429 if (c->stream->feed_opened)
1433 fd = open(c->stream->feed_filename, O_RDWR);
1438 c->stream->feed_write_index = ffm_read_write_index(fd);
1439 c->stream->feed_size = lseek(fd, 0, SEEK_END);
1440 lseek(fd, 0, SEEK_SET);
1442 /* init buffer input */
1443 c->buffer_ptr = c->buffer;
1444 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
1445 c->stream->feed_opened = 1;
1449 static int http_receive_data(HTTPContext *c)
1453 if (c->buffer_end > c->buffer_ptr) {
1456 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
1458 if (errno != EAGAIN && errno != EINTR) {
1459 /* error : close connection */
1462 } else if (len == 0) {
1463 /* end of connection : close it */
1466 c->buffer_ptr += len;
1467 c->data_count += len;
1471 if (c->buffer_ptr >= c->buffer_end) {
1472 FFStream *feed = c->stream;
1473 /* a packet has been received : write it in the store, except
1475 if (c->data_count > FFM_PACKET_SIZE) {
1477 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
1478 /* XXX: use llseek or url_seek */
1479 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
1480 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
1482 feed->feed_write_index += FFM_PACKET_SIZE;
1483 /* update file size */
1484 if (feed->feed_write_index > c->stream->feed_size)
1485 feed->feed_size = feed->feed_write_index;
1487 /* handle wrap around if max file size reached */
1488 if (feed->feed_write_index >= c->stream->feed_max_size)
1489 feed->feed_write_index = FFM_PACKET_SIZE;
1492 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
1494 /* wake up any waiting connections */
1495 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
1496 if (c1->state == HTTPSTATE_WAIT_FEED &&
1497 c1->stream->feed == c->stream->feed) {
1498 c1->state = HTTPSTATE_SEND_DATA;
1502 /* We have a header in our hands that contains useful data */
1504 AVInputFormat *fmt_in;
1505 ByteIOContext *pb = &s.pb;
1508 memset(&s, 0, sizeof(s));
1510 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
1511 pb->buf_end = c->buffer_end; /* ?? */
1512 pb->is_streamed = 1;
1514 /* use feed output format name to find corresponding input format */
1515 fmt_in = av_find_input_format(feed->fmt->name);
1519 s.priv_data = av_mallocz(fmt_in->priv_data_size);
1523 if (fmt_in->read_header(&s, 0) < 0) {
1524 av_freep(&s.priv_data);
1528 /* Now we have the actual streams */
1529 if (s.nb_streams != feed->nb_streams) {
1530 av_freep(&s.priv_data);
1533 for (i = 0; i < s.nb_streams; i++) {
1534 memcpy(&feed->streams[i]->codec,
1535 &s.streams[i]->codec, sizeof(AVCodecContext));
1537 av_freep(&s.priv_data);
1539 c->buffer_ptr = c->buffer;
1544 c->stream->feed_opened = 0;
1549 /* return the stream number in the feed */
1550 int add_av_stream(FFStream *feed,
1554 AVCodecContext *av, *av1;
1558 for(i=0;i<feed->nb_streams;i++) {
1559 st = feed->streams[i];
1561 if (av1->codec_id == av->codec_id &&
1562 av1->codec_type == av->codec_type &&
1563 av1->bit_rate == av->bit_rate) {
1565 switch(av->codec_type) {
1566 case CODEC_TYPE_AUDIO:
1567 if (av1->channels == av->channels &&
1568 av1->sample_rate == av->sample_rate)
1571 case CODEC_TYPE_VIDEO:
1572 if (av1->width == av->width &&
1573 av1->height == av->height &&
1574 av1->frame_rate == av->frame_rate &&
1575 av1->gop_size == av->gop_size)
1584 fst = av_mallocz(sizeof(AVStream));
1587 fst->priv_data = av_mallocz(sizeof(FeedData));
1588 memcpy(&fst->codec, av, sizeof(AVCodecContext));
1589 feed->streams[feed->nb_streams++] = fst;
1590 return feed->nb_streams - 1;
1595 /* compute the needed AVStream for each feed */
1596 void build_feed_streams(void)
1598 FFStream *stream, *feed;
1601 /* gather all streams */
1602 for(stream = first_stream; stream != NULL; stream = stream->next) {
1603 feed = stream->feed;
1605 if (!stream->is_feed) {
1606 for(i=0;i<stream->nb_streams;i++) {
1607 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
1610 for(i=0;i<stream->nb_streams;i++) {
1611 stream->feed_streams[i] = i;
1617 /* create feed files if needed */
1618 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
1621 if (!url_exist(feed->feed_filename)) {
1622 AVFormatContext s1, *s = &s1;
1624 /* only write the header of the ffm file */
1625 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
1626 fprintf(stderr, "Could not open output feed file '%s'\n",
1627 feed->feed_filename);
1630 s->oformat = feed->fmt;
1631 s->nb_streams = feed->nb_streams;
1632 for(i=0;i<s->nb_streams;i++) {
1634 st = feed->streams[i];
1638 /* XXX: need better api */
1639 av_freep(&s->priv_data);
1642 /* get feed size and write index */
1643 fd = open(feed->feed_filename, O_RDONLY);
1645 fprintf(stderr, "Could not open output feed file '%s'\n",
1646 feed->feed_filename);
1650 feed->feed_write_index = ffm_read_write_index(fd);
1651 feed->feed_size = lseek(fd, 0, SEEK_END);
1652 /* ensure that we do not wrap before the end of file */
1653 if (feed->feed_max_size < feed->feed_size)
1654 feed->feed_max_size = feed->feed_size;
1660 static void get_arg(char *buf, int buf_size, const char **pp)
1667 while (isspace(*p)) p++;
1670 if (*p == '\"' || *p == '\'')
1682 if ((q - buf) < buf_size - 1)
1687 if (quote && *p == quote)
1692 /* add a codec and set the default parameters */
1693 void add_codec(FFStream *stream, AVCodecContext *av)
1697 /* compute default parameters */
1698 switch(av->codec_type) {
1699 case CODEC_TYPE_AUDIO:
1700 if (av->bit_rate == 0)
1701 av->bit_rate = 64000;
1702 if (av->sample_rate == 0)
1703 av->sample_rate = 22050;
1704 if (av->channels == 0)
1707 case CODEC_TYPE_VIDEO:
1708 if (av->bit_rate == 0)
1709 av->bit_rate = 64000;
1710 if (av->frame_rate == 0)
1711 av->frame_rate = 5 * FRAME_RATE_BASE;
1712 if (av->width == 0 || av->height == 0) {
1716 /* Bitrate tolerance is less for streaming */
1717 if (av->bit_rate_tolerance == 0)
1718 av->bit_rate_tolerance = av->bit_rate / 4;
1723 if (av->max_qdiff == 0)
1725 av->qcompress = 0.5;
1733 st = av_mallocz(sizeof(AVStream));
1736 stream->streams[stream->nb_streams++] = st;
1737 memcpy(&st->codec, av, sizeof(AVCodecContext));
1740 int opt_audio_codec(const char *arg)
1746 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
1751 return CODEC_ID_NONE;
1757 int opt_video_codec(const char *arg)
1763 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
1768 return CODEC_ID_NONE;
1774 int parse_ffconfig(const char *filename)
1781 int val, errors, line_num;
1782 FFStream **last_stream, *stream;
1783 FFStream **last_feed, *feed;
1784 AVCodecContext audio_enc, video_enc;
1785 int audio_id, video_id;
1787 f = fopen(filename, "r");
1795 first_stream = NULL;
1796 last_stream = &first_stream;
1798 last_feed = &first_feed;
1801 audio_id = CODEC_ID_NONE;
1802 video_id = CODEC_ID_NONE;
1804 if (fgets(line, sizeof(line), f) == NULL)
1810 if (*p == '\0' || *p == '#')
1813 get_arg(cmd, sizeof(cmd), &p);
1815 if (!strcasecmp(cmd, "Port")) {
1816 get_arg(arg, sizeof(arg), &p);
1817 my_addr.sin_port = htons (atoi(arg));
1818 } else if (!strcasecmp(cmd, "BindAddress")) {
1819 get_arg(arg, sizeof(arg), &p);
1820 if (!inet_aton(arg, &my_addr.sin_addr)) {
1821 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
1822 filename, line_num, arg);
1825 } else if (!strcasecmp(cmd, "MaxClients")) {
1826 get_arg(arg, sizeof(arg), &p);
1828 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
1829 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
1830 filename, line_num, arg);
1833 nb_max_connections = val;
1835 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
1836 get_arg(arg, sizeof(arg), &p);
1838 if (val < 10 || val > 100000) {
1839 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
1840 filename, line_num, arg);
1843 nb_max_bandwidth = val;
1845 } else if (!strcasecmp(cmd, "CustomLog")) {
1846 get_arg(logfilename, sizeof(logfilename), &p);
1847 } else if (!strcasecmp(cmd, "<Feed")) {
1848 /*********************************************/
1849 /* Feed related options */
1851 if (stream || feed) {
1852 fprintf(stderr, "%s:%d: Already in a tag\n",
1853 filename, line_num);
1855 feed = av_mallocz(sizeof(FFStream));
1856 /* add in stream list */
1857 *last_stream = feed;
1858 last_stream = &feed->next;
1859 /* add in feed list */
1861 last_feed = &feed->next_feed;
1863 get_arg(feed->filename, sizeof(feed->filename), &p);
1864 q = strrchr(feed->filename, '>');
1867 feed->fmt = guess_format("ffm", NULL, NULL);
1868 /* defaut feed file */
1869 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
1870 "/tmp/%s.ffm", feed->filename);
1871 feed->feed_max_size = 5 * 1024 * 1024;
1873 feed->feed = feed; /* self feeding :-) */
1875 } else if (!strcasecmp(cmd, "File")) {
1877 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
1878 } else if (stream) {
1879 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
1881 } else if (!strcasecmp(cmd, "FileMaxSize")) {
1886 get_arg(arg, sizeof(arg), &p);
1888 fsize = strtod(p1, (char **)&p1);
1889 switch(toupper(*p1)) {
1894 fsize *= 1024 * 1024;
1897 fsize *= 1024 * 1024 * 1024;
1900 feed->feed_max_size = (INT64)fsize;
1902 } else if (!strcasecmp(cmd, "</Feed>")) {
1904 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
1905 filename, line_num);
1908 /* Make sure that we start out clean */
1909 if (unlink(feed->feed_filename) < 0
1910 && errno != ENOENT) {
1911 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
1912 filename, line_num, feed->feed_filename, strerror(errno));
1917 } else if (!strcasecmp(cmd, "<Stream")) {
1918 /*********************************************/
1919 /* Stream related options */
1921 if (stream || feed) {
1922 fprintf(stderr, "%s:%d: Already in a tag\n",
1923 filename, line_num);
1925 stream = av_mallocz(sizeof(FFStream));
1926 *last_stream = stream;
1927 last_stream = &stream->next;
1929 get_arg(stream->filename, sizeof(stream->filename), &p);
1930 q = strrchr(stream->filename, '>');
1933 stream->fmt = guess_format(NULL, stream->filename, NULL);
1934 memset(&audio_enc, 0, sizeof(AVCodecContext));
1935 memset(&video_enc, 0, sizeof(AVCodecContext));
1936 audio_id = CODEC_ID_NONE;
1937 video_id = CODEC_ID_NONE;
1939 audio_id = stream->fmt->audio_codec;
1940 video_id = stream->fmt->video_codec;
1943 } else if (!strcasecmp(cmd, "Feed")) {
1944 get_arg(arg, sizeof(arg), &p);
1949 while (sfeed != NULL) {
1950 if (!strcmp(sfeed->filename, arg))
1952 sfeed = sfeed->next_feed;
1955 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
1956 filename, line_num, arg);
1958 stream->feed = sfeed;
1961 } else if (!strcasecmp(cmd, "Format")) {
1962 get_arg(arg, sizeof(arg), &p);
1963 if (!strcmp(arg, "status")) {
1964 stream->stream_type = STREAM_TYPE_STATUS;
1967 stream->stream_type = STREAM_TYPE_LIVE;
1968 /* jpeg cannot be used here, so use single frame jpeg */
1969 if (!strcmp(arg, "jpeg"))
1970 strcpy(arg, "singlejpeg");
1971 stream->fmt = guess_format(arg, NULL, NULL);
1973 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
1974 filename, line_num, arg);
1979 audio_id = stream->fmt->audio_codec;
1980 video_id = stream->fmt->video_codec;
1982 } else if (!strcasecmp(cmd, "Preroll")) {
1983 get_arg(arg, sizeof(arg), &p);
1985 stream->prebuffer = atoi(arg) * 1000;
1987 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
1989 stream->send_on_key = 1;
1991 } else if (!strcasecmp(cmd, "AudioCodec")) {
1992 get_arg(arg, sizeof(arg), &p);
1993 audio_id = opt_audio_codec(arg);
1994 if (audio_id == CODEC_ID_NONE) {
1995 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
1996 filename, line_num, arg);
1999 } else if (!strcasecmp(cmd, "VideoCodec")) {
2000 get_arg(arg, sizeof(arg), &p);
2001 video_id = opt_video_codec(arg);
2002 if (video_id == CODEC_ID_NONE) {
2003 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
2004 filename, line_num, arg);
2007 } else if (!strcasecmp(cmd, "MaxTime")) {
2008 get_arg(arg, sizeof(arg), &p);
2010 stream->max_time = atoi(arg);
2012 } else if (!strcasecmp(cmd, "AudioBitRate")) {
2013 get_arg(arg, sizeof(arg), &p);
2015 audio_enc.bit_rate = atoi(arg) * 1000;
2017 } else if (!strcasecmp(cmd, "AudioChannels")) {
2018 get_arg(arg, sizeof(arg), &p);
2020 audio_enc.channels = atoi(arg);
2022 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
2023 get_arg(arg, sizeof(arg), &p);
2025 audio_enc.sample_rate = atoi(arg);
2027 } else if (!strcasecmp(cmd, "VideoBitRate")) {
2028 get_arg(arg, sizeof(arg), &p);
2030 video_enc.bit_rate = atoi(arg) * 1000;
2032 } else if (!strcasecmp(cmd, "VideoSize")) {
2033 get_arg(arg, sizeof(arg), &p);
2035 parse_image_size(&video_enc.width, &video_enc.height, arg);
2036 if ((video_enc.width % 16) != 0 ||
2037 (video_enc.height % 16) != 0) {
2038 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
2039 filename, line_num);
2043 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
2044 get_arg(arg, sizeof(arg), &p);
2046 video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
2048 } else if (!strcasecmp(cmd, "VideoGopSize")) {
2049 get_arg(arg, sizeof(arg), &p);
2051 video_enc.gop_size = atoi(arg);
2053 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
2055 video_enc.gop_size = 1;
2057 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
2059 video_enc.flags |= CODEC_FLAG_HQ;
2061 } else if (!strcasecmp(cmd, "VideoQDiff")) {
2063 video_enc.max_qdiff = atoi(arg);
2064 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
2065 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
2066 filename, line_num);
2070 } else if (!strcasecmp(cmd, "VideoQMax")) {
2072 video_enc.qmax = atoi(arg);
2073 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
2074 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
2075 filename, line_num);
2079 } else if (!strcasecmp(cmd, "VideoQMin")) {
2081 video_enc.qmin = atoi(arg);
2082 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
2083 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
2084 filename, line_num);
2088 } else if (!strcasecmp(cmd, "NoVideo")) {
2089 video_id = CODEC_ID_NONE;
2090 } else if (!strcasecmp(cmd, "NoAudio")) {
2091 audio_id = CODEC_ID_NONE;
2092 } else if (!strcasecmp(cmd, "</Stream>")) {
2094 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
2095 filename, line_num);
2098 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
2099 if (audio_id != CODEC_ID_NONE) {
2100 audio_enc.codec_type = CODEC_TYPE_AUDIO;
2101 audio_enc.codec_id = audio_id;
2102 add_codec(stream, &audio_enc);
2104 if (video_id != CODEC_ID_NONE) {
2105 video_enc.codec_type = CODEC_TYPE_VIDEO;
2106 video_enc.codec_id = video_id;
2107 add_codec(stream, &video_enc);
2112 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
2113 filename, line_num, cmd);
2126 void *http_server_thread(void *arg)
2128 http_server(my_addr);
2133 static void write_packet(FFCodec *ffenc,
2134 UINT8 *buf, int size)
2137 AVCodecContext *enc = &ffenc->enc;
2139 mk_header(&hdr, enc, size);
2140 wptr = http_fifo.wptr;
2141 fifo_write(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &wptr);
2142 fifo_write(&http_fifo, buf, size, &wptr);
2143 /* atomic modification of wptr */
2144 http_fifo.wptr = wptr;
2145 ffenc->data_count += size;
2146 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
2152 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
2153 "usage: ffserver [-L] [-h] [-f configfile]\n"
2154 "Hyper fast multi format Audio/Video streaming server\n"
2156 "-L : print the LICENCE\n"
2158 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
2165 "ffserver version " FFMPEG_VERSION "\n"
2166 "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
2167 "This library is free software; you can redistribute it and/or\n"
2168 "modify it under the terms of the GNU Lesser General Public\n"
2169 "License as published by the Free Software Foundation; either\n"
2170 "version 2 of the License, or (at your option) any later version.\n"
2172 "This library is distributed in the hope that it will be useful,\n"
2173 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
2174 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
2175 "Lesser General Public License for more details.\n"
2177 "You should have received a copy of the GNU Lesser General Public\n"
2178 "License along with this library; if not, write to the Free Software\n"
2179 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
2183 int main(int argc, char **argv)
2185 const char *config_filename;
2190 config_filename = "/etc/ffserver.conf";
2193 c = getopt_long_only(argc, argv, "Lh?f:", NULL, NULL);
2205 config_filename = optarg;
2212 /* address on which the server will handle connections */
2213 my_addr.sin_family = AF_INET;
2214 my_addr.sin_port = htons (8080);
2215 my_addr.sin_addr.s_addr = htonl (INADDR_ANY);
2216 nb_max_connections = 5;
2217 nb_max_bandwidth = 1000;
2218 first_stream = NULL;
2219 logfilename[0] = '\0';
2221 if (parse_ffconfig(config_filename) < 0) {
2222 fprintf(stderr, "Incorrect config file - exiting.\n");
2226 build_feed_streams();
2229 signal(SIGPIPE, SIG_IGN);
2231 /* open log file if needed */
2232 if (logfilename[0] != '\0') {
2233 if (!strcmp(logfilename, "-"))
2236 logfile = fopen(logfilename, "w");
2239 if (http_server(my_addr) < 0) {
2240 fprintf(stderr, "Could not start http server\n");