]> git.sesse.net Git - ffmpeg/blob - ffserver.c
update
[ffmpeg] / ffserver.c
1 /*
2  * Multiple format streaming server
3  * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
4  *
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.
9  *
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.
14  *
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
18  */
19 #define HAVE_AV_CONFIG_H
20 #include "avformat.h"
21
22 #include <stdarg.h>
23 #include <netinet/in.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/ioctl.h>
27 #include <sys/poll.h>
28 #include <errno.h>
29 #include <sys/time.h>
30 #include <time.h>
31 #include <getopt.h>
32 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <arpa/inet.h>
35 #include <netdb.h>
36 #include <ctype.h>
37 #include <signal.h>
38
39 /* maximum number of simultaneous HTTP connections */
40 #define HTTP_MAX_CONNECTIONS 2000
41
42 enum HTTPState {
43     HTTPSTATE_WAIT_REQUEST,
44     HTTPSTATE_SEND_HEADER,
45     HTTPSTATE_SEND_DATA_HEADER,
46     HTTPSTATE_SEND_DATA,
47     HTTPSTATE_SEND_DATA_TRAILER,
48     HTTPSTATE_RECEIVE_DATA,
49     HTTPSTATE_WAIT_FEED,
50 };
51
52 const char *http_state[] = {
53     "WAIT_REQUEST",
54     "SEND_HEADER",
55     "SEND_DATA_HEADER",
56     "SEND_DATA",
57     "SEND_DATA_TRAILER",
58     "RECEIVE_DATA",
59     "WAIT_FEED",
60 };
61
62 #define IOBUFFER_MAX_SIZE 32768
63 #define PACKET_MAX_SIZE 16384
64
65 /* coef for exponential mean for bitrate estimation in statistics */
66 #define AVG_COEF 0.9
67
68 /* timeouts are in ms */
69 #define REQUEST_TIMEOUT (15 * 1000)
70 #define SYNC_TIMEOUT (10 * 1000)
71
72 /* context associated with one connection */
73 typedef struct HTTPContext {
74     enum HTTPState state;
75     int fd; /* socket file descriptor */
76     struct sockaddr_in from_addr; /* origin */
77     struct pollfd *poll_entry; /* used when polling */
78     long timeout;
79     UINT8 *buffer_ptr, *buffer_end;
80     int http_error;
81     struct HTTPContext *next;
82     int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
83     INT64 data_count;
84     /* feed input */
85     int feed_fd;
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 */
92     int suppress_log;
93     int bandwidth;
94     time_t start_time;
95     char protocol[16];
96     char method[16];
97     char url[128];
98     UINT8 buffer[IOBUFFER_MAX_SIZE];
99     UINT8 pbuffer[PACKET_MAX_SIZE];
100 } HTTPContext;
101
102 /* each generated stream is described here */
103 enum StreamType {
104     STREAM_TYPE_LIVE,
105     STREAM_TYPE_STATUS,
106 };
107
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;
113     AVOutputFormat *fmt;
114     int nb_streams;
115     int prebuffer;      /* Number of millseconds early to start */
116     time_t max_time;
117     int send_on_key;
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;
123     /* feed specific */
124     int feed_opened;     /* true if someone if writing to feed */
125     int is_feed;         /* true if it is a feed */
126     int conns_served;
127     INT64 bytes_served;
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;
132 } FFStream;
133
134 typedef struct FeedData {
135     long long data_count;
136     float avg_frame_size;   /* frame size averraged over last frames with exponential mean */
137 } FeedData;
138
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 */
144
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);
152
153 int nb_max_connections;
154 int nb_connections;
155
156 int nb_max_bandwidth;
157 int nb_bandwidth;
158
159 static long gettime_ms(void)
160 {
161     struct timeval tv;
162
163     gettimeofday(&tv,NULL);
164     return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
165 }
166
167 static FILE *logfile = NULL;
168
169 static void http_log(char *fmt, ...)
170 {
171     va_list ap;
172     va_start(ap, fmt);
173     
174     if (logfile) {
175         vfprintf(logfile, fmt, ap);
176         fflush(logfile);
177     }
178     va_end(ap);
179 }
180
181 static void log_connection(HTTPContext *c)
182 {
183     char buf1[32], buf2[32], *p;
184     time_t ti;
185
186     if (c->suppress_log) 
187         return;
188
189     /* XXX: reentrant function ? */
190     p = inet_ntoa(c->from_addr.sin_addr);
191     strcpy(buf1, p);
192     ti = time(NULL);
193     p = ctime(&ti);
194     strcpy(buf2, p);
195     p = buf2 + strlen(p) - 1;
196     if (*p == '\n')
197         *p = '\0';
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);
200 }
201
202 /* main loop of the http server */
203 static int http_server(struct sockaddr_in my_addr)
204 {
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;
209     long cur_time;
210
211     server_fd = socket(AF_INET,SOCK_STREAM,0);
212     if (server_fd < 0) {
213         perror ("socket");
214         return -1;
215     }
216         
217     tmp = 1;
218     setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
219
220     if (bind (server_fd, (struct sockaddr *) &my_addr, sizeof (my_addr)) < 0) {
221         perror ("bind");
222         close(server_fd);
223         return -1;
224     }
225   
226     if (listen (server_fd, 5) < 0) {
227         perror ("listen");
228         close(server_fd);
229         return -1;
230     }
231
232     http_log("ffserver started.\n");
233
234     fcntl(server_fd, F_SETFL, O_NONBLOCK);
235     first_http_ctx = NULL;
236     nb_connections = 0;
237     first_http_ctx = NULL;
238     for(;;) {
239         poll_entry = poll_table;
240         poll_entry->fd = server_fd;
241         poll_entry->events = POLLIN;
242         poll_entry++;
243
244         /* wait for events on each HTTP handle */
245         c = first_http_ctx;
246         while (c != NULL) {
247             int fd;
248             fd = c->fd;
249             switch(c->state) {
250             case HTTPSTATE_WAIT_REQUEST:
251                 c->poll_entry = poll_entry;
252                 poll_entry->fd = fd;
253                 poll_entry->events = POLLIN;
254                 poll_entry++;
255                 break;
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;
261                 poll_entry->fd = fd;
262                 poll_entry->events = POLLOUT;
263                 poll_entry++;
264                 break;
265             case HTTPSTATE_RECEIVE_DATA:
266                 c->poll_entry = poll_entry;
267                 poll_entry->fd = fd;
268                 poll_entry->events = POLLIN;
269                 poll_entry++;
270                 break;
271             case HTTPSTATE_WAIT_FEED:
272                 /* need to catch errors */
273                 c->poll_entry = poll_entry;
274                 poll_entry->fd = fd;
275                 poll_entry->events = POLLIN;/* Maybe this will work */
276                 poll_entry++;
277                 break;
278             default:
279                 c->poll_entry = NULL;
280                 break;
281             }
282             c = c->next;
283         }
284
285         /* wait for an event on one connection. We poll at least every
286            second to handle timeouts */
287         do {
288             ret = poll(poll_table, poll_entry - poll_table, 1000);
289         } while (ret == -1);
290         
291         cur_time = gettime_ms();
292
293         /* now handle the events */
294
295         cp = &first_http_ctx;
296         while ((*cp) != NULL) {
297             c = *cp;
298             if (handle_http (c, cur_time) < 0) {
299                 /* close and free the connection */
300                 log_connection(c);
301                 close(c->fd);
302                 if (c->fmt_in)
303                     av_close_input_file(c->fmt_in);
304                 *cp = c->next;
305                 nb_bandwidth -= c->bandwidth;
306                 av_free(c);
307                 nb_connections--;
308             } else {
309                 cp = &c->next;
310             }
311         }
312
313         /* new connection request ? */
314         poll_entry = poll_table;
315         if (poll_entry->revents & POLLIN) {
316             int fd, len;
317
318             len = sizeof(from_addr);
319             fd = accept(server_fd, (struct sockaddr *)&from_addr, 
320                         &len);
321             if (fd >= 0) {
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) {
326                     close(fd);
327                 } else {
328                     /* add a new connection */
329                     c = av_mallocz(sizeof(HTTPContext));
330                     c->next = first_http_ctx;
331                     first_http_ctx = c;
332                     c->fd = fd;
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;
339                     nb_connections++;
340                 }
341             }
342         }
343         poll_entry++;
344     }
345 }
346
347 static int handle_http(HTTPContext *c, long cur_time)
348 {
349     int len;
350     
351     switch(c->state) {
352     case HTTPSTATE_WAIT_REQUEST:
353         /* timeout ? */
354         if ((c->timeout - cur_time) < 0)
355             return -1;
356         if (c->poll_entry->revents & (POLLERR | POLLHUP))
357             return -1;
358
359         /* no need to read if no events */
360         if (!(c->poll_entry->revents & POLLIN))
361             return 0;
362         /* read the data */
363         len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
364         if (len < 0) {
365             if (errno != EAGAIN && errno != EINTR)
366                 return -1;
367         } else if (len == 0) {
368             return -1;
369         } else {
370             /* search for end of request. XXX: not fully correct since garbage could come after the end */
371             UINT8 *ptr;
372             c->buffer_ptr += len;
373             ptr = c->buffer_ptr;
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)
378                     return -1;
379             } else if (ptr >= c->buffer_end) {
380                 /* request too long: cannot do anything */
381                 return -1;
382             }
383         }
384         break;
385
386     case HTTPSTATE_SEND_HEADER:
387         if (c->poll_entry->revents & (POLLERR | POLLHUP))
388             return -1;
389
390         /* no need to read if no events */
391         if (!(c->poll_entry->revents & POLLOUT))
392             return 0;
393         len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
394         if (len < 0) {
395             if (errno != EAGAIN && errno != EINTR) {
396                 /* error : close connection */
397                 return -1;
398             }
399         } else {
400             c->buffer_ptr += len;
401             if (c->stream)
402                 c->stream->bytes_served += len;
403             c->data_count += len;
404             if (c->buffer_ptr >= c->buffer_end) {
405                 /* if error, exit */
406                 if (c->http_error)
407                     return -1;
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;
411             }
412         }
413         break;
414
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))
420             return -1;
421         
422         if (!(c->poll_entry->revents & POLLOUT))
423             return 0;
424         if (http_send_data(c) < 0)
425             return -1;
426         break;
427     case HTTPSTATE_RECEIVE_DATA:
428         /* no need to read if no events */
429         if (c->poll_entry->revents & (POLLERR | POLLHUP))
430             return -1;
431         if (!(c->poll_entry->revents & POLLIN))
432             return 0;
433         if (http_receive_data(c) < 0)
434             return -1;
435         break;
436     case HTTPSTATE_WAIT_FEED:
437         /* no need to read if no events */
438         if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
439             return -1;
440
441         /* nothing to do, we'll be waken up by incoming feed packets */
442         break;
443     default:
444         return -1;
445     }
446     return 0;
447 }
448
449
450 /* parse http request and prepare header */
451 static int http_parse_request(HTTPContext *c)
452 {
453     char *p;
454     int post;
455     int doing_asx;
456     int doing_ram;
457     char cmd[32];
458     char info[1024], *filename;
459     char url[1024], *q;
460     char protocol[32];
461     char msg[1024];
462     const char *mime_type;
463     FFStream *stream;
464     int i;
465
466     p = c->buffer;
467     q = cmd;
468     while (!isspace(*p) && *p != '\0') {
469         if ((q - cmd) < sizeof(cmd) - 1)
470             *q++ = *p;
471         p++;
472     }
473     *q = '\0';
474
475     pstrcpy(c->method, sizeof(c->method), cmd);
476
477     if (!strcmp(cmd, "GET"))
478         post = 0;
479     else if (!strcmp(cmd, "POST"))
480         post = 1;
481     else
482         return -1;
483
484     while (isspace(*p)) p++;
485     q = url;
486     while (!isspace(*p) && *p != '\0') {
487         if ((q - url) < sizeof(url) - 1)
488             *q++ = *p;
489         p++;
490     }
491     *q = '\0';
492
493     pstrcpy(c->url, sizeof(c->url), url);
494
495     while (isspace(*p)) p++;
496     q = protocol;
497     while (!isspace(*p) && *p != '\0') {
498         if ((q - protocol) < sizeof(protocol) - 1)
499             *q++ = *p;
500         p++;
501     }
502     *q = '\0';
503     if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
504         return -1;
505
506     pstrcpy(c->protocol, sizeof(c->protocol), protocol);
507     
508     /* find the filename and the optional info string in the request */
509     p = url;
510     if (*p == '/')
511         p++;
512     filename = p;
513     p = strchr(p, '?');
514     if (p) {
515         pstrcpy(info, sizeof(info), p);
516         *p = '\0';
517     } else {
518         info[0] = '\0';
519     }
520
521     if (strlen(filename) > 4 && strcmp(".asx", filename + strlen(filename) - 4) == 0) {
522         doing_asx = 1;
523         filename[strlen(filename)-1] = 'f';
524     } else {
525         doing_asx = 0;
526     }
527
528     if (strlen(filename) > 4 && 
529         (strcmp(".rpm", filename + strlen(filename) - 4) == 0 ||
530          strcmp(".ram", filename + strlen(filename) - 4) == 0)) {
531         doing_ram = 1;
532         strcpy(filename + strlen(filename)-2, "m");
533     } else {
534         doing_ram = 0;
535     }
536
537     stream = first_stream;
538     while (stream != NULL) {
539         if (!strcmp(stream->filename, filename))
540             break;
541         stream = stream->next;
542     }
543     if (stream == NULL) {
544         sprintf(msg, "File '%s' not found", url);
545         goto send_error;
546     }
547
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;
555                 break;
556             case CODEC_TYPE_VIDEO:
557                 c->bandwidth += st->codec.bit_rate;
558                 break;
559             default:
560                 av_abort();
561             }
562         }
563     }
564
565     c->bandwidth /= 1000;
566     nb_bandwidth += c->bandwidth;
567
568     if (post == 0 && nb_max_bandwidth < nb_bandwidth) {
569         c->http_error = 200;
570         q = c->buffer;
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");
579
580         /* prepare output buffer */
581         c->buffer_ptr = c->buffer;
582         c->buffer_end = q;
583         c->state = HTTPSTATE_SEND_HEADER;
584         return 0;
585     }
586     
587     if (doing_asx || doing_ram) {
588         char *hostinfo = 0;
589         
590         for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
591             if (strncasecmp(p, "Host:", 5) == 0) {
592                 hostinfo = p + 5;
593                 break;
594             }
595             p = strchr(p, '\n');
596             if (!p)
597                 break;
598
599             p++;
600         }
601
602         if (hostinfo) {
603             char *eoh;
604             char hostbuf[260];
605
606             while (isspace(*hostinfo))
607                 hostinfo++;
608
609             eoh = strchr(hostinfo, '\n');
610             if (eoh) {
611                 if (eoh[-1] == '\r')
612                     eoh--;
613
614                 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
615                     memcpy(hostbuf, hostinfo, eoh - hostinfo);
616                     hostbuf[eoh - hostinfo] = 0;
617
618                     c->http_error = 200;
619                     q = c->buffer;
620                     if (doing_asx) {
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);
636                     } else
637                         av_abort();
638
639                     /* prepare output buffer */
640                     c->buffer_ptr = c->buffer;
641                     c->buffer_end = q;
642                     c->state = HTTPSTATE_SEND_HEADER;
643                     return 0;
644                 }
645             }
646         }
647
648         sprintf(msg, "ASX/RAM file not handled");
649         goto send_error;
650     }
651
652     c->stream = stream;
653     stream->conns_served++;
654
655     /* XXX: add there authenticate and IP match */
656
657     if (post) {
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
662              */
663             char *logline = 0;
664             
665             for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
666                 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
667                     logline = p;
668                     break;
669                 }
670                 p = strchr(p, '\n');
671                 if (!p)
672                     break;
673
674                 p++;
675             }
676
677             if (logline) {
678                 char *eol = strchr(logline, '\n');
679
680                 logline += 17;
681
682                 if (eol) {
683                     if (eol[-1] == '\r')
684                         eol--;
685                     http_log("%.*s\n", eol - logline, logline);
686                     c->suppress_log = 1;
687                 }
688             }
689             
690             sprintf(msg, "POST command not handled");
691             goto send_error;
692         }
693         if (http_start_receive_data(c) < 0) {
694             sprintf(msg, "could not open feed");
695             goto send_error;
696         }
697         c->http_error = 0;
698         c->state = HTTPSTATE_RECEIVE_DATA;
699         return 0;
700     }
701
702     if (c->stream->stream_type == STREAM_TYPE_STATUS)
703         goto send_stats;
704
705     /* open input stream */
706     if (open_input_stream(c, info) < 0) {
707         sprintf(msg, "Input stream corresponding to '%s' not found", url);
708         goto send_error;
709     }
710
711     /* prepare http header */
712     q = c->buffer;
713     q += sprintf(q, "HTTP/1.0 200 OK\r\n");
714     mime_type = c->stream->fmt->mime_type;
715     if (!mime_type)
716         mime_type = "application/x-octet_stream";
717     q += sprintf(q, "Pragma: no-cache\r\n");
718
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";
725     }
726     q += sprintf(q, "Content-Type: %s\r\n", mime_type);
727     q += sprintf(q, "\r\n");
728     
729     /* prepare output buffer */
730     c->http_error = 0;
731     c->buffer_ptr = c->buffer;
732     c->buffer_end = q;
733     c->state = HTTPSTATE_SEND_HEADER;
734     return 0;
735  send_error:
736     c->http_error = 404;
737     q = c->buffer;
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");
745
746     /* prepare output buffer */
747     c->buffer_ptr = c->buffer;
748     c->buffer_end = q;
749     c->state = HTTPSTATE_SEND_HEADER;
750     return 0;
751  send_stats:
752     compute_stats(c);
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;
756     return 0;
757 }
758
759 static void compute_stats(HTTPContext *c)
760 {
761     HTTPContext *c1;
762     FFStream *stream;
763     char *q, *p;
764     time_t ti;
765     int i;
766
767     q = c->buffer;
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");
772     
773     q += sprintf(q, "<HEAD><TITLE>FFServer Status</TITLE></HEAD>\n<BODY>");
774     q += sprintf(q, "<H1>FFServer Status</H1>\n");
775     /* format status */
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];
782         char *eosf;
783
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");
792                 }
793             }
794             
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:
801                 {
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 = "";
808
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;
815                             if (codec) {
816                                 if (*audio_codec_name)
817                                     audio_codec_name_extra = "...";
818                                 audio_codec_name = codec->name;
819                             }
820                             break;
821                         case CODEC_TYPE_VIDEO:
822                             video_bit_rate += st->codec.bit_rate;
823                             if (codec) {
824                                 if (*video_codec_name)
825                                     video_codec_name_extra = "...";
826                                 video_codec_name = codec->name;
827                             }
828                             break;
829                         default:
830                             av_abort();
831                         }
832                     }
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", 
834                                  stream->fmt->name,
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);
838                     if (stream->feed) {
839                         q += sprintf(q, "<TD>%s", stream->feed->filename);
840                     } else {
841                         q += sprintf(q, "<TD>%s", stream->feed_filename);
842                     }
843                     q += sprintf(q, "\n");
844                 }
845                 break;
846             default:
847                 q += sprintf(q, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
848                 break;
849             }
850         }
851         stream = stream->next;
852     }
853     q += sprintf(q, "</TABLE>\n");
854
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");
860
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";
865                 char parameters[64];
866
867                 parameters[0] = 0;
868
869                 switch(st->codec.codec_type) {
870                 case CODEC_TYPE_AUDIO:
871                     type = "audio";
872                     break;
873                 case CODEC_TYPE_VIDEO:
874                     type = "video";
875                     sprintf(parameters, "%dx%d, q=%d-%d", st->codec.width, st->codec.height,
876                                 st->codec.qmin, st->codec.qmax);
877                     break;
878                 default:
879                     av_abort();
880                 }
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);
883             }
884             q += sprintf(q, "</table>\n");
885
886         }       
887         stream = stream->next;
888     }
889     
890 #if 0
891     {
892         float avg;
893         AVCodecContext *enc;
894         char buf[1024];
895         
896         /* feed status */
897         stream = first_feed;
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;
905                 enc = &st->codec;
906             
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);
913             }
914             q += sprintf(q, "</TABLE>\n");
915             stream = stream->next_feed;
916         }
917     }
918 #endif
919
920     /* connection status */
921     q += sprintf(q, "<H2>Connection Status</H2>\n");
922
923     q += sprintf(q, "Number of connections: %d / %d<BR>\n",
924                  nb_connections, nb_max_connections);
925
926     q += sprintf(q, "Bandwidth in use: %dk / %dk<BR>\n",
927                  nb_bandwidth, nb_max_bandwidth);
928
929     q += sprintf(q, "<TABLE>\n");
930     q += sprintf(q, "<TR><TD>#<TD>File<TD>IP<TD>State<TD>Size\n");
931     c1 = first_http_ctx;
932     i = 0;
933     while (c1 != NULL && q < (char *) c->buffer + sizeof(c->buffer) - 2048) {
934         i++;
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)" : "",
939                      p, 
940                      http_state[c1->state],
941                      c1->data_count);
942         c1 = c1->next;
943     }
944     q += sprintf(q, "</TABLE>\n");
945     
946     /* date */
947     ti = time(NULL);
948     p = ctime(&ti);
949     q += sprintf(q, "<HR size=1 noshade>Generated at %s", p);
950     q += sprintf(q, "</BODY>\n</HTML>\n");
951
952     c->buffer_ptr = c->buffer;
953     c->buffer_end = q;
954 }
955
956
957 static void http_write_packet(void *opaque, 
958                               unsigned char *buf, int size)
959 {
960     HTTPContext *c = opaque;
961
962     if (c->buffer_ptr == c->buffer_end || !c->buffer_ptr)
963         c->buffer_ptr = c->buffer_end = c->buffer;
964
965     if (c->buffer_end - c->buffer + size > IOBUFFER_MAX_SIZE)
966         av_abort();
967
968     memcpy(c->buffer_end, buf, size);
969     c->buffer_end += size;
970 }
971
972 static int open_input_stream(HTTPContext *c, const char *info)
973 {
974     char buf[128];
975     char input_filename[1024];
976     AVFormatContext *s;
977     int buf_size;
978     INT64 stream_pos;
979
980     /* find file name */
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;
990         } else {
991             stream_pos = gettime() - c->stream->prebuffer * 1000;
992         }
993     } else {
994         strcpy(input_filename, c->stream->feed_filename);
995         buf_size = 0;
996         /* compute position (relative time) */
997         if (find_info_tag(buf, sizeof(buf), "date", info)) {
998             stream_pos = parse_date(buf, 1);
999         } else {
1000             stream_pos = 0;
1001         }
1002     }
1003     if (input_filename[0] == '\0')
1004         return -1;
1005
1006     /* open stream */
1007     if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0)
1008         return -1;
1009     c->fmt_in = s;
1010
1011     if (c->fmt_in->iformat->read_seek) {
1012         c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
1013     }
1014     
1015     //    printf("stream %s opened pos=%0.6f\n", input_filename, stream_pos / 1000000.0);
1016     return 0;
1017 }
1018
1019 static int http_prepare_data(HTTPContext *c)
1020 {
1021     int i;
1022
1023     switch(c->state) {
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++) {
1031                 AVStream *st;
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));
1036                 else
1037                     memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]], sizeof(AVStream));
1038
1039                 st->codec.frame_number = 0; /* XXX: should be done in
1040                                                AVStream, not in codec */
1041             }
1042             c->got_key_frame = 0;
1043         } else {
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++) {
1048                 AVStream *st;
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 */
1054             }
1055             c->got_key_frame = 0;
1056         }
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;
1064         break;
1065     case HTTPSTATE_SEND_DATA:
1066         /* find a new packet */
1067 #if 0
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;
1074         }
1075         
1076         start_rptr = c->rptr;
1077         if (fifo_read(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &c->rptr) < 0)
1078             return 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 */
1083             av_free(payload);
1084             c->rptr = start_rptr;
1085             return 0;
1086         }
1087         
1088         c->last_http_fifo_write_count = http_fifo_write_count - 
1089             fifo_size(&http_fifo, c->rptr);
1090         
1091         if (c->stream->stream_type != STREAM_TYPE_MASTER) {
1092             /* test if the packet can be handled by this format */
1093             ret = 0;
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);
1103                     }
1104                     break;
1105                 }
1106             }
1107             if (ret) {
1108                 /* must send trailer now */
1109                 c->state = HTTPSTATE_SEND_DATA_TRAILER;
1110             }
1111         } else {
1112             /* master case : send everything */
1113             char *q;
1114             q = c->buffer;
1115             memcpy(q, &hdr, sizeof(hdr));
1116             q += sizeof(hdr);
1117             memcpy(q, payload, payload_size);
1118             q += payload_size;
1119             c->buffer_ptr = c->buffer;
1120             c->buffer_end = q;
1121         }
1122         av_free(payload);
1123 #endif
1124         {
1125             AVPacket pkt;
1126
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);
1132             }
1133
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 */
1144                 } else {
1145                     /* must send trailer now because eof or error */
1146                     c->state = HTTPSTATE_SEND_DATA_TRAILER;
1147                 }
1148             } else {
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;
1157                             }
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). 
1164                              */
1165                             if (!c->stream->send_on_key || ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
1166                                 goto send_it;
1167                             }
1168                         }
1169                     }
1170                 } else {
1171                     AVCodecContext *codec;
1172                 send_it:
1173                     /* Fudge here */
1174                     codec = &c->fmt_ctx.streams[pkt.stream_index]->codec;
1175
1176                     codec->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
1177
1178 #ifdef PJSG
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); */
1182                     }
1183 #endif
1184
1185                     if (av_write_packet(&c->fmt_ctx, &pkt, 0))
1186                         c->state = HTTPSTATE_SEND_DATA_TRAILER;
1187
1188                     codec->frame_number++;
1189                 }
1190
1191                 av_free_packet(&pkt);
1192             }
1193         }
1194         break;
1195     default:
1196     case HTTPSTATE_SEND_DATA_TRAILER:
1197         /* last packet test ? */
1198         if (c->last_packet_sent)
1199             return -1;
1200         /* prepare header */
1201         av_write_trailer(&c->fmt_ctx);
1202         c->last_packet_sent = 1;
1203         break;
1204     }
1205     return 0;
1206 }
1207
1208 /* should convert the format at the same time */
1209 static int http_send_data(HTTPContext *c)
1210 {
1211     int len, ret;
1212
1213     while (c->buffer_ptr >= c->buffer_end) {
1214         ret = http_prepare_data(c);
1215         if (ret < 0)
1216             return -1;
1217         else if (ret == 0) {
1218             continue;
1219         } else {
1220             /* state change requested */
1221             return 0;
1222         }
1223     }
1224
1225     if (c->buffer_end > c->buffer_ptr) {
1226         len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
1227         if (len < 0) {
1228             if (errno != EAGAIN && errno != EINTR) {
1229                 /* error : close connection */
1230                 return -1;
1231             }
1232         } else {
1233             c->buffer_ptr += len;
1234             c->data_count += len;
1235             if (c->stream)
1236                 c->stream->bytes_served += len;
1237         }
1238     }
1239     return 0;
1240 }
1241
1242 static int http_start_receive_data(HTTPContext *c)
1243 {
1244     int fd;
1245
1246     if (c->stream->feed_opened)
1247         return -1;
1248
1249     /* open feed */
1250     fd = open(c->stream->feed_filename, O_RDWR);
1251     if (fd < 0)
1252         return -1;
1253     c->feed_fd = fd;
1254     
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);
1258
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;
1263     return 0;
1264 }
1265     
1266 static int http_receive_data(HTTPContext *c)
1267 {
1268     HTTPContext *c1;
1269
1270     if (c->buffer_end > c->buffer_ptr) {
1271         int len;
1272
1273         len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
1274         if (len < 0) {
1275             if (errno != EAGAIN && errno != EINTR) {
1276                 /* error : close connection */
1277                 goto fail;
1278             }
1279         } else if (len == 0) {
1280             /* end of connection : close it */
1281             goto fail;
1282         } else {
1283             c->buffer_ptr += len;
1284             c->data_count += len;
1285         }
1286     }
1287
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
1291            if header */
1292         if (c->data_count > FFM_PACKET_SIZE) {
1293             
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);
1298             
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;
1303
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;
1307
1308             /* write index */
1309             ffm_write_write_index(c->feed_fd, feed->feed_write_index);
1310
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;
1316                 }
1317             }
1318         } else {
1319             /* We have a header in our hands that contains useful data */
1320             AVFormatContext s;
1321             AVInputFormat *fmt_in;
1322             ByteIOContext *pb = &s.pb;
1323             int i;
1324
1325             memset(&s, 0, sizeof(s));
1326
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;
1330
1331             /* use feed output format name to find corresponding input format */
1332             fmt_in = av_find_input_format(feed->fmt->name);
1333             if (!fmt_in)
1334                 goto fail;
1335
1336             s.priv_data = av_mallocz(fmt_in->priv_data_size);
1337             if (!s.priv_data)
1338                 goto fail;
1339
1340             if (fmt_in->read_header(&s, 0) < 0) {
1341                 av_freep(&s.priv_data);
1342                 goto fail;
1343             }
1344
1345             /* Now we have the actual streams */
1346             if (s.nb_streams != feed->nb_streams) {
1347                 av_freep(&s.priv_data);
1348                 goto fail;
1349             }
1350             for (i = 0; i < s.nb_streams; i++) {
1351                 memcpy(&feed->streams[i]->codec, 
1352                        &s.streams[i]->codec, sizeof(AVCodecContext));
1353             } 
1354             av_freep(&s.priv_data);
1355         }
1356         c->buffer_ptr = c->buffer;
1357     }
1358
1359     return 0;
1360  fail:
1361     c->stream->feed_opened = 0;
1362     close(c->feed_fd);
1363     return -1;
1364 }
1365
1366 /* return the stream number in the feed */
1367 int add_av_stream(FFStream *feed,
1368                   AVStream *st)
1369 {
1370     AVStream *fst;
1371     AVCodecContext *av, *av1;
1372     int i;
1373
1374     av = &st->codec;
1375     for(i=0;i<feed->nb_streams;i++) {
1376         st = feed->streams[i];
1377         av1 = &st->codec;
1378         if (av1->codec_id == av->codec_id &&
1379             av1->codec_type == av->codec_type &&
1380             av1->bit_rate == av->bit_rate) {
1381
1382             switch(av->codec_type) {
1383             case CODEC_TYPE_AUDIO:
1384                 if (av1->channels == av->channels &&
1385                     av1->sample_rate == av->sample_rate)
1386                     goto found;
1387                 break;
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)
1393                     goto found;
1394                 break;
1395             default:
1396                 av_abort();
1397             }
1398         }
1399     }
1400     
1401     fst = av_mallocz(sizeof(AVStream));
1402     if (!fst)
1403         return -1;
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;
1408  found:
1409     return i;
1410 }
1411
1412 /* compute the needed AVStream for each feed */
1413 void build_feed_streams(void)
1414 {
1415     FFStream *stream, *feed;
1416     int i;
1417
1418     /* gather all streams */
1419     for(stream = first_stream; stream != NULL; stream = stream->next) {
1420         feed = stream->feed;
1421         if (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]);
1425                 }
1426             } else {
1427                 for(i=0;i<stream->nb_streams;i++) {
1428                     stream->feed_streams[i] = i;
1429                 }
1430             }
1431         }
1432     }
1433
1434     /* create feed files if needed */
1435     for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
1436         int fd;
1437
1438         if (!url_exist(feed->feed_filename)) {
1439             AVFormatContext s1, *s = &s1;
1440
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);
1445                 exit(1);
1446             }
1447             s->oformat = feed->fmt;
1448             s->nb_streams = feed->nb_streams;
1449             for(i=0;i<s->nb_streams;i++) {
1450                 AVStream *st;
1451                 st = feed->streams[i];
1452                 s->streams[i] = st;
1453             }
1454             av_write_header(s);
1455             /* XXX: need better api */
1456             av_freep(&s->priv_data);
1457             url_fclose(&s->pb);
1458         }
1459         /* get feed size and write index */
1460         fd = open(feed->feed_filename, O_RDONLY);
1461         if (fd < 0) {
1462             fprintf(stderr, "Could not open output feed file '%s'\n",
1463                     feed->feed_filename);
1464             exit(1);
1465         }
1466
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;
1472
1473         close(fd);
1474     }
1475 }
1476
1477 static void get_arg(char *buf, int buf_size, const char **pp)
1478 {
1479     const char *p;
1480     char *q;
1481     int quote;
1482
1483     p = *pp;
1484     while (isspace(*p)) p++;
1485     q = buf;
1486     quote = 0;
1487     if (*p == '\"' || *p == '\'')
1488         quote = *p++;
1489     for(;;) {
1490         if (quote) {
1491             if (*p == quote)
1492                 break;
1493         } else {
1494             if (isspace(*p))
1495                 break;
1496         }
1497         if (*p == '\0')
1498             break;
1499         if ((q - buf) < buf_size - 1)
1500             *q++ = *p;
1501         p++;
1502     }
1503     *q = '\0';
1504     if (quote && *p == quote)
1505         p++;
1506     *pp = p;
1507 }
1508
1509 /* add a codec and set the default parameters */
1510 void add_codec(FFStream *stream, AVCodecContext *av)
1511 {
1512     AVStream *st;
1513
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)
1522             av->channels = 1;
1523         break;
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) {
1530             av->width = 160;
1531             av->height = 128;
1532         }
1533         /* Bitrate tolerance is less for streaming */
1534         if (av->bit_rate_tolerance == 0)
1535             av->bit_rate_tolerance = av->bit_rate / 4;
1536         if (av->qmin == 0)
1537             av->qmin = 3;
1538         if (av->qmax == 0)
1539             av->qmax = 31;
1540         if (av->max_qdiff == 0)
1541             av->max_qdiff = 3;
1542         av->qcompress = 0.5;
1543         av->qblur = 0.5;
1544
1545         break;
1546     default:
1547         av_abort();
1548     }
1549
1550     st = av_mallocz(sizeof(AVStream));
1551     if (!st)
1552         return;
1553     stream->streams[stream->nb_streams++] = st;
1554     memcpy(&st->codec, av, sizeof(AVCodecContext));
1555 }
1556
1557 int opt_audio_codec(const char *arg)
1558 {
1559     AVCodec *p;
1560
1561     p = first_avcodec;
1562     while (p) {
1563         if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
1564             break;
1565         p = p->next;
1566     }
1567     if (p == NULL) {
1568         return CODEC_ID_NONE;
1569     }
1570
1571     return p->id;
1572 }
1573
1574 int opt_video_codec(const char *arg)
1575 {
1576     AVCodec *p;
1577
1578     p = first_avcodec;
1579     while (p) {
1580         if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
1581             break;
1582         p = p->next;
1583     }
1584     if (p == NULL) {
1585         return CODEC_ID_NONE;
1586     }
1587
1588     return p->id;
1589 }
1590
1591 int parse_ffconfig(const char *filename)
1592 {
1593     FILE *f;
1594     char line[1024];
1595     char cmd[64];
1596     char arg[1024];
1597     const char *p;
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;
1603
1604     f = fopen(filename, "r");
1605     if (!f) {
1606         perror(filename);
1607         return -1;
1608     }
1609     
1610     errors = 0;
1611     line_num = 0;
1612     first_stream = NULL;
1613     last_stream = &first_stream;
1614     first_feed = NULL;
1615     last_feed = &first_feed;
1616     stream = NULL;
1617     feed = NULL;
1618     audio_id = CODEC_ID_NONE;
1619     video_id = CODEC_ID_NONE;
1620     for(;;) {
1621         if (fgets(line, sizeof(line), f) == NULL)
1622             break;
1623         line_num++;
1624         p = line;
1625         while (isspace(*p)) 
1626             p++;
1627         if (*p == '\0' || *p == '#')
1628             continue;
1629
1630         get_arg(cmd, sizeof(cmd), &p);
1631         
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);
1640                 errors++;
1641             }
1642         } else if (!strcasecmp(cmd, "MaxClients")) {
1643             get_arg(arg, sizeof(arg), &p);
1644             val = atoi(arg);
1645             if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
1646                 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n", 
1647                         filename, line_num, arg);
1648                 errors++;
1649             } else {
1650                 nb_max_connections = val;
1651             }
1652         } else if (!strcasecmp(cmd, "MaxBandwidth")) {
1653             get_arg(arg, sizeof(arg), &p);
1654             val = atoi(arg);
1655             if (val < 10 || val > 100000) {
1656                 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n", 
1657                         filename, line_num, arg);
1658                 errors++;
1659             } else {
1660                 nb_max_bandwidth = val;
1661             }
1662         } else if (!strcasecmp(cmd, "CustomLog")) {
1663             get_arg(logfilename, sizeof(logfilename), &p);
1664         } else if (!strcasecmp(cmd, "<Feed")) {
1665             /*********************************************/
1666             /* Feed related options */
1667             char *q;
1668             if (stream || feed) {
1669                 fprintf(stderr, "%s:%d: Already in a tag\n",
1670                         filename, line_num);
1671             } else {
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 */
1677                 *last_feed = feed;
1678                 last_feed = &feed->next_feed;
1679                 
1680                 get_arg(feed->filename, sizeof(feed->filename), &p);
1681                 q = strrchr(feed->filename, '>');
1682                 if (*q)
1683                     *q = '\0';
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;
1689                 feed->is_feed = 1;
1690                 feed->feed = feed; /* self feeding :-) */
1691             }
1692         } else if (!strcasecmp(cmd, "File")) {
1693             if (feed) {
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);
1697             }
1698         } else if (!strcasecmp(cmd, "FileMaxSize")) {
1699             if (feed) {
1700                 const char *p1;
1701                 double fsize;
1702
1703                 get_arg(arg, sizeof(arg), &p);
1704                 p1 = arg;
1705                 fsize = strtod(p1, (char **)&p1);
1706                 switch(toupper(*p1)) {
1707                 case 'K':
1708                     fsize *= 1024;
1709                     break;
1710                 case 'M':
1711                     fsize *= 1024 * 1024;
1712                     break;
1713                 case 'G':
1714                     fsize *= 1024 * 1024 * 1024;
1715                     break;
1716                 }
1717                 feed->feed_max_size = (INT64)fsize;
1718             }
1719         } else if (!strcasecmp(cmd, "</Feed>")) {
1720             if (!feed) {
1721                 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
1722                         filename, line_num);
1723                 errors++;
1724             } else {
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));
1730                     errors++;
1731                 }
1732             }
1733             feed = NULL;
1734         } else if (!strcasecmp(cmd, "<Stream")) {
1735             /*********************************************/
1736             /* Stream related options */
1737             char *q;
1738             if (stream || feed) {
1739                 fprintf(stderr, "%s:%d: Already in a tag\n",
1740                         filename, line_num);
1741             } else {
1742                 stream = av_mallocz(sizeof(FFStream));
1743                 *last_stream = stream;
1744                 last_stream = &stream->next;
1745
1746                 get_arg(stream->filename, sizeof(stream->filename), &p);
1747                 q = strrchr(stream->filename, '>');
1748                 if (*q)
1749                     *q = '\0';
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;
1755                 if (stream->fmt) {
1756                     audio_id = stream->fmt->audio_codec;
1757                     video_id = stream->fmt->video_codec;
1758                 }
1759             }
1760         } else if (!strcasecmp(cmd, "Feed")) {
1761             get_arg(arg, sizeof(arg), &p);
1762             if (stream) {
1763                 FFStream *sfeed;
1764                 
1765                 sfeed = first_feed;
1766                 while (sfeed != NULL) {
1767                     if (!strcmp(sfeed->filename, arg))
1768                         break;
1769                     sfeed = sfeed->next_feed;
1770                 }
1771                 if (!sfeed) {
1772                     fprintf(stderr, "%s:%d: feed '%s' not defined\n",
1773                             filename, line_num, arg);
1774                 } else {
1775                     stream->feed = sfeed;
1776                 }
1777             }
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;
1782                 stream->fmt = NULL;
1783             } else {
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);
1789                 if (!stream->fmt) {
1790                     fprintf(stderr, "%s:%d: Unknown Format: %s\n", 
1791                             filename, line_num, arg);
1792                     errors++;
1793                 }
1794             }
1795             if (stream->fmt) {
1796                 audio_id = stream->fmt->audio_codec;
1797                 video_id = stream->fmt->video_codec;
1798             }
1799         } else if (!strcasecmp(cmd, "Preroll")) {
1800             get_arg(arg, sizeof(arg), &p);
1801             if (stream) {
1802                 stream->prebuffer = atoi(arg) * 1000;
1803             }
1804         } else if (!strcasecmp(cmd, "StartSendOnKey")) {
1805             if (stream) {
1806                 stream->send_on_key = 1;
1807             }
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);
1814                 errors++;
1815             }
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);
1822                 errors++;
1823             }
1824         } else if (!strcasecmp(cmd, "MaxTime")) {
1825             get_arg(arg, sizeof(arg), &p);
1826             if (stream) {
1827                 stream->max_time = atoi(arg);
1828             }
1829         } else if (!strcasecmp(cmd, "AudioBitRate")) {
1830             get_arg(arg, sizeof(arg), &p);
1831             if (stream) {
1832                 audio_enc.bit_rate = atoi(arg) * 1000;
1833             }
1834         } else if (!strcasecmp(cmd, "AudioChannels")) {
1835             get_arg(arg, sizeof(arg), &p);
1836             if (stream) {
1837                 audio_enc.channels = atoi(arg);
1838             }
1839         } else if (!strcasecmp(cmd, "AudioSampleRate")) {
1840             get_arg(arg, sizeof(arg), &p);
1841             if (stream) {
1842                 audio_enc.sample_rate = atoi(arg);
1843             }
1844         } else if (!strcasecmp(cmd, "VideoBitRate")) {
1845             get_arg(arg, sizeof(arg), &p);
1846             if (stream) {
1847                 video_enc.bit_rate = atoi(arg) * 1000;
1848             }
1849         } else if (!strcasecmp(cmd, "VideoSize")) {
1850             get_arg(arg, sizeof(arg), &p);
1851             if (stream) {
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);
1857                     errors++;
1858                 }
1859             }
1860         } else if (!strcasecmp(cmd, "VideoFrameRate")) {
1861             get_arg(arg, sizeof(arg), &p);
1862             if (stream) {
1863                 video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
1864             }
1865         } else if (!strcasecmp(cmd, "VideoGopSize")) {
1866             get_arg(arg, sizeof(arg), &p);
1867             if (stream) {
1868                 video_enc.gop_size = atoi(arg);
1869             }
1870         } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
1871             if (stream) {
1872                 video_enc.gop_size = 1;
1873             }
1874         } else if (!strcasecmp(cmd, "VideoHighQuality")) {
1875             if (stream) {
1876                 video_enc.flags |= CODEC_FLAG_HQ;
1877             }
1878         } else if (!strcasecmp(cmd, "VideoQDiff")) {
1879             if (stream) {
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);
1884                     errors++;
1885                 }
1886             }
1887         } else if (!strcasecmp(cmd, "VideoQMax")) {
1888             if (stream) {
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);
1893                     errors++;
1894                 }
1895             }
1896         } else if (!strcasecmp(cmd, "VideoQMin")) {
1897             if (stream) {
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);
1902                     errors++;
1903                 }
1904             }
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>")) {
1910             if (!stream) {
1911                 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
1912                         filename, line_num);
1913                 errors++;
1914             }
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);
1920                 }
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);
1925                 }
1926             }
1927             stream = NULL;
1928         } else {
1929             fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n", 
1930                     filename, line_num, cmd);
1931             errors++;
1932         }
1933     }
1934
1935     fclose(f);
1936     if (errors)
1937         return -1;
1938     else
1939         return 0;
1940 }
1941
1942
1943 void *http_server_thread(void *arg)
1944 {
1945     http_server(my_addr);
1946     return NULL;
1947 }
1948
1949 #if 0
1950 static void write_packet(FFCodec *ffenc,
1951                          UINT8 *buf, int size)
1952 {
1953     PacketHeader hdr;
1954     AVCodecContext *enc = &ffenc->enc;
1955     UINT8 *wptr;
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);
1964 }
1965 #endif
1966
1967 void help(void)
1968 {
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"
1972            "\n"
1973            "-L            : print the LICENCE\n"
1974            "-h            : this help\n"
1975            "-f configfile : use configfile instead of /etc/ffserver.conf\n"
1976            );
1977 }
1978
1979 void licence(void)
1980 {
1981     printf(
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"
1988     "\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"
1993     "\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"
1997     );
1998 }
1999
2000 int main(int argc, char **argv)
2001 {
2002     const char *config_filename;
2003     int c;
2004
2005     register_all();
2006
2007     config_filename = "/etc/ffserver.conf";
2008
2009     for(;;) {
2010         c = getopt_long_only(argc, argv, "Lh?f:", NULL, NULL);
2011         if (c == -1)
2012             break;
2013         switch(c) {
2014         case 'L':
2015             licence();
2016             exit(1);
2017         case '?':
2018         case 'h':
2019             help();
2020             exit(1);
2021         case 'f':
2022             config_filename = optarg;
2023             break;
2024         default:
2025             exit(2);
2026         }
2027     }
2028
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';
2037
2038     if (parse_ffconfig(config_filename) < 0) {
2039         fprintf(stderr, "Incorrect config file - exiting.\n");
2040         exit(1);
2041     }
2042
2043     build_feed_streams();
2044
2045     /* signal init */
2046     signal(SIGPIPE, SIG_IGN);
2047
2048     /* open log file if needed */
2049     if (logfilename[0] != '\0') {
2050         if (!strcmp(logfilename, "-"))
2051             logfile = stdout;
2052         else
2053             logfile = fopen(logfilename, "w");
2054     }
2055
2056     if (http_server(my_addr) < 0) {
2057         fprintf(stderr, "Could not start http server\n");
2058         exit(1);
2059     }
2060
2061     return 0;
2062 }