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 */
98 UINT8 buffer[IOBUFFER_MAX_SIZE];
99 UINT8 pbuffer[PACKET_MAX_SIZE];
102 /* each generated stream is described here */
108 /* description of each stream of the ffserver.conf file */
109 typedef struct FFStream {
110 enum StreamType stream_type;
111 char filename[1024]; /* stream filename */
112 struct FFStream *feed;
115 int prebuffer; /* Number of millseconds early to start */
118 AVStream *streams[MAX_STREAMS];
119 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
120 char feed_filename[1024]; /* file name of the feed storage, or
121 input file name for a stream */
122 struct FFStream *next;
124 int feed_opened; /* true if someone if writing to feed */
125 int is_feed; /* true if it is a feed */
128 INT64 feed_max_size; /* maximum storage size */
129 INT64 feed_write_index; /* current write position in feed (it wraps round) */
130 INT64 feed_size; /* current size of feed */
131 struct FFStream *next_feed;
134 typedef struct FeedData {
135 long long data_count;
136 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
139 struct sockaddr_in my_addr;
140 char logfilename[1024];
141 HTTPContext *first_http_ctx;
142 FFStream *first_feed; /* contains only feeds */
143 FFStream *first_stream; /* contains all streams, including feeds */
145 static int handle_http(HTTPContext *c, long cur_time);
146 static int http_parse_request(HTTPContext *c);
147 static int http_send_data(HTTPContext *c);
148 static void compute_stats(HTTPContext *c);
149 static int open_input_stream(HTTPContext *c, const char *info);
150 static int http_start_receive_data(HTTPContext *c);
151 static int http_receive_data(HTTPContext *c);
153 int nb_max_connections;
156 int nb_max_bandwidth;
159 static long gettime_ms(void)
163 gettimeofday(&tv,NULL);
164 return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
167 static FILE *logfile = NULL;
169 static void http_log(char *fmt, ...)
175 vfprintf(logfile, fmt, ap);
181 static void log_connection(HTTPContext *c)
183 char buf1[32], buf2[32], *p;
189 /* XXX: reentrant function ? */
190 p = inet_ntoa(c->from_addr.sin_addr);
195 p = buf2 + strlen(p) - 1;
198 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
199 buf1, buf2, c->method, c->url, c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
202 /* main loop of the http server */
203 static int http_server(struct sockaddr_in my_addr)
205 int server_fd, tmp, ret;
206 struct sockaddr_in from_addr;
207 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 1], *poll_entry;
208 HTTPContext *c, **cp;
211 server_fd = socket(AF_INET,SOCK_STREAM,0);
218 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
220 if (bind (server_fd, (struct sockaddr *) &my_addr, sizeof (my_addr)) < 0) {
226 if (listen (server_fd, 5) < 0) {
232 http_log("ffserver started.\n");
234 fcntl(server_fd, F_SETFL, O_NONBLOCK);
235 first_http_ctx = NULL;
237 first_http_ctx = NULL;
239 poll_entry = poll_table;
240 poll_entry->fd = server_fd;
241 poll_entry->events = POLLIN;
244 /* wait for events on each HTTP handle */
250 case HTTPSTATE_WAIT_REQUEST:
251 c->poll_entry = poll_entry;
253 poll_entry->events = POLLIN;
256 case HTTPSTATE_SEND_HEADER:
257 case HTTPSTATE_SEND_DATA_HEADER:
258 case HTTPSTATE_SEND_DATA:
259 case HTTPSTATE_SEND_DATA_TRAILER:
260 c->poll_entry = poll_entry;
262 poll_entry->events = POLLOUT;
265 case HTTPSTATE_RECEIVE_DATA:
266 c->poll_entry = poll_entry;
268 poll_entry->events = POLLIN;
271 case HTTPSTATE_WAIT_FEED:
272 /* need to catch errors */
273 c->poll_entry = poll_entry;
275 poll_entry->events = POLLIN;/* Maybe this will work */
279 c->poll_entry = NULL;
285 /* wait for an event on one connection. We poll at least every
286 second to handle timeouts */
288 ret = poll(poll_table, poll_entry - poll_table, 1000);
291 cur_time = gettime_ms();
293 /* now handle the events */
295 cp = &first_http_ctx;
296 while ((*cp) != NULL) {
298 if (handle_http (c, cur_time) < 0) {
299 /* close and free the connection */
303 av_close_input_file(c->fmt_in);
305 nb_bandwidth -= c->bandwidth;
313 /* new connection request ? */
314 poll_entry = poll_table;
315 if (poll_entry->revents & POLLIN) {
318 len = sizeof(from_addr);
319 fd = accept(server_fd, (struct sockaddr *)&from_addr,
322 fcntl(fd, F_SETFL, O_NONBLOCK);
323 /* XXX: should output a warning page when coming
324 close to the connection limit */
325 if (nb_connections >= nb_max_connections) {
328 /* add a new connection */
329 c = av_mallocz(sizeof(HTTPContext));
330 c->next = first_http_ctx;
333 c->poll_entry = NULL;
334 c->from_addr = from_addr;
335 c->state = HTTPSTATE_WAIT_REQUEST;
336 c->buffer_ptr = c->buffer;
337 c->buffer_end = c->buffer + IOBUFFER_MAX_SIZE;
338 c->timeout = cur_time + REQUEST_TIMEOUT;
347 static int handle_http(HTTPContext *c, long cur_time)
352 case HTTPSTATE_WAIT_REQUEST:
354 if ((c->timeout - cur_time) < 0)
356 if (c->poll_entry->revents & (POLLERR | POLLHUP))
359 /* no need to read if no events */
360 if (!(c->poll_entry->revents & POLLIN))
363 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
365 if (errno != EAGAIN && errno != EINTR)
367 } else if (len == 0) {
370 /* search for end of request. XXX: not fully correct since garbage could come after the end */
372 c->buffer_ptr += len;
374 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
375 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
376 /* request found : parse it and reply */
377 if (http_parse_request(c) < 0)
379 } else if (ptr >= c->buffer_end) {
380 /* request too long: cannot do anything */
386 case HTTPSTATE_SEND_HEADER:
387 if (c->poll_entry->revents & (POLLERR | POLLHUP))
390 /* no need to read if no events */
391 if (!(c->poll_entry->revents & POLLOUT))
393 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
395 if (errno != EAGAIN && errno != EINTR) {
396 /* error : close connection */
400 c->buffer_ptr += len;
402 c->stream->bytes_served += len;
403 c->data_count += len;
404 if (c->buffer_ptr >= c->buffer_end) {
408 /* all the buffer was send : synchronize to the incoming stream */
409 c->state = HTTPSTATE_SEND_DATA_HEADER;
410 c->buffer_ptr = c->buffer_end = c->buffer;
415 case HTTPSTATE_SEND_DATA:
416 case HTTPSTATE_SEND_DATA_HEADER:
417 case HTTPSTATE_SEND_DATA_TRAILER:
418 /* no need to read if no events */
419 if (c->poll_entry->revents & (POLLERR | POLLHUP))
422 if (!(c->poll_entry->revents & POLLOUT))
424 if (http_send_data(c) < 0)
427 case HTTPSTATE_RECEIVE_DATA:
428 /* no need to read if no events */
429 if (c->poll_entry->revents & (POLLERR | POLLHUP))
431 if (!(c->poll_entry->revents & POLLIN))
433 if (http_receive_data(c) < 0)
436 case HTTPSTATE_WAIT_FEED:
437 /* no need to read if no events */
438 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
441 /* nothing to do, we'll be waken up by incoming feed packets */
450 /* parse http request and prepare header */
451 static int http_parse_request(HTTPContext *c)
458 char info[1024], *filename;
462 const char *mime_type;
468 while (!isspace(*p) && *p != '\0') {
469 if ((q - cmd) < sizeof(cmd) - 1)
475 pstrcpy(c->method, sizeof(c->method), cmd);
477 if (!strcmp(cmd, "GET"))
479 else if (!strcmp(cmd, "POST"))
484 while (isspace(*p)) p++;
486 while (!isspace(*p) && *p != '\0') {
487 if ((q - url) < sizeof(url) - 1)
493 pstrcpy(c->url, sizeof(c->url), url);
495 while (isspace(*p)) p++;
497 while (!isspace(*p) && *p != '\0') {
498 if ((q - protocol) < sizeof(protocol) - 1)
503 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
506 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
508 /* find the filename and the optional info string in the request */
515 pstrcpy(info, sizeof(info), p);
521 if (strlen(filename) > 4 && strcmp(".asx", filename + strlen(filename) - 4) == 0) {
523 filename[strlen(filename)-1] = 'f';
528 if (strlen(filename) > 4 &&
529 (strcmp(".rpm", filename + strlen(filename) - 4) == 0 ||
530 strcmp(".ram", filename + strlen(filename) - 4) == 0)) {
532 strcpy(filename + strlen(filename)-2, "m");
537 stream = first_stream;
538 while (stream != NULL) {
539 if (!strcmp(stream->filename, filename))
541 stream = stream->next;
543 if (stream == NULL) {
544 sprintf(msg, "File '%s' not found", url);
548 if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
549 /* See if we meet the bandwidth requirements */
550 for(i=0;i<stream->nb_streams;i++) {
551 AVStream *st = stream->streams[i];
552 switch(st->codec.codec_type) {
553 case CODEC_TYPE_AUDIO:
554 c->bandwidth += st->codec.bit_rate;
556 case CODEC_TYPE_VIDEO:
557 c->bandwidth += st->codec.bit_rate;
565 c->bandwidth /= 1000;
566 nb_bandwidth += c->bandwidth;
568 if (post == 0 && nb_max_bandwidth < nb_bandwidth) {
571 q += sprintf(q, "HTTP/1.0 200 Server too busy\r\n");
572 q += sprintf(q, "Content-type: text/html\r\n");
573 q += sprintf(q, "\r\n");
574 q += sprintf(q, "<html><head><title>Too busy</title></head><body>\r\n");
575 q += sprintf(q, "The server is too busy to serve your request at this time.<p>\r\n");
576 q += sprintf(q, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
577 nb_bandwidth, nb_max_bandwidth);
578 q += sprintf(q, "</body></html>\r\n");
580 /* prepare output buffer */
581 c->buffer_ptr = c->buffer;
583 c->state = HTTPSTATE_SEND_HEADER;
587 if (doing_asx || doing_ram) {
590 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
591 if (strncasecmp(p, "Host:", 5) == 0) {
606 while (isspace(*hostinfo))
609 eoh = strchr(hostinfo, '\n');
614 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
615 memcpy(hostbuf, hostinfo, eoh - hostinfo);
616 hostbuf[eoh - hostinfo] = 0;
621 q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n");
622 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
623 q += sprintf(q, "\r\n");
624 q += sprintf(q, "<ASX Version=\"3\">\r\n");
625 q += sprintf(q, "<!-- Autogenerated by ffserver -->\r\n");
626 q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
627 hostbuf, filename, info);
628 q += sprintf(q, "</ASX>\r\n");
629 } else if (doing_ram) {
630 q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n");
631 q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n");
632 q += sprintf(q, "\r\n");
633 q += sprintf(q, "# Autogenerated by ffserver\r\n");
634 q += sprintf(q, "http://%s/%s%s\r\n",
635 hostbuf, filename, info);
639 /* prepare output buffer */
640 c->buffer_ptr = c->buffer;
642 c->state = HTTPSTATE_SEND_HEADER;
648 sprintf(msg, "ASX/RAM file not handled");
653 stream->conns_served++;
655 /* XXX: add there authenticate and IP match */
658 /* if post, it means a feed is being sent */
659 if (!stream->is_feed) {
660 /* However it might be a status report from WMP! Lets log the data
661 * as it might come in handy one day
665 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
666 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
678 char *eol = strchr(logline, '\n');
685 http_log("%.*s\n", eol - logline, logline);
690 sprintf(msg, "POST command not handled");
693 if (http_start_receive_data(c) < 0) {
694 sprintf(msg, "could not open feed");
698 c->state = HTTPSTATE_RECEIVE_DATA;
702 if (c->stream->stream_type == STREAM_TYPE_STATUS)
705 /* open input stream */
706 if (open_input_stream(c, info) < 0) {
707 sprintf(msg, "Input stream corresponding to '%s' not found", url);
711 /* prepare http header */
713 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
714 mime_type = c->stream->fmt->mime_type;
716 mime_type = "application/x-octet_stream";
717 q += sprintf(q, "Pragma: no-cache\r\n");
719 /* for asf, we need extra headers */
720 if (!strcmp(c->stream->fmt->name,"asf")) {
721 q += sprintf(q, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=1234\r\nPragma: features=\"broadcast\"\r\n");
722 /* mime_type = "application/octet-stream"; */
723 /* video/x-ms-asf seems better -- netscape doesn't crash any more! */
724 mime_type = "video/x-ms-asf";
726 q += sprintf(q, "Content-Type: %s\r\n", mime_type);
727 q += sprintf(q, "\r\n");
729 /* prepare output buffer */
731 c->buffer_ptr = c->buffer;
733 c->state = HTTPSTATE_SEND_HEADER;
738 q += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
739 q += sprintf(q, "Content-type: %s\r\n", "text/html");
740 q += sprintf(q, "\r\n");
741 q += sprintf(q, "<HTML>\n");
742 q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
743 q += sprintf(q, "<BODY>%s</BODY>\n", msg);
744 q += sprintf(q, "</HTML>\n");
746 /* prepare output buffer */
747 c->buffer_ptr = c->buffer;
749 c->state = HTTPSTATE_SEND_HEADER;
753 c->http_error = 200; /* horrible : we use this value to avoid
754 going to the send data state */
755 c->state = HTTPSTATE_SEND_HEADER;
759 static void compute_stats(HTTPContext *c)
768 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
769 q += sprintf(q, "Content-type: %s\r\n", "text/html");
770 q += sprintf(q, "Pragma: no-cache\r\n");
771 q += sprintf(q, "\r\n");
773 q += sprintf(q, "<HEAD><TITLE>FFServer Status</TITLE></HEAD>\n<BODY>");
774 q += sprintf(q, "<H1>FFServer Status</H1>\n");
776 q += sprintf(q, "<H2>Available Streams</H2>\n");
777 q += sprintf(q, "<TABLE cellspacing=0 cellpadding=4>\n");
778 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");
779 stream = first_stream;
780 while (stream != NULL) {
781 char sfilename[1024];
784 if (stream->feed != stream) {
785 pstrcpy(sfilename, sizeof(sfilename) - 1, stream->filename);
786 eosf = sfilename + strlen(sfilename);
787 if (eosf - sfilename >= 4) {
788 if (strcmp(eosf - 4, ".asf") == 0) {
789 strcpy(eosf - 4, ".asx");
790 } else if (strcmp(eosf - 3, ".rm") == 0) {
791 strcpy(eosf - 3, ".ram");
795 q += sprintf(q, "<TR><TD><A HREF=\"/%s\">%s</A> ",
796 sfilename, stream->filename);
797 q += sprintf(q, "<td align=right> %d <td align=right> %lld",
798 stream->conns_served, stream->bytes_served / 1000);
799 switch(stream->stream_type) {
800 case STREAM_TYPE_LIVE:
802 int audio_bit_rate = 0;
803 int video_bit_rate = 0;
804 char *audio_codec_name = "";
805 char *video_codec_name = "";
806 char *audio_codec_name_extra = "";
807 char *video_codec_name_extra = "";
809 for(i=0;i<stream->nb_streams;i++) {
810 AVStream *st = stream->streams[i];
811 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
812 switch(st->codec.codec_type) {
813 case CODEC_TYPE_AUDIO:
814 audio_bit_rate += st->codec.bit_rate;
816 if (*audio_codec_name)
817 audio_codec_name_extra = "...";
818 audio_codec_name = codec->name;
821 case CODEC_TYPE_VIDEO:
822 video_bit_rate += st->codec.bit_rate;
824 if (*video_codec_name)
825 video_codec_name_extra = "...";
826 video_codec_name = codec->name;
833 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",
835 (audio_bit_rate + video_bit_rate) / 1000,
836 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
837 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
839 q += sprintf(q, "<TD>%s", stream->feed->filename);
841 q += sprintf(q, "<TD>%s", stream->feed_filename);
843 q += sprintf(q, "\n");
847 q += sprintf(q, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
851 stream = stream->next;
853 q += sprintf(q, "</TABLE>\n");
855 stream = first_stream;
856 while (stream != NULL) {
857 if (stream->feed == stream) {
858 q += sprintf(q, "<h2>Feed %s</h2>", stream->filename);
859 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");
861 for (i = 0; i < stream->nb_streams; i++) {
862 AVStream *st = stream->streams[i];
863 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
864 char *type = "unknown";
869 switch(st->codec.codec_type) {
870 case CODEC_TYPE_AUDIO:
873 case CODEC_TYPE_VIDEO:
875 sprintf(parameters, "%dx%d, q=%d-%d", st->codec.width, st->codec.height,
876 st->codec.qmin, st->codec.qmax);
881 q += sprintf(q, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
882 i, type, st->codec.bit_rate/1000, codec ? codec->name : "", parameters);
884 q += sprintf(q, "</table>\n");
887 stream = stream->next;
898 while (stream != NULL) {
899 q += sprintf(q, "<H1>Feed '%s'</H1>\n", stream->filename);
900 q += sprintf(q, "<TABLE>\n");
901 q += sprintf(q, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
902 for(i=0;i<stream->nb_streams;i++) {
903 AVStream *st = stream->streams[i];
904 FeedData *fdata = st->priv_data;
907 avcodec_string(buf, sizeof(buf), enc);
908 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
909 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
910 avg /= enc->frame_size;
911 q += sprintf(q, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
912 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
914 q += sprintf(q, "</TABLE>\n");
915 stream = stream->next_feed;
920 /* connection status */
921 q += sprintf(q, "<H2>Connection Status</H2>\n");
923 q += sprintf(q, "Number of connections: %d / %d<BR>\n",
924 nb_connections, nb_max_connections);
926 q += sprintf(q, "Bandwidth in use: %dk / %dk<BR>\n",
927 nb_bandwidth, nb_max_bandwidth);
929 q += sprintf(q, "<TABLE>\n");
930 q += sprintf(q, "<TR><TD>#<TD>File<TD>IP<TD>State<TD>Size\n");
933 while (c1 != NULL && q < (char *) c->buffer + sizeof(c->buffer) - 2048) {
935 p = inet_ntoa(c1->from_addr.sin_addr);
936 q += sprintf(q, "<TR><TD><B>%d</B><TD>%s%s <TD> %s <TD> %s <TD> %Ld\n",
937 i, c1->stream->filename,
938 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
940 http_state[c1->state],
944 q += sprintf(q, "</TABLE>\n");
949 q += sprintf(q, "<HR size=1 noshade>Generated at %s", p);
950 q += sprintf(q, "</BODY>\n</HTML>\n");
952 c->buffer_ptr = c->buffer;
957 static void http_write_packet(void *opaque,
958 unsigned char *buf, int size)
960 HTTPContext *c = opaque;
962 if (c->buffer_ptr == c->buffer_end || !c->buffer_ptr)
963 c->buffer_ptr = c->buffer_end = c->buffer;
965 if (c->buffer_end - c->buffer + size > IOBUFFER_MAX_SIZE)
968 memcpy(c->buffer_end, buf, size);
969 c->buffer_end += size;
972 static int open_input_stream(HTTPContext *c, const char *info)
975 char input_filename[1024];
981 if (c->stream->feed) {
982 strcpy(input_filename, c->stream->feed->feed_filename);
983 buf_size = FFM_PACKET_SIZE;
984 /* compute position (absolute time) */
985 if (find_info_tag(buf, sizeof(buf), "date", info)) {
986 stream_pos = parse_date(buf, 0);
987 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
988 int prebuffer = strtol(buf, 0, 10);
989 stream_pos = gettime() - prebuffer * 1000000;
991 stream_pos = gettime() - c->stream->prebuffer * 1000;
994 strcpy(input_filename, c->stream->feed_filename);
996 /* compute position (relative time) */
997 if (find_info_tag(buf, sizeof(buf), "date", info)) {
998 stream_pos = parse_date(buf, 1);
1003 if (input_filename[0] == '\0')
1007 if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0)
1011 if (c->fmt_in->iformat->read_seek) {
1012 c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
1015 // printf("stream %s opened pos=%0.6f\n", input_filename, stream_pos / 1000000.0);
1019 static int http_prepare_data(HTTPContext *c)
1024 case HTTPSTATE_SEND_DATA_HEADER:
1025 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
1026 if (c->stream->feed) {
1027 /* open output stream by using specified codecs */
1028 c->fmt_ctx.oformat = c->stream->fmt;
1029 c->fmt_ctx.nb_streams = c->stream->nb_streams;
1030 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
1032 st = av_mallocz(sizeof(AVStream));
1033 c->fmt_ctx.streams[i] = st;
1034 if (c->stream->feed == c->stream)
1035 memcpy(st, c->stream->streams[i], sizeof(AVStream));
1037 memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]], sizeof(AVStream));
1039 st->codec.frame_number = 0; /* XXX: should be done in
1040 AVStream, not in codec */
1042 c->got_key_frame = 0;
1044 /* open output stream by using codecs in specified file */
1045 c->fmt_ctx.oformat = c->stream->fmt;
1046 c->fmt_ctx.nb_streams = c->fmt_in->nb_streams;
1047 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
1049 st = av_mallocz(sizeof(AVStream));
1050 c->fmt_ctx.streams[i] = st;
1051 memcpy(st, c->fmt_in->streams[i], sizeof(AVStream));
1052 st->codec.frame_number = 0; /* XXX: should be done in
1053 AVStream, not in codec */
1055 c->got_key_frame = 0;
1057 init_put_byte(&c->fmt_ctx.pb, c->pbuffer, PACKET_MAX_SIZE,
1058 1, c, NULL, http_write_packet, NULL);
1059 c->fmt_ctx.pb.is_streamed = 1;
1060 /* prepare header */
1061 av_write_header(&c->fmt_ctx);
1062 c->state = HTTPSTATE_SEND_DATA;
1063 c->last_packet_sent = 0;
1065 case HTTPSTATE_SEND_DATA:
1066 /* find a new packet */
1068 fifo_total_size = http_fifo_write_count - c->last_http_fifo_write_count;
1069 if (fifo_total_size >= ((3 * FIFO_MAX_SIZE) / 4)) {
1070 /* overflow : resync. We suppose that wptr is at this
1071 point a pointer to a valid packet */
1072 c->rptr = http_fifo.wptr;
1073 c->got_key_frame = 0;
1076 start_rptr = c->rptr;
1077 if (fifo_read(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &c->rptr) < 0)
1079 payload_size = ntohs(hdr.payload_size);
1080 payload = av_malloc(payload_size);
1081 if (fifo_read(&http_fifo, payload, payload_size, &c->rptr) < 0) {
1082 /* cannot read all the payload */
1084 c->rptr = start_rptr;
1088 c->last_http_fifo_write_count = http_fifo_write_count -
1089 fifo_size(&http_fifo, c->rptr);
1091 if (c->stream->stream_type != STREAM_TYPE_MASTER) {
1092 /* test if the packet can be handled by this format */
1094 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
1095 AVStream *st = c->fmt_ctx.streams[i];
1096 if (test_header(&hdr, &st->codec)) {
1097 /* only begin sending when got a key frame */
1098 if (st->codec.key_frame)
1099 c->got_key_frame |= 1 << i;
1100 if (c->got_key_frame & (1 << i)) {
1101 ret = c->fmt_ctx.format->write_packet(&c->fmt_ctx, i,
1102 payload, payload_size);
1108 /* must send trailer now */
1109 c->state = HTTPSTATE_SEND_DATA_TRAILER;
1112 /* master case : send everything */
1115 memcpy(q, &hdr, sizeof(hdr));
1117 memcpy(q, payload, payload_size);
1119 c->buffer_ptr = c->buffer;
1127 /* read a packet from the input stream */
1128 if (c->stream->feed) {
1129 ffm_set_write_index(c->fmt_in,
1130 c->stream->feed->feed_write_index,
1131 c->stream->feed->feed_size);
1134 if (c->stream->max_time &&
1135 c->stream->max_time + c->start_time > time(0)) {
1136 /* We have timed out */
1137 c->state = HTTPSTATE_SEND_DATA_TRAILER;
1138 } else if (av_read_packet(c->fmt_in, &pkt) < 0) {
1139 if (c->stream->feed && c->stream->feed->feed_opened) {
1140 /* if coming from feed, it means we reached the end of the
1141 ffm file, so must wait for more data */
1142 c->state = HTTPSTATE_WAIT_FEED;
1143 return 1; /* state changed */
1145 /* must send trailer now because eof or error */
1146 c->state = HTTPSTATE_SEND_DATA_TRAILER;
1149 /* send it to the appropriate stream */
1150 if (c->stream->feed) {
1151 /* if coming from a feed, select the right stream */
1152 for(i=0;i<c->stream->nb_streams;i++) {
1153 if (c->stream->feed_streams[i] == pkt.stream_index) {
1154 pkt.stream_index = i;
1155 if (pkt.flags & PKT_FLAG_KEY) {
1156 c->got_key_frame |= 1 << i;
1158 /* See if we have all the key frames, then
1159 * we start to send. This logic is not quite
1160 * right, but it works for the case of a
1161 * single video stream with one or more
1162 * audio streams (for which every frame is
1163 * typically a key frame).
1165 if (!c->stream->send_on_key || ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
1171 AVCodecContext *codec;
1174 codec = &c->fmt_ctx.streams[pkt.stream_index]->codec;
1176 codec->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
1179 if (codec->codec_type == CODEC_TYPE_AUDIO) {
1180 codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
1181 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
1185 if (av_write_packet(&c->fmt_ctx, &pkt, 0))
1186 c->state = HTTPSTATE_SEND_DATA_TRAILER;
1188 codec->frame_number++;
1191 av_free_packet(&pkt);
1196 case HTTPSTATE_SEND_DATA_TRAILER:
1197 /* last packet test ? */
1198 if (c->last_packet_sent)
1200 /* prepare header */
1201 av_write_trailer(&c->fmt_ctx);
1202 c->last_packet_sent = 1;
1208 /* should convert the format at the same time */
1209 static int http_send_data(HTTPContext *c)
1213 while (c->buffer_ptr >= c->buffer_end) {
1214 ret = http_prepare_data(c);
1217 else if (ret == 0) {
1220 /* state change requested */
1225 if (c->buffer_end > c->buffer_ptr) {
1226 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
1228 if (errno != EAGAIN && errno != EINTR) {
1229 /* error : close connection */
1233 c->buffer_ptr += len;
1234 c->data_count += len;
1236 c->stream->bytes_served += len;
1242 static int http_start_receive_data(HTTPContext *c)
1246 if (c->stream->feed_opened)
1250 fd = open(c->stream->feed_filename, O_RDWR);
1255 c->stream->feed_write_index = ffm_read_write_index(fd);
1256 c->stream->feed_size = lseek(fd, 0, SEEK_END);
1257 lseek(fd, 0, SEEK_SET);
1259 /* init buffer input */
1260 c->buffer_ptr = c->buffer;
1261 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
1262 c->stream->feed_opened = 1;
1266 static int http_receive_data(HTTPContext *c)
1270 if (c->buffer_end > c->buffer_ptr) {
1273 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
1275 if (errno != EAGAIN && errno != EINTR) {
1276 /* error : close connection */
1279 } else if (len == 0) {
1280 /* end of connection : close it */
1283 c->buffer_ptr += len;
1284 c->data_count += len;
1288 if (c->buffer_ptr >= c->buffer_end) {
1289 FFStream *feed = c->stream;
1290 /* a packet has been received : write it in the store, except
1292 if (c->data_count > FFM_PACKET_SIZE) {
1294 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
1295 /* XXX: use llseek or url_seek */
1296 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
1297 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
1299 feed->feed_write_index += FFM_PACKET_SIZE;
1300 /* update file size */
1301 if (feed->feed_write_index > c->stream->feed_size)
1302 feed->feed_size = feed->feed_write_index;
1304 /* handle wrap around if max file size reached */
1305 if (feed->feed_write_index >= c->stream->feed_max_size)
1306 feed->feed_write_index = FFM_PACKET_SIZE;
1309 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
1311 /* wake up any waiting connections */
1312 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
1313 if (c1->state == HTTPSTATE_WAIT_FEED &&
1314 c1->stream->feed == c->stream->feed) {
1315 c1->state = HTTPSTATE_SEND_DATA;
1319 /* We have a header in our hands that contains useful data */
1321 AVInputFormat *fmt_in;
1322 ByteIOContext *pb = &s.pb;
1325 memset(&s, 0, sizeof(s));
1327 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
1328 pb->buf_end = c->buffer_end; /* ?? */
1329 pb->is_streamed = 1;
1331 /* use feed output format name to find corresponding input format */
1332 fmt_in = av_find_input_format(feed->fmt->name);
1336 s.priv_data = av_mallocz(fmt_in->priv_data_size);
1340 if (fmt_in->read_header(&s, 0) < 0) {
1341 av_freep(&s.priv_data);
1345 /* Now we have the actual streams */
1346 if (s.nb_streams != feed->nb_streams) {
1347 av_freep(&s.priv_data);
1350 for (i = 0; i < s.nb_streams; i++) {
1351 memcpy(&feed->streams[i]->codec,
1352 &s.streams[i]->codec, sizeof(AVCodecContext));
1354 av_freep(&s.priv_data);
1356 c->buffer_ptr = c->buffer;
1361 c->stream->feed_opened = 0;
1366 /* return the stream number in the feed */
1367 int add_av_stream(FFStream *feed,
1371 AVCodecContext *av, *av1;
1375 for(i=0;i<feed->nb_streams;i++) {
1376 st = feed->streams[i];
1378 if (av1->codec_id == av->codec_id &&
1379 av1->codec_type == av->codec_type &&
1380 av1->bit_rate == av->bit_rate) {
1382 switch(av->codec_type) {
1383 case CODEC_TYPE_AUDIO:
1384 if (av1->channels == av->channels &&
1385 av1->sample_rate == av->sample_rate)
1388 case CODEC_TYPE_VIDEO:
1389 if (av1->width == av->width &&
1390 av1->height == av->height &&
1391 av1->frame_rate == av->frame_rate &&
1392 av1->gop_size == av->gop_size)
1401 fst = av_mallocz(sizeof(AVStream));
1404 fst->priv_data = av_mallocz(sizeof(FeedData));
1405 memcpy(&fst->codec, av, sizeof(AVCodecContext));
1406 feed->streams[feed->nb_streams++] = fst;
1407 return feed->nb_streams - 1;
1412 /* compute the needed AVStream for each feed */
1413 void build_feed_streams(void)
1415 FFStream *stream, *feed;
1418 /* gather all streams */
1419 for(stream = first_stream; stream != NULL; stream = stream->next) {
1420 feed = stream->feed;
1422 if (!stream->is_feed) {
1423 for(i=0;i<stream->nb_streams;i++) {
1424 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
1427 for(i=0;i<stream->nb_streams;i++) {
1428 stream->feed_streams[i] = i;
1434 /* create feed files if needed */
1435 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
1438 if (!url_exist(feed->feed_filename)) {
1439 AVFormatContext s1, *s = &s1;
1441 /* only write the header of the ffm file */
1442 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
1443 fprintf(stderr, "Could not open output feed file '%s'\n",
1444 feed->feed_filename);
1447 s->oformat = feed->fmt;
1448 s->nb_streams = feed->nb_streams;
1449 for(i=0;i<s->nb_streams;i++) {
1451 st = feed->streams[i];
1455 /* XXX: need better api */
1456 av_freep(&s->priv_data);
1459 /* get feed size and write index */
1460 fd = open(feed->feed_filename, O_RDONLY);
1462 fprintf(stderr, "Could not open output feed file '%s'\n",
1463 feed->feed_filename);
1467 feed->feed_write_index = ffm_read_write_index(fd);
1468 feed->feed_size = lseek(fd, 0, SEEK_END);
1469 /* ensure that we do not wrap before the end of file */
1470 if (feed->feed_max_size < feed->feed_size)
1471 feed->feed_max_size = feed->feed_size;
1477 static void get_arg(char *buf, int buf_size, const char **pp)
1484 while (isspace(*p)) p++;
1487 if (*p == '\"' || *p == '\'')
1499 if ((q - buf) < buf_size - 1)
1504 if (quote && *p == quote)
1509 /* add a codec and set the default parameters */
1510 void add_codec(FFStream *stream, AVCodecContext *av)
1514 /* compute default parameters */
1515 switch(av->codec_type) {
1516 case CODEC_TYPE_AUDIO:
1517 if (av->bit_rate == 0)
1518 av->bit_rate = 64000;
1519 if (av->sample_rate == 0)
1520 av->sample_rate = 22050;
1521 if (av->channels == 0)
1524 case CODEC_TYPE_VIDEO:
1525 if (av->bit_rate == 0)
1526 av->bit_rate = 64000;
1527 if (av->frame_rate == 0)
1528 av->frame_rate = 5 * FRAME_RATE_BASE;
1529 if (av->width == 0 || av->height == 0) {
1533 /* Bitrate tolerance is less for streaming */
1534 if (av->bit_rate_tolerance == 0)
1535 av->bit_rate_tolerance = av->bit_rate / 4;
1540 if (av->max_qdiff == 0)
1542 av->qcompress = 0.5;
1550 st = av_mallocz(sizeof(AVStream));
1553 stream->streams[stream->nb_streams++] = st;
1554 memcpy(&st->codec, av, sizeof(AVCodecContext));
1557 int opt_audio_codec(const char *arg)
1563 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
1568 return CODEC_ID_NONE;
1574 int opt_video_codec(const char *arg)
1580 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
1585 return CODEC_ID_NONE;
1591 int parse_ffconfig(const char *filename)
1598 int val, errors, line_num;
1599 FFStream **last_stream, *stream;
1600 FFStream **last_feed, *feed;
1601 AVCodecContext audio_enc, video_enc;
1602 int audio_id, video_id;
1604 f = fopen(filename, "r");
1612 first_stream = NULL;
1613 last_stream = &first_stream;
1615 last_feed = &first_feed;
1618 audio_id = CODEC_ID_NONE;
1619 video_id = CODEC_ID_NONE;
1621 if (fgets(line, sizeof(line), f) == NULL)
1627 if (*p == '\0' || *p == '#')
1630 get_arg(cmd, sizeof(cmd), &p);
1632 if (!strcasecmp(cmd, "Port")) {
1633 get_arg(arg, sizeof(arg), &p);
1634 my_addr.sin_port = htons (atoi(arg));
1635 } else if (!strcasecmp(cmd, "BindAddress")) {
1636 get_arg(arg, sizeof(arg), &p);
1637 if (!inet_aton(arg, &my_addr.sin_addr)) {
1638 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
1639 filename, line_num, arg);
1642 } else if (!strcasecmp(cmd, "MaxClients")) {
1643 get_arg(arg, sizeof(arg), &p);
1645 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
1646 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
1647 filename, line_num, arg);
1650 nb_max_connections = val;
1652 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
1653 get_arg(arg, sizeof(arg), &p);
1655 if (val < 10 || val > 100000) {
1656 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
1657 filename, line_num, arg);
1660 nb_max_bandwidth = val;
1662 } else if (!strcasecmp(cmd, "CustomLog")) {
1663 get_arg(logfilename, sizeof(logfilename), &p);
1664 } else if (!strcasecmp(cmd, "<Feed")) {
1665 /*********************************************/
1666 /* Feed related options */
1668 if (stream || feed) {
1669 fprintf(stderr, "%s:%d: Already in a tag\n",
1670 filename, line_num);
1672 feed = av_mallocz(sizeof(FFStream));
1673 /* add in stream list */
1674 *last_stream = feed;
1675 last_stream = &feed->next;
1676 /* add in feed list */
1678 last_feed = &feed->next_feed;
1680 get_arg(feed->filename, sizeof(feed->filename), &p);
1681 q = strrchr(feed->filename, '>');
1684 feed->fmt = guess_format("ffm", NULL, NULL);
1685 /* defaut feed file */
1686 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
1687 "/tmp/%s.ffm", feed->filename);
1688 feed->feed_max_size = 5 * 1024 * 1024;
1690 feed->feed = feed; /* self feeding :-) */
1692 } else if (!strcasecmp(cmd, "File")) {
1694 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
1695 } else if (stream) {
1696 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
1698 } else if (!strcasecmp(cmd, "FileMaxSize")) {
1703 get_arg(arg, sizeof(arg), &p);
1705 fsize = strtod(p1, (char **)&p1);
1706 switch(toupper(*p1)) {
1711 fsize *= 1024 * 1024;
1714 fsize *= 1024 * 1024 * 1024;
1717 feed->feed_max_size = (INT64)fsize;
1719 } else if (!strcasecmp(cmd, "</Feed>")) {
1721 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
1722 filename, line_num);
1725 /* Make sure that we start out clean */
1726 if (unlink(feed->feed_filename) < 0
1727 && errno != ENOENT) {
1728 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
1729 filename, line_num, feed->feed_filename, strerror(errno));
1734 } else if (!strcasecmp(cmd, "<Stream")) {
1735 /*********************************************/
1736 /* Stream related options */
1738 if (stream || feed) {
1739 fprintf(stderr, "%s:%d: Already in a tag\n",
1740 filename, line_num);
1742 stream = av_mallocz(sizeof(FFStream));
1743 *last_stream = stream;
1744 last_stream = &stream->next;
1746 get_arg(stream->filename, sizeof(stream->filename), &p);
1747 q = strrchr(stream->filename, '>');
1750 stream->fmt = guess_format(NULL, stream->filename, NULL);
1751 memset(&audio_enc, 0, sizeof(AVCodecContext));
1752 memset(&video_enc, 0, sizeof(AVCodecContext));
1753 audio_id = CODEC_ID_NONE;
1754 video_id = CODEC_ID_NONE;
1756 audio_id = stream->fmt->audio_codec;
1757 video_id = stream->fmt->video_codec;
1760 } else if (!strcasecmp(cmd, "Feed")) {
1761 get_arg(arg, sizeof(arg), &p);
1766 while (sfeed != NULL) {
1767 if (!strcmp(sfeed->filename, arg))
1769 sfeed = sfeed->next_feed;
1772 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
1773 filename, line_num, arg);
1775 stream->feed = sfeed;
1778 } else if (!strcasecmp(cmd, "Format")) {
1779 get_arg(arg, sizeof(arg), &p);
1780 if (!strcmp(arg, "status")) {
1781 stream->stream_type = STREAM_TYPE_STATUS;
1784 stream->stream_type = STREAM_TYPE_LIVE;
1785 /* jpeg cannot be used here, so use single frame jpeg */
1786 if (!strcmp(arg, "jpeg"))
1787 strcpy(arg, "singlejpeg");
1788 stream->fmt = guess_format(arg, NULL, NULL);
1790 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
1791 filename, line_num, arg);
1796 audio_id = stream->fmt->audio_codec;
1797 video_id = stream->fmt->video_codec;
1799 } else if (!strcasecmp(cmd, "Preroll")) {
1800 get_arg(arg, sizeof(arg), &p);
1802 stream->prebuffer = atoi(arg) * 1000;
1804 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
1806 stream->send_on_key = 1;
1808 } else if (!strcasecmp(cmd, "AudioCodec")) {
1809 get_arg(arg, sizeof(arg), &p);
1810 audio_id = opt_audio_codec(arg);
1811 if (audio_id == CODEC_ID_NONE) {
1812 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
1813 filename, line_num, arg);
1816 } else if (!strcasecmp(cmd, "VideoCodec")) {
1817 get_arg(arg, sizeof(arg), &p);
1818 video_id = opt_video_codec(arg);
1819 if (video_id == CODEC_ID_NONE) {
1820 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
1821 filename, line_num, arg);
1824 } else if (!strcasecmp(cmd, "MaxTime")) {
1825 get_arg(arg, sizeof(arg), &p);
1827 stream->max_time = atoi(arg);
1829 } else if (!strcasecmp(cmd, "AudioBitRate")) {
1830 get_arg(arg, sizeof(arg), &p);
1832 audio_enc.bit_rate = atoi(arg) * 1000;
1834 } else if (!strcasecmp(cmd, "AudioChannels")) {
1835 get_arg(arg, sizeof(arg), &p);
1837 audio_enc.channels = atoi(arg);
1839 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
1840 get_arg(arg, sizeof(arg), &p);
1842 audio_enc.sample_rate = atoi(arg);
1844 } else if (!strcasecmp(cmd, "VideoBitRate")) {
1845 get_arg(arg, sizeof(arg), &p);
1847 video_enc.bit_rate = atoi(arg) * 1000;
1849 } else if (!strcasecmp(cmd, "VideoSize")) {
1850 get_arg(arg, sizeof(arg), &p);
1852 parse_image_size(&video_enc.width, &video_enc.height, arg);
1853 if ((video_enc.width % 16) != 0 ||
1854 (video_enc.height % 16) != 0) {
1855 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
1856 filename, line_num);
1860 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
1861 get_arg(arg, sizeof(arg), &p);
1863 video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
1865 } else if (!strcasecmp(cmd, "VideoGopSize")) {
1866 get_arg(arg, sizeof(arg), &p);
1868 video_enc.gop_size = atoi(arg);
1870 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
1872 video_enc.gop_size = 1;
1874 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
1876 video_enc.flags |= CODEC_FLAG_HQ;
1878 } else if (!strcasecmp(cmd, "VideoQDiff")) {
1880 video_enc.max_qdiff = atoi(arg);
1881 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
1882 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
1883 filename, line_num);
1887 } else if (!strcasecmp(cmd, "VideoQMax")) {
1889 video_enc.qmax = atoi(arg);
1890 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
1891 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
1892 filename, line_num);
1896 } else if (!strcasecmp(cmd, "VideoQMin")) {
1898 video_enc.qmin = atoi(arg);
1899 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
1900 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
1901 filename, line_num);
1905 } else if (!strcasecmp(cmd, "NoVideo")) {
1906 video_id = CODEC_ID_NONE;
1907 } else if (!strcasecmp(cmd, "NoAudio")) {
1908 audio_id = CODEC_ID_NONE;
1909 } else if (!strcasecmp(cmd, "</Stream>")) {
1911 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
1912 filename, line_num);
1915 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
1916 if (audio_id != CODEC_ID_NONE) {
1917 audio_enc.codec_type = CODEC_TYPE_AUDIO;
1918 audio_enc.codec_id = audio_id;
1919 add_codec(stream, &audio_enc);
1921 if (video_id != CODEC_ID_NONE) {
1922 video_enc.codec_type = CODEC_TYPE_VIDEO;
1923 video_enc.codec_id = video_id;
1924 add_codec(stream, &video_enc);
1929 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
1930 filename, line_num, cmd);
1943 void *http_server_thread(void *arg)
1945 http_server(my_addr);
1950 static void write_packet(FFCodec *ffenc,
1951 UINT8 *buf, int size)
1954 AVCodecContext *enc = &ffenc->enc;
1956 mk_header(&hdr, enc, size);
1957 wptr = http_fifo.wptr;
1958 fifo_write(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &wptr);
1959 fifo_write(&http_fifo, buf, size, &wptr);
1960 /* atomic modification of wptr */
1961 http_fifo.wptr = wptr;
1962 ffenc->data_count += size;
1963 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
1969 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
1970 "usage: ffserver [-L] [-h] [-f configfile]\n"
1971 "Hyper fast multi format Audio/Video streaming server\n"
1973 "-L : print the LICENCE\n"
1975 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
1982 "ffserver version " FFMPEG_VERSION "\n"
1983 "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
1984 "This library is free software; you can redistribute it and/or\n"
1985 "modify it under the terms of the GNU Lesser General Public\n"
1986 "License as published by the Free Software Foundation; either\n"
1987 "version 2 of the License, or (at your option) any later version.\n"
1989 "This library is distributed in the hope that it will be useful,\n"
1990 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1991 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
1992 "Lesser General Public License for more details.\n"
1994 "You should have received a copy of the GNU Lesser General Public\n"
1995 "License along with this library; if not, write to the Free Software\n"
1996 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
2000 int main(int argc, char **argv)
2002 const char *config_filename;
2007 config_filename = "/etc/ffserver.conf";
2010 c = getopt_long_only(argc, argv, "Lh?f:", NULL, NULL);
2022 config_filename = optarg;
2029 /* address on which the server will handle connections */
2030 my_addr.sin_family = AF_INET;
2031 my_addr.sin_port = htons (8080);
2032 my_addr.sin_addr.s_addr = htonl (INADDR_ANY);
2033 nb_max_connections = 5;
2034 nb_max_bandwidth = 1000;
2035 first_stream = NULL;
2036 logfilename[0] = '\0';
2038 if (parse_ffconfig(config_filename) < 0) {
2039 fprintf(stderr, "Incorrect config file - exiting.\n");
2043 build_feed_streams();
2046 signal(SIGPIPE, SIG_IGN);
2048 /* open log file if needed */
2049 if (logfilename[0] != '\0') {
2050 if (!strcmp(logfilename, "-"))
2053 logfile = fopen(logfilename, "w");
2056 if (http_server(my_addr) < 0) {
2057 fprintf(stderr, "Could not start http server\n");