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