jpeg hack
[ffmpeg.git] / ffserver.c
1 /*
2  * Multiple format streaming server
3  * Copyright (c) 2000,2001 Gerard Lantau.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program 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
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 #include <stdarg.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.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 #include "avformat.h"
40
41 /* maximum number of simultaneous HTTP connections */
42 #define HTTP_MAX_CONNECTIONS 2000
43
44 enum HTTPState {
45     HTTPSTATE_WAIT_REQUEST,
46     HTTPSTATE_SEND_HEADER,
47     HTTPSTATE_SEND_DATA_HEADER,
48     HTTPSTATE_SEND_DATA,
49     HTTPSTATE_SEND_DATA_TRAILER,
50     HTTPSTATE_RECEIVE_DATA,
51     HTTPSTATE_WAIT_FEED,
52 };
53
54 const char *http_state[] = {
55     "WAIT_REQUEST",
56     "SEND_HEADER",
57     "SEND_DATA_HEADER",
58     "SEND_DATA",
59     "SEND_DATA_TRAILER",
60     "RECEIVE_DATA",
61     "WAIT_FEED",
62 };
63
64 #define IOBUFFER_MAX_SIZE 16384
65
66 /* coef for exponential mean for bitrate estimation in statistics */
67 #define AVG_COEF 0.9
68
69 /* timeouts are in ms */
70 #define REQUEST_TIMEOUT (15 * 1000)
71 #define SYNC_TIMEOUT (10 * 1000)
72
73 /* context associated with one connection */
74 typedef struct HTTPContext {
75     enum HTTPState state;
76     int fd; /* socket file descriptor */
77     struct sockaddr_in from_addr; /* origin */
78     struct pollfd *poll_entry; /* used when polling */
79     long timeout;
80     UINT8 buffer[IOBUFFER_MAX_SIZE];
81     UINT8 *buffer_ptr, *buffer_end;
82     int http_error;
83     struct HTTPContext *next;
84     int got_key_frame[MAX_STREAMS]; /* for each type */
85     INT64 data_count;
86     /* feed input */
87     int feed_fd;
88     /* input format handling */
89     AVFormatContext *fmt_in;
90     /* output format handling */
91     struct FFStream *stream;
92     AVFormatContext fmt_ctx;
93     int last_packet_sent; /* true if last data packet was sent */
94 } HTTPContext;
95
96 /* each generated stream is described here */
97 enum StreamType {
98     STREAM_TYPE_LIVE,
99     STREAM_TYPE_STATUS,
100 };
101
102 /* description of each stream of the ffserver.conf file */
103 typedef struct FFStream {
104     enum StreamType stream_type;
105     char filename[1024];     /* stream filename */
106     struct FFStream *feed;
107     AVFormat *fmt;
108     int nb_streams;
109     AVStream *streams[MAX_STREAMS];
110     int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
111     char feed_filename[1024]; /* file name of the feed storage, or
112                                  input file name for a stream */
113     struct FFStream *next;
114     /* feed specific */
115     int feed_opened;     /* true if someone if writing to feed */
116     int is_feed;         /* true if it is a feed */
117     INT64 feed_max_size;      /* maximum storage size */
118     INT64 feed_write_index;   /* current write position in feed (it wraps round) */
119     INT64 feed_size;          /* current size of feed */
120     struct FFStream *next_feed;
121 } FFStream;
122
123 typedef struct FeedData {
124     long long data_count;
125     float avg_frame_size;   /* frame size averraged over last frames with exponential mean */
126 } FeedData;
127
128 struct sockaddr_in my_addr;
129 char logfilename[1024];
130 HTTPContext *first_http_ctx;
131 FFStream *first_feed;   /* contains only feeds */
132 FFStream *first_stream; /* contains all streams, including feeds */
133
134 static int handle_http(HTTPContext *c, long cur_time);
135 static int http_parse_request(HTTPContext *c);
136 static int http_send_data(HTTPContext *c);
137 static void compute_stats(HTTPContext *c);
138 static int open_input_stream(HTTPContext *c, const char *info);
139 static int http_start_receive_data(HTTPContext *c);
140 static int http_receive_data(HTTPContext *c);
141
142 int nb_max_connections;
143 int nb_connections;
144
145 static long gettime_ms(void)
146 {
147     struct timeval tv;
148
149     gettimeofday(&tv,NULL);
150     return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
151 }
152
153 static FILE *logfile = NULL;
154
155 static void http_log(char *fmt, ...)
156 {
157     va_list ap;
158     va_start(ap, fmt);
159     
160     if (logfile)
161         vfprintf(logfile, fmt, ap);
162     va_end(ap);
163 }
164
165 /* main loop of the http server */
166 static int http_server(struct sockaddr_in my_addr)
167 {
168     int server_fd, tmp, ret;
169     struct sockaddr_in from_addr;
170     struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 1], *poll_entry;
171     HTTPContext *c, **cp;
172     long cur_time;
173
174     server_fd = socket(AF_INET,SOCK_STREAM,0);
175     if (server_fd < 0) {
176         perror ("socket");
177         return -1;
178     }
179         
180     tmp = 1;
181     setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
182
183     if (bind (server_fd, (struct sockaddr *) &my_addr, sizeof (my_addr)) < 0) {
184         perror ("bind");
185         close(server_fd);
186         return -1;
187     }
188   
189     if (listen (server_fd, 5) < 0) {
190         perror ("listen");
191         close(server_fd);
192         return -1;
193     }
194
195     http_log("ffserver started.\n");
196
197     fcntl(server_fd, F_SETFL, O_NONBLOCK);
198     first_http_ctx = NULL;
199     nb_connections = 0;
200     first_http_ctx = NULL;
201     for(;;) {
202         poll_entry = poll_table;
203         poll_entry->fd = server_fd;
204         poll_entry->events = POLLIN;
205         poll_entry++;
206
207         /* wait for events on each HTTP handle */
208         c = first_http_ctx;
209         while (c != NULL) {
210             int fd;
211             fd = c->fd;
212             switch(c->state) {
213             case HTTPSTATE_WAIT_REQUEST:
214                 c->poll_entry = poll_entry;
215                 poll_entry->fd = fd;
216                 poll_entry->events = POLLIN;
217                 poll_entry++;
218                 break;
219             case HTTPSTATE_SEND_HEADER:
220             case HTTPSTATE_SEND_DATA_HEADER:
221             case HTTPSTATE_SEND_DATA:
222             case HTTPSTATE_SEND_DATA_TRAILER:
223                 c->poll_entry = poll_entry;
224                 poll_entry->fd = fd;
225                 poll_entry->events = POLLOUT;
226                 poll_entry++;
227                 break;
228             case HTTPSTATE_RECEIVE_DATA:
229                 c->poll_entry = poll_entry;
230                 poll_entry->fd = fd;
231                 poll_entry->events = POLLIN;
232                 poll_entry++;
233                 break;
234             case HTTPSTATE_WAIT_FEED:
235                 /* need to catch errors */
236                 c->poll_entry = poll_entry;
237                 poll_entry->fd = fd;
238                 poll_entry->events = 0;
239                 poll_entry++;
240                 break;
241             default:
242                 c->poll_entry = NULL;
243                 break;
244             }
245             c = c->next;
246         }
247
248         /* wait for an event on one connection. We poll at least every
249            second to handle timeouts */
250         do {
251             ret = poll(poll_table, poll_entry - poll_table, 1000);
252         } while (ret == -1);
253         
254         cur_time = gettime_ms();
255
256         /* now handle the events */
257
258         cp = &first_http_ctx;
259         while ((*cp) != NULL) {
260             c = *cp;
261             if (handle_http (c, cur_time) < 0) {
262                 /* close and free the connection */
263                 close(c->fd);
264                 if (c->fmt_in)
265                     av_close_input_file(c->fmt_in);
266                 *cp = c->next;
267                 free(c);
268                 nb_connections--;
269             } else {
270                 cp = &c->next;
271             }
272         }
273
274         /* new connection request ? */
275         poll_entry = poll_table;
276         if (poll_entry->revents & POLLIN) {
277             int fd, len;
278
279             len = sizeof(from_addr);
280             fd = accept(server_fd, (struct sockaddr *)&from_addr, 
281                         &len);
282             if (fd >= 0) {
283                 fcntl(fd, F_SETFL, O_NONBLOCK);
284                 /* XXX: should output a warning page when coming
285                    close to the connection limit */
286                 if (nb_connections >= nb_max_connections) {
287                     close(fd);
288                 } else {
289                     /* add a new connection */
290                     c = av_mallocz(sizeof(HTTPContext));
291                     c->next = first_http_ctx;
292                     first_http_ctx = c;
293                     c->fd = fd;
294                     c->poll_entry = NULL;
295                     c->from_addr = from_addr;
296                     c->state = HTTPSTATE_WAIT_REQUEST;
297                     c->buffer_ptr = c->buffer;
298                     c->buffer_end = c->buffer + IOBUFFER_MAX_SIZE;
299                     c->timeout = cur_time + REQUEST_TIMEOUT;
300                     nb_connections++;
301                 }
302             }
303         }
304         poll_entry++;
305     }
306 }
307
308 static int handle_http(HTTPContext *c, long cur_time)
309 {
310     int len;
311     
312     switch(c->state) {
313     case HTTPSTATE_WAIT_REQUEST:
314         /* timeout ? */
315         if ((c->timeout - cur_time) < 0)
316             return -1;
317         if (c->poll_entry->revents & (POLLERR | POLLHUP))
318             return -1;
319
320         /* no need to read if no events */
321         if (!(c->poll_entry->revents & POLLIN))
322             return 0;
323         /* read the data */
324         len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
325         if (len < 0) {
326             if (errno != EAGAIN && errno != EINTR)
327                 return -1;
328         } else if (len == 0) {
329             return -1;
330         } else {
331             /* search for end of request. XXX: not fully correct since garbage could come after the end */
332             UINT8 *ptr;
333             c->buffer_ptr += len;
334             ptr = c->buffer_ptr;
335             if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
336                 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
337                 /* request found : parse it and reply */
338                 if (http_parse_request(c) < 0)
339                     return -1;
340             } else if (ptr >= c->buffer_end) {
341                 /* request too long: cannot do anything */
342                 return -1;
343             }
344         }
345         break;
346
347     case HTTPSTATE_SEND_HEADER:
348         if (c->poll_entry->revents & (POLLERR | POLLHUP))
349             return -1;
350
351         /* no need to read if no events */
352         if (!(c->poll_entry->revents & POLLOUT))
353             return 0;
354         len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
355         if (len < 0) {
356             if (errno != EAGAIN && errno != EINTR) {
357                 /* error : close connection */
358                 return -1;
359             }
360         } else {
361             c->buffer_ptr += len;
362             if (c->buffer_ptr >= c->buffer_end) {
363                 /* if error, exit */
364                 if (c->http_error)
365                     return -1;
366                 /* all the buffer was send : synchronize to the incoming stream */
367                 c->state = HTTPSTATE_SEND_DATA_HEADER;
368                 c->buffer_ptr = c->buffer_end = c->buffer;
369             }
370         }
371         break;
372
373     case HTTPSTATE_SEND_DATA:
374     case HTTPSTATE_SEND_DATA_HEADER:
375     case HTTPSTATE_SEND_DATA_TRAILER:
376         /* no need to read if no events */
377         if (c->poll_entry->revents & (POLLERR | POLLHUP))
378             return -1;
379         
380         if (!(c->poll_entry->revents & POLLOUT))
381             return 0;
382         if (http_send_data(c) < 0)
383             return -1;
384         break;
385     case HTTPSTATE_RECEIVE_DATA:
386         /* no need to read if no events */
387         if (c->poll_entry->revents & (POLLERR | POLLHUP))
388             return -1;
389         if (!(c->poll_entry->revents & POLLIN))
390             return 0;
391         if (http_receive_data(c) < 0)
392             return -1;
393         break;
394     case HTTPSTATE_WAIT_FEED:
395         /* no need to read if no events */
396         if (c->poll_entry->revents & (POLLERR | POLLHUP))
397             return -1;
398
399         /* nothing to do, we'll be waken up by incoming feed packets */
400         break;
401     default:
402         return -1;
403     }
404     return 0;
405 }
406
407 /* parse http request and prepare header */
408 static int http_parse_request(HTTPContext *c)
409 {
410     char *p;
411     int post;
412     char cmd[32];
413     char info[1024], *filename;
414     char url[1024], *q;
415     char protocol[32];
416     char msg[1024];
417     const char *mime_type;
418     FFStream *stream;
419
420     p = c->buffer;
421     q = cmd;
422     while (!isspace(*p) && *p != '\0') {
423         if ((q - cmd) < sizeof(cmd) - 1)
424             *q++ = *p;
425         p++;
426     }
427     *q = '\0';
428     if (!strcmp(cmd, "GET"))
429         post = 0;
430     else if (!strcmp(cmd, "POST"))
431         post = 1;
432     else
433         return -1;
434
435     while (isspace(*p)) p++;
436     q = url;
437     while (!isspace(*p) && *p != '\0') {
438         if ((q - url) < sizeof(url) - 1)
439             *q++ = *p;
440         p++;
441     }
442     *q = '\0';
443
444     while (isspace(*p)) p++;
445     q = protocol;
446     while (!isspace(*p) && *p != '\0') {
447         if ((q - protocol) < sizeof(protocol) - 1)
448             *q++ = *p;
449         p++;
450     }
451     *q = '\0';
452     if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
453         return -1;
454     
455     /* find the filename and the optional info string in the request */
456     p = url;
457     if (*p == '/')
458         p++;
459     filename = p;
460     p = strchr(p, '?');
461     if (p) {
462         strcpy(info, p);
463         *p = '\0';
464     } else {
465         info[0] = '\0';
466     }
467
468     stream = first_stream;
469     while (stream != NULL) {
470         if (!strcmp(stream->filename, filename))
471             break;
472         stream = stream->next;
473     }
474     if (stream == NULL) {
475         sprintf(msg, "File '%s' not found", url);
476         goto send_error;
477     }
478     c->stream = stream;
479     
480     /* should do it after so that the size can be computed */
481     {
482         char buf1[32], buf2[32], *p;
483         time_t ti;
484         /* XXX: reentrant function ? */
485         p = inet_ntoa(c->from_addr.sin_addr);
486         strcpy(buf1, p);
487         ti = time(NULL);
488         p = ctime(&ti);
489         strcpy(buf2, p);
490         p = buf2 + strlen(p) - 1;
491         if (*p == '\n')
492             *p = '\0';
493         http_log("%s - - [%s] \"%s %s %s\" %d %d\n", 
494                  buf1, buf2, cmd, url, protocol, 200, 1024);
495     }
496
497     /* XXX: add there authenticate and IP match */
498
499     if (post) {
500         /* if post, it means a feed is being sent */
501         if (!stream->is_feed) {
502             sprintf(msg, "POST command not handled");
503             goto send_error;
504         }
505         if (http_start_receive_data(c) < 0) {
506             sprintf(msg, "could not open feed");
507             goto send_error;
508         }
509         c->http_error = 0;
510         c->state = HTTPSTATE_RECEIVE_DATA;
511         return 0;
512     }
513
514     if (c->stream->stream_type == STREAM_TYPE_STATUS)
515         goto send_stats;
516
517     /* open input stream */
518     if (open_input_stream(c, info) < 0) {
519         sprintf(msg, "Input stream corresponding to '%s' not found", url);
520         goto send_error;
521     }
522
523     /* prepare http header */
524     q = c->buffer;
525     q += sprintf(q, "HTTP/1.0 200 OK\r\n");
526     mime_type = c->stream->fmt->mime_type;
527     if (!mime_type)
528         mime_type = "application/x-octet_stream";
529     q += sprintf(q, "Content-type: %s\r\n", mime_type);
530     q += sprintf(q, "Pragma: no-cache\r\n");
531
532     /* for asf, we need extra headers */
533     if (!strcmp(c->stream->fmt->name,"asf")) {
534         q += sprintf(q, "Pragma: features=broadcast\r\n");
535     }
536     q += sprintf(q, "\r\n");
537     
538     /* prepare output buffer */
539     c->http_error = 0;
540     c->buffer_ptr = c->buffer;
541     c->buffer_end = q;
542     c->state = HTTPSTATE_SEND_HEADER;
543     return 0;
544  send_error:
545     c->http_error = 404;
546     q = c->buffer;
547     q += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
548     q += sprintf(q, "Content-type: %s\r\n", "text/html");
549     q += sprintf(q, "\r\n");
550     q += sprintf(q, "<HTML>\n");
551     q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
552     q += sprintf(q, "<BODY>%s</BODY>\n", msg);
553     q += sprintf(q, "</HTML>\n");
554
555     /* prepare output buffer */
556     c->buffer_ptr = c->buffer;
557     c->buffer_end = q;
558     c->state = HTTPSTATE_SEND_HEADER;
559     return 0;
560  send_stats:
561     compute_stats(c);
562     c->http_error = 200; /* horrible : we use this value to avoid
563                             going to the send data state */
564     c->state = HTTPSTATE_SEND_HEADER;
565     return 0;
566 }
567
568 static void compute_stats(HTTPContext *c)
569 {
570     HTTPContext *c1;
571     FFStream *stream;
572     char *q, *p;
573     time_t ti;
574     int i;
575
576     q = c->buffer;
577     q += sprintf(q, "HTTP/1.0 200 OK\r\n");
578     q += sprintf(q, "Content-type: %s\r\n", "text/html");
579     q += sprintf(q, "Pragma: no-cache\r\n");
580     q += sprintf(q, "\r\n");
581     
582     q += sprintf(q, "<HEAD><TITLE>FFServer Status</TITLE></HEAD>\n<BODY>");
583     q += sprintf(q, "<H1>FFServer Status</H1>\n");
584     /* format status */
585     q += sprintf(q, "<H1>Available Streams</H1>\n");
586     q += sprintf(q, "<TABLE>\n");
587     q += sprintf(q, "<TR><TD>Path<TD>Format<TD>Bit rate (kbits/s)<TD>Video<TD>Audio<TD>Feed\n");
588     stream = first_stream;
589     while (stream != NULL) {
590         q += sprintf(q, "<TR><TD><A HREF=\"/%s\">%s</A> ", 
591                      stream->filename, stream->filename);
592         switch(stream->stream_type) {
593         case STREAM_TYPE_LIVE:
594             {
595                 int audio_bit_rate = 0;
596                 int video_bit_rate = 0;
597
598                 for(i=0;i<stream->nb_streams;i++) {
599                     AVStream *st = stream->streams[i];
600                     switch(st->codec.codec_type) {
601                     case CODEC_TYPE_AUDIO:
602                         audio_bit_rate += st->codec.bit_rate;
603                         break;
604                     case CODEC_TYPE_VIDEO:
605                         video_bit_rate += st->codec.bit_rate;
606                         break;
607                     }
608                 }
609                 q += sprintf(q, "<TD> %s <TD> %d <TD> %d <TD> %d", 
610                              stream->fmt->name,
611                              (audio_bit_rate + video_bit_rate) / 1000,
612                              video_bit_rate / 1000, audio_bit_rate / 1000);
613                 if (stream->feed) {
614                     q += sprintf(q, "<TD>%s", stream->feed->filename);
615                 } else {
616                     q += sprintf(q, "<TD>%s", stream->feed_filename);
617                 }
618                 q += sprintf(q, "\n");
619             }
620             break;
621         default:
622             q += sprintf(q, "<TD> - <TD> - <TD> - <TD> -\n");
623             break;
624         }
625         stream = stream->next;
626     }
627     q += sprintf(q, "</TABLE>\n");
628     
629 #if 0
630     {
631         float avg;
632         AVCodecContext *enc;
633         char buf[1024];
634         
635         /* feed status */
636         stream = first_feed;
637         while (stream != NULL) {
638             q += sprintf(q, "<H1>Feed '%s'</H1>\n", stream->filename);
639             q += sprintf(q, "<TABLE>\n");
640             q += sprintf(q, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
641             for(i=0;i<stream->nb_streams;i++) {
642                 AVStream *st = stream->streams[i];
643                 FeedData *fdata = st->priv_data;
644                 enc = &st->codec;
645             
646                 avcodec_string(buf, sizeof(buf), enc);
647                 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
648                 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
649                     avg /= enc->frame_size;
650                 q += sprintf(q, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n", 
651                              buf, enc->frame_number, fdata->data_count, avg / 1000.0);
652             }
653             q += sprintf(q, "</TABLE>\n");
654             stream = stream->next_feed;
655         }
656     }
657 #endif
658
659     /* connection status */
660     q += sprintf(q, "<H1>Connection Status</H1>\n");
661
662     q += sprintf(q, "Number of connections: %d / %d<BR>\n",
663                  nb_connections, nb_max_connections);
664
665     q += sprintf(q, "<TABLE>\n");
666     q += sprintf(q, "<TR><TD>#<TD>File<TD>IP<TD>State<TD>Size\n");
667     c1 = first_http_ctx;
668     i = 0;
669     while (c1 != NULL) {
670         i++;
671         p = inet_ntoa(c1->from_addr.sin_addr);
672         q += sprintf(q, "<TR><TD><B>%d</B><TD>%s%s <TD> %s <TD> %s <TD> %Ld\n", 
673                      i, c1->stream->filename, 
674                      c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
675                      p, 
676                      http_state[c1->state],
677                      c1->data_count);
678         c1 = c1->next;
679     }
680     q += sprintf(q, "</TABLE>\n");
681     
682     /* date */
683     ti = time(NULL);
684     p = ctime(&ti);
685     q += sprintf(q, "<HR>Generated at %s", p);
686     q += sprintf(q, "</BODY>\n</HTML>\n");
687
688     c->buffer_ptr = c->buffer;
689     c->buffer_end = q;
690 }
691
692
693 static void http_write_packet(void *opaque, 
694                               unsigned char *buf, int size)
695 {
696     HTTPContext *c = opaque;
697     if (size > IOBUFFER_MAX_SIZE)
698         abort();
699     memcpy(c->buffer, buf, size);
700     c->buffer_ptr = c->buffer;
701     c->buffer_end = c->buffer + size;
702 }
703
704 static int open_input_stream(HTTPContext *c, const char *info)
705 {
706     char buf[128];
707     char input_filename[1024];
708     AVFormatContext *s;
709     int buf_size;
710     INT64 stream_pos;
711
712     /* find file name */
713     if (c->stream->feed) {
714         strcpy(input_filename, c->stream->feed->feed_filename);
715         buf_size = FFM_PACKET_SIZE;
716         /* compute position (absolute time) */
717         if (find_info_tag(buf, sizeof(buf), "date", info)) {
718             stream_pos = parse_date(buf, 0);
719         } else {
720             stream_pos = gettime();
721         }
722     } else {
723         strcpy(input_filename, c->stream->feed_filename);
724         buf_size = 0;
725         /* compute position (relative time) */
726         if (find_info_tag(buf, sizeof(buf), "date", info)) {
727             stream_pos = parse_date(buf, 1);
728         } else {
729             stream_pos = 0;
730         }
731     }
732     if (input_filename[0] == '\0')
733         return -1;
734
735     /* open stream */
736     s = av_open_input_file(input_filename, buf_size);
737     if (!s)
738         return -1;
739     c->fmt_in = s;
740
741     if (c->fmt_in->format->read_seek) {
742         c->fmt_in->format->read_seek(c->fmt_in, stream_pos);
743     }
744     
745     //    printf("stream %s opened pos=%0.6f\n", input_filename, stream_pos / 1000000.0);
746     return 0;
747 }
748
749 static int http_prepare_data(HTTPContext *c)
750 {
751     int i;
752
753     switch(c->state) {
754     case HTTPSTATE_SEND_DATA_HEADER:
755         memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
756         if (c->stream->feed) {
757             /* open output stream by using specified codecs */
758             c->fmt_ctx.format = c->stream->fmt;
759             c->fmt_ctx.nb_streams = c->stream->nb_streams;
760             for(i=0;i<c->fmt_ctx.nb_streams;i++) {
761                 AVStream *st;
762                 st = av_mallocz(sizeof(AVStream));
763                 c->fmt_ctx.streams[i] = st;
764                 memcpy(st, c->stream->streams[i], sizeof(AVStream));
765                 st->codec.frame_number = 0; /* XXX: should be done in
766                                                AVStream, not in codec */
767                 c->got_key_frame[i] = 0;
768             }
769         } else {
770             /* open output stream by using codecs in specified file */
771             c->fmt_ctx.format = c->stream->fmt;
772             c->fmt_ctx.nb_streams = c->fmt_in->nb_streams;
773             for(i=0;i<c->fmt_ctx.nb_streams;i++) {
774                 AVStream *st;
775                 st = av_mallocz(sizeof(AVStream));
776                 c->fmt_ctx.streams[i] = st;
777                 memcpy(st, c->fmt_in->streams[i], sizeof(AVStream));
778                 st->codec.frame_number = 0; /* XXX: should be done in
779                                                AVStream, not in codec */
780                 c->got_key_frame[i] = 0;
781             }
782         }
783         init_put_byte(&c->fmt_ctx.pb, c->buffer, IOBUFFER_MAX_SIZE,
784                       1, c, NULL, http_write_packet, NULL);
785         c->fmt_ctx.pb.is_streamed = 1;
786         /* prepare header */
787         c->fmt_ctx.format->write_header(&c->fmt_ctx);
788         c->state = HTTPSTATE_SEND_DATA;
789         c->last_packet_sent = 0;
790         break;
791     case HTTPSTATE_SEND_DATA:
792         /* find a new packet */
793 #if 0
794         fifo_total_size = http_fifo_write_count - c->last_http_fifo_write_count;
795         if (fifo_total_size >= ((3 * FIFO_MAX_SIZE) / 4)) {
796             /* overflow : resync. We suppose that wptr is at this
797                point a pointer to a valid packet */
798             c->rptr = http_fifo.wptr;
799             for(i=0;i<c->fmt_ctx.nb_streams;i++) {
800                 c->got_key_frame[i] = 0;
801             }
802         }
803         
804         start_rptr = c->rptr;
805         if (fifo_read(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &c->rptr) < 0)
806             return 0;
807         payload_size = ntohs(hdr.payload_size);
808         payload = malloc(payload_size);
809         if (fifo_read(&http_fifo, payload, payload_size, &c->rptr) < 0) {
810             /* cannot read all the payload */
811             free(payload);
812             c->rptr = start_rptr;
813             return 0;
814         }
815         
816         c->last_http_fifo_write_count = http_fifo_write_count - 
817             fifo_size(&http_fifo, c->rptr);
818         
819         if (c->stream->stream_type != STREAM_TYPE_MASTER) {
820             /* test if the packet can be handled by this format */
821             ret = 0;
822             for(i=0;i<c->fmt_ctx.nb_streams;i++) {
823                 AVStream *st = c->fmt_ctx.streams[i];
824                 if (test_header(&hdr, &st->codec)) {
825                     /* only begin sending when got a key frame */
826                     if (st->codec.key_frame)
827                         c->got_key_frame[i] = 1;
828                     if (c->got_key_frame[i]) {
829                         ret = c->fmt_ctx.format->write_packet(&c->fmt_ctx, i,
830                                                                    payload, payload_size);
831                     }
832                     break;
833                 }
834             }
835             if (ret) {
836                 /* must send trailer now */
837                 c->state = HTTPSTATE_SEND_DATA_TRAILER;
838             }
839         } else {
840             /* master case : send everything */
841             char *q;
842             q = c->buffer;
843             memcpy(q, &hdr, sizeof(hdr));
844             q += sizeof(hdr);
845             memcpy(q, payload, payload_size);
846             q += payload_size;
847             c->buffer_ptr = c->buffer;
848             c->buffer_end = q;
849         }
850         free(payload);
851 #endif
852         {
853             AVPacket pkt;
854
855             /* read a packet from the input stream */
856             if (c->stream->feed) {
857                 ffm_set_write_index(c->fmt_in, 
858                                     c->stream->feed->feed_write_index,
859                                     c->stream->feed->feed_size);
860             }
861             if (av_read_packet(c->fmt_in, &pkt) < 0) {
862                 if (c->stream->feed && c->stream->feed->feed_opened) {
863                     /* if coming from feed, it means we reached the end of the
864                        ffm file, so must wait for more data */
865                     c->state = HTTPSTATE_WAIT_FEED;
866                     return 1; /* state changed */
867                 } else {
868                     /* must send trailer now because eof or error */
869                     c->state = HTTPSTATE_SEND_DATA_TRAILER;
870                 }
871             } else {
872                 /* send it to the appropriate stream */
873                 if (c->stream->feed) {
874                     /* if coming from a feed, select the right stream */
875                     for(i=0;i<c->stream->nb_streams;i++) {
876                         if (c->stream->feed_streams[i] == pkt.stream_index) {
877                             pkt.stream_index = i;
878                             goto send_it;
879                         }
880                     }
881                 } else {
882                 send_it:
883                     av_write_packet(&c->fmt_ctx, &pkt);
884                 }
885                 
886                 av_free_packet(&pkt);
887             }
888         }
889         break;
890     default:
891     case HTTPSTATE_SEND_DATA_TRAILER:
892         /* last packet test ? */
893         if (c->last_packet_sent)
894             return -1;
895         /* prepare header */
896         c->fmt_ctx.format->write_trailer(&c->fmt_ctx);
897         c->last_packet_sent = 1;
898         break;
899     }
900     return 0;
901 }
902
903 /* should convert the format at the same time */
904 static int http_send_data(HTTPContext *c)
905 {
906     int len, ret;
907
908     while (c->buffer_ptr >= c->buffer_end) {
909         ret = http_prepare_data(c);
910         if (ret < 0)
911             return -1;
912         else if (ret == 0) {
913             break;
914         } else {
915             /* state change requested */
916             return 0;
917         }
918     }
919
920     len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
921     if (len < 0) {
922         if (errno != EAGAIN && errno != EINTR) {
923             /* error : close connection */
924             return -1;
925         }
926     } else {
927         c->buffer_ptr += len;
928         c->data_count += len;
929     }
930     return 0;
931 }
932
933 static int http_start_receive_data(HTTPContext *c)
934 {
935     int fd;
936
937     if (c->stream->feed_opened)
938         return -1;
939
940     /* open feed */
941     fd = open(c->stream->feed_filename, O_RDWR);
942     if (fd < 0)
943         return -1;
944     c->feed_fd = fd;
945     
946     c->stream->feed_write_index = ffm_read_write_index(fd);
947     c->stream->feed_size = lseek(fd, 0, SEEK_END);
948     lseek(fd, 0, SEEK_SET);
949
950     /* init buffer input */
951     c->buffer_ptr = c->buffer;
952     c->buffer_end = c->buffer + FFM_PACKET_SIZE;
953     c->stream->feed_opened = 1;
954     return 0;
955 }
956     
957 static int http_receive_data(HTTPContext *c)
958 {
959     int len;
960     HTTPContext *c1;
961
962     if (c->buffer_ptr >= c->buffer_end) {
963         /* a packet has been received : write it in the store, except
964            if header */
965         if (c->data_count > FFM_PACKET_SIZE) {
966             FFStream *feed = c->stream;
967             
968             //            printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
969             /* XXX: use llseek or url_seek */
970             lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
971             write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
972             
973             feed->feed_write_index += FFM_PACKET_SIZE;
974             /* update file size */
975             if (feed->feed_write_index > c->stream->feed_size)
976                 feed->feed_size = feed->feed_write_index;
977
978             /* handle wrap around if max file size reached */
979             if (feed->feed_write_index >= c->stream->feed_max_size)
980                 feed->feed_write_index = FFM_PACKET_SIZE;
981
982             /* write index */
983             ffm_write_write_index(c->feed_fd, feed->feed_write_index);
984
985             /* wake up any waiting connections */
986             for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
987                 if (c1->state == HTTPSTATE_WAIT_FEED && 
988                     c1->stream->feed == c->stream->feed) {
989                     c1->state = HTTPSTATE_SEND_DATA;
990                 }
991             }
992         }
993         c->buffer_ptr = c->buffer;
994     }
995
996     len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
997     if (len < 0) {
998         if (errno != EAGAIN && errno != EINTR) {
999             /* error : close connection */
1000             goto fail;
1001         }
1002     } else if (len == 0) {
1003         /* end of connection : close it */
1004         goto fail;
1005     } else {
1006         c->buffer_ptr += len;
1007         c->data_count += len;
1008     }
1009     return 0;
1010  fail:
1011     c->stream->feed_opened = 0;
1012     close(c->feed_fd);
1013     return -1;
1014 }
1015
1016 /* return the stream number in the feed */
1017 int add_av_stream(FFStream *feed,
1018                   AVStream *st)
1019 {
1020     AVStream *fst;
1021     AVCodecContext *av, *av1;
1022     int i;
1023
1024     av = &st->codec;
1025     for(i=0;i<feed->nb_streams;i++) {
1026         st = feed->streams[i];
1027         av1 = &st->codec;
1028         if (av1->codec == av->codec &&
1029             av1->bit_rate == av->bit_rate) {
1030
1031             switch(av->codec_type) {
1032             case CODEC_TYPE_AUDIO:
1033                 if (av1->channels == av->channels &&
1034                     av1->sample_rate == av->sample_rate)
1035                     goto found;
1036                 break;
1037             case CODEC_TYPE_VIDEO:
1038                 if (av1->width == av->width &&
1039                     av1->height == av->height &&
1040                     av1->frame_rate == av->frame_rate &&
1041                     av1->gop_size == av->gop_size)
1042                     goto found;
1043                 break;
1044             }
1045         }
1046     }
1047     
1048     fst = av_mallocz(sizeof(AVStream));
1049     if (!fst)
1050         return -1;
1051     fst->priv_data = av_mallocz(sizeof(FeedData));
1052     memcpy(&fst->codec, av, sizeof(AVCodecContext));
1053     feed->streams[feed->nb_streams++] = fst;
1054     return feed->nb_streams - 1;
1055  found:
1056     return i;
1057 }
1058
1059 /* compute the needed AVStream for each feed */
1060 void build_feed_streams(void)
1061 {
1062     FFStream *stream, *feed;
1063     int i;
1064
1065     /* gather all streams */
1066     for(stream = first_stream; stream != NULL; stream = stream->next) {
1067         feed = stream->feed;
1068         if (feed) {
1069             if (!stream->is_feed) {
1070                 for(i=0;i<stream->nb_streams;i++) {
1071                     stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
1072                 }
1073             } else {
1074                 for(i=0;i<stream->nb_streams;i++) {
1075                     stream->feed_streams[i] = i;
1076                 }
1077             }
1078         }
1079     }
1080
1081     /* create feed files if needed */
1082     for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
1083         int fd;
1084
1085         if (!url_exist(feed->feed_filename)) {
1086             AVFormatContext s1, *s = &s1;
1087
1088             /* only write the header of the ffm file */
1089             if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
1090                 fprintf(stderr, "Could not open output feed file '%s'\n",
1091                         feed->feed_filename);
1092                 exit(1);
1093             }
1094             s->format = feed->fmt;
1095             s->nb_streams = feed->nb_streams;
1096             for(i=0;i<s->nb_streams;i++) {
1097                 AVStream *st;
1098                 st = feed->streams[i];
1099                 s->streams[i] = st;
1100             }
1101             s->format->write_header(s);
1102
1103             url_fclose(&s->pb);
1104         }
1105         /* get feed size and write index */
1106         fd = open(feed->feed_filename, O_RDONLY);
1107         if (fd < 0) {
1108             fprintf(stderr, "Could not open output feed file '%s'\n",
1109                     feed->feed_filename);
1110             exit(1);
1111         }
1112
1113         feed->feed_write_index = ffm_read_write_index(fd);
1114         feed->feed_size = lseek(fd, 0, SEEK_END);
1115         /* ensure that we do not wrap before the end of file */
1116         if (feed->feed_max_size < feed->feed_size)
1117             feed->feed_max_size = feed->feed_size;
1118
1119         close(fd);
1120     }
1121 }
1122
1123 static void get_arg(char *buf, int buf_size, const char **pp)
1124 {
1125     const char *p;
1126     char *q;
1127     int quote;
1128
1129     p = *pp;
1130     while (isspace(*p)) p++;
1131     q = buf;
1132     quote = 0;
1133     if (*p == '\"' || *p == '\'')
1134         quote = *p++;
1135     for(;;) {
1136         if (quote) {
1137             if (*p == quote)
1138                 break;
1139         } else {
1140             if (isspace(*p))
1141                 break;
1142         }
1143         if (*p == '\0')
1144             break;
1145         if ((q - buf) < buf_size - 1)
1146             *q++ = *p;
1147         p++;
1148     }
1149     *q = '\0';
1150     if (quote && *p == quote)
1151         p++;
1152     *pp = p;
1153 }
1154
1155 /* add a codec and set the default parameters */
1156 void add_codec(FFStream *stream, AVCodecContext *av)
1157 {
1158     AVStream *st;
1159
1160     /* compute default parameters */
1161     switch(av->codec_type) {
1162     case CODEC_TYPE_AUDIO:
1163         if (av->bit_rate == 0)
1164             av->bit_rate = 64000;
1165         if (av->sample_rate == 0)
1166             av->sample_rate = 22050;
1167         if (av->channels == 0)
1168             av->channels = 1;
1169         break;
1170     case CODEC_TYPE_VIDEO:
1171         if (av->bit_rate == 0)
1172             av->bit_rate = 64000;
1173         if (av->frame_rate == 0)
1174             av->frame_rate = 5 * FRAME_RATE_BASE;
1175         if (av->width == 0 || av->height == 0) {
1176             av->width = 160;
1177             av->height = 128;
1178         }
1179         break;
1180     }
1181
1182     st = av_mallocz(sizeof(AVStream));
1183     if (!st)
1184         return;
1185     stream->streams[stream->nb_streams++] = st;
1186     memcpy(&st->codec, av, sizeof(AVCodecContext));
1187 }
1188
1189 int parse_ffconfig(const char *filename)
1190 {
1191     FILE *f;
1192     char line[1024];
1193     char cmd[64];
1194     char arg[1024];
1195     const char *p;
1196     int val, errors, line_num;
1197     FFStream **last_stream, *stream;
1198     FFStream **last_feed, *feed;
1199     AVCodecContext audio_enc, video_enc;
1200     int audio_id, video_id;
1201
1202     f = fopen(filename, "r");
1203     if (!f) {
1204         perror(filename);
1205         return -1;
1206     }
1207     
1208     errors = 0;
1209     line_num = 0;
1210     first_stream = NULL;
1211     last_stream = &first_stream;
1212     first_feed = NULL;
1213     last_feed = &first_feed;
1214     stream = NULL;
1215     feed = NULL;
1216     audio_id = CODEC_ID_NONE;
1217     video_id = CODEC_ID_NONE;
1218     for(;;) {
1219         if (fgets(line, sizeof(line), f) == NULL)
1220             break;
1221         line_num++;
1222         p = line;
1223         while (isspace(*p)) 
1224             p++;
1225         if (*p == '\0' || *p == '#')
1226             continue;
1227
1228         get_arg(cmd, sizeof(cmd), &p);
1229         
1230         if (!strcasecmp(cmd, "Port")) {
1231             get_arg(arg, sizeof(arg), &p);
1232             my_addr.sin_port = htons (atoi(arg));
1233         } else if (!strcasecmp(cmd, "BindAddress")) {
1234             get_arg(arg, sizeof(arg), &p);
1235             if (!inet_aton(arg, &my_addr.sin_addr)) {
1236                 fprintf(stderr, "%s:%d: Invalid IP address: %s\n", 
1237                         filename, line_num, arg);
1238                 errors++;
1239             }
1240         } else if (!strcasecmp(cmd, "MaxClients")) {
1241             get_arg(arg, sizeof(arg), &p);
1242             val = atoi(arg);
1243             if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
1244                 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n", 
1245                         filename, line_num, arg);
1246                 errors++;
1247             } else {
1248                 nb_max_connections = val;
1249             }
1250         } else if (!strcasecmp(cmd, "CustomLog")) {
1251             get_arg(logfilename, sizeof(logfilename), &p);
1252         } else if (!strcasecmp(cmd, "<Feed")) {
1253             /*********************************************/
1254             /* Feed related options */
1255             char *q;
1256             if (stream || feed) {
1257                 fprintf(stderr, "%s:%d: Already in a tag\n",
1258                         filename, line_num);
1259             } else {
1260                 feed = av_mallocz(sizeof(FFStream));
1261                 /* add in stream list */
1262                 *last_stream = feed;
1263                 last_stream = &feed->next;
1264                 /* add in feed list */
1265                 *last_feed = feed;
1266                 last_feed = &feed->next_feed;
1267                 
1268                 get_arg(feed->filename, sizeof(feed->filename), &p);
1269                 q = strrchr(feed->filename, '>');
1270                 if (*q)
1271                     *q = '\0';
1272                 feed->fmt = guess_format("ffm", NULL, NULL);
1273                 /* defaut feed file */
1274                 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
1275                          "/tmp/%s.ffm", feed->filename);
1276                 feed->feed_max_size = 5 * 1024 * 1024;
1277                 feed->is_feed = 1;
1278                 feed->feed = feed; /* self feeding :-) */
1279             }
1280         } else if (!strcasecmp(cmd, "File")) {
1281             if (feed) {
1282                 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
1283             } else if (stream) {
1284                 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
1285             }
1286         } else if (!strcasecmp(cmd, "FileMaxSize")) {
1287             if (feed) {
1288                 const char *p1;
1289                 double fsize;
1290
1291                 get_arg(arg, sizeof(arg), &p);
1292                 p1 = arg;
1293                 fsize = strtod(p1, (char **)&p1);
1294                 switch(toupper(*p1)) {
1295                 case 'K':
1296                     fsize *= 1024;
1297                     break;
1298                 case 'M':
1299                     fsize *= 1024 * 1024;
1300                     break;
1301                 case 'G':
1302                     fsize *= 1024 * 1024 * 1024;
1303                     break;
1304                 }
1305                 feed->feed_max_size = (INT64)fsize;
1306             }
1307         } else if (!strcasecmp(cmd, "</Feed>")) {
1308             if (!feed) {
1309                 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
1310                         filename, line_num);
1311                 errors++;
1312             }
1313             feed = NULL;
1314         } else if (!strcasecmp(cmd, "<Stream")) {
1315             /*********************************************/
1316             /* Stream related options */
1317             char *q;
1318             if (stream || feed) {
1319                 fprintf(stderr, "%s:%d: Already in a tag\n",
1320                         filename, line_num);
1321             } else {
1322                 stream = av_mallocz(sizeof(FFStream));
1323                 *last_stream = stream;
1324                 last_stream = &stream->next;
1325
1326                 get_arg(stream->filename, sizeof(stream->filename), &p);
1327                 q = strrchr(stream->filename, '>');
1328                 if (*q)
1329                     *q = '\0';
1330                 stream->fmt = guess_format(NULL, stream->filename, NULL);
1331                 memset(&audio_enc, 0, sizeof(AVCodecContext));
1332                 memset(&video_enc, 0, sizeof(AVCodecContext));
1333                 audio_id = CODEC_ID_NONE;
1334                 video_id = CODEC_ID_NONE;
1335                 if (stream->fmt) {
1336                     audio_id = stream->fmt->audio_codec;
1337                     video_id = stream->fmt->video_codec;
1338                 }
1339             }
1340         } else if (!strcasecmp(cmd, "Feed")) {
1341             get_arg(arg, sizeof(arg), &p);
1342             if (stream) {
1343                 FFStream *sfeed;
1344                 
1345                 sfeed = first_feed;
1346                 while (sfeed != NULL) {
1347                     if (!strcmp(sfeed->filename, arg))
1348                         break;
1349                     sfeed = sfeed->next_feed;
1350                 }
1351                 if (!sfeed) {
1352                     fprintf(stderr, "%s:%d: feed '%s' not defined\n",
1353                             filename, line_num, arg);
1354                 } else {
1355                     stream->feed = sfeed;
1356                 }
1357             }
1358         } else if (!strcasecmp(cmd, "Format")) {
1359             get_arg(arg, sizeof(arg), &p);
1360             if (!strcmp(arg, "status")) {
1361                 stream->stream_type = STREAM_TYPE_STATUS;
1362                 stream->fmt = NULL;
1363             } else {
1364                 stream->stream_type = STREAM_TYPE_LIVE;
1365                 /* jpeg cannot be used here, so use single frame jpeg */
1366                 if (!strcmp(arg, "jpeg"))
1367                     strcpy(arg, "singlejpeg");
1368                 stream->fmt = guess_format(arg, NULL, NULL);
1369                 if (!stream->fmt) {
1370                     fprintf(stderr, "%s:%d: Unknown Format: %s\n", 
1371                             filename, line_num, arg);
1372                     errors++;
1373                 }
1374             }
1375             if (stream->fmt) {
1376                 audio_id = stream->fmt->audio_codec;
1377                 video_id = stream->fmt->video_codec;
1378             }
1379         } else if (!strcasecmp(cmd, "AudioBitRate")) {
1380             get_arg(arg, sizeof(arg), &p);
1381             if (stream) {
1382                 audio_enc.bit_rate = atoi(arg) * 1000;
1383             }
1384         } else if (!strcasecmp(cmd, "AudioChannels")) {
1385             get_arg(arg, sizeof(arg), &p);
1386             if (stream) {
1387                 audio_enc.channels = atoi(arg);
1388             }
1389         } else if (!strcasecmp(cmd, "AudioSampleRate")) {
1390             get_arg(arg, sizeof(arg), &p);
1391             if (stream) {
1392                 audio_enc.sample_rate = atoi(arg);
1393             }
1394         } else if (!strcasecmp(cmd, "VideoBitRate")) {
1395             get_arg(arg, sizeof(arg), &p);
1396             if (stream) {
1397                 video_enc.bit_rate = atoi(arg) * 1000;
1398             }
1399         } else if (!strcasecmp(cmd, "VideoSize")) {
1400             get_arg(arg, sizeof(arg), &p);
1401             if (stream) {
1402                 parse_image_size(&video_enc.width, &video_enc.height, arg);
1403                 if ((video_enc.width % 16) != 0 ||
1404                     (video_enc.height % 16) != 0) {
1405                     fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
1406                             filename, line_num);
1407                     errors++;
1408                 }
1409             }
1410         } else if (!strcasecmp(cmd, "VideoFrameRate")) {
1411             get_arg(arg, sizeof(arg), &p);
1412             if (stream) {
1413                 video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
1414             }
1415         } else if (!strcasecmp(cmd, "VideoGopSize")) {
1416             get_arg(arg, sizeof(arg), &p);
1417             if (stream) {
1418                 video_enc.gop_size = atoi(arg);
1419             }
1420         } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
1421             if (stream) {
1422                 video_enc.gop_size = 1;
1423             }
1424         } else if (!strcasecmp(cmd, "NoVideo")) {
1425             video_id = CODEC_ID_NONE;
1426         } else if (!strcasecmp(cmd, "NoAudio")) {
1427             audio_id = CODEC_ID_NONE;
1428         } else if (!strcasecmp(cmd, "</Stream>")) {
1429             if (!stream) {
1430                 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
1431                         filename, line_num);
1432                 errors++;
1433             }
1434             if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
1435                 if (audio_id != CODEC_ID_NONE) {
1436                     audio_enc.codec_type = CODEC_TYPE_AUDIO;
1437                     audio_enc.codec_id = audio_id;
1438                     add_codec(stream, &audio_enc);
1439                 }
1440                 if (video_id != CODEC_ID_NONE) {
1441                     video_enc.codec_type = CODEC_TYPE_VIDEO;
1442                     video_enc.codec_id = video_id;
1443                     add_codec(stream, &video_enc);
1444                 }
1445             }
1446             stream = NULL;
1447         } else {
1448             fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n", 
1449                     filename, line_num, cmd);
1450             errors++;
1451         }
1452     }
1453
1454     fclose(f);
1455     if (errors)
1456         return -1;
1457     else
1458         return 0;
1459 }
1460
1461
1462 void *http_server_thread(void *arg)
1463 {
1464     http_server(my_addr);
1465     return NULL;
1466 }
1467
1468 #if 0
1469 static void write_packet(FFCodec *ffenc,
1470                          UINT8 *buf, int size)
1471 {
1472     PacketHeader hdr;
1473     AVCodecContext *enc = &ffenc->enc;
1474     UINT8 *wptr;
1475     mk_header(&hdr, enc, size);
1476     wptr = http_fifo.wptr;
1477     fifo_write(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &wptr);
1478     fifo_write(&http_fifo, buf, size, &wptr);
1479     /* atomic modification of wptr */
1480     http_fifo.wptr = wptr;
1481     ffenc->data_count += size;
1482     ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
1483 }
1484 #endif
1485
1486 void help(void)
1487 {
1488     printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000,2001 Gerard Lantau\n"
1489            "usage: ffserver [-L] [-h] [-f configfile]\n"
1490            "Hyper fast multi format Audio/Video streaming server\n"
1491            "\n"
1492            "-L            : print the LICENCE\n"
1493            "-h            : this help\n"
1494            "-f configfile : use configfile instead of /etc/ffserver.conf\n"
1495            );
1496 }
1497
1498 void licence(void)
1499 {
1500     printf(
1501     "ffserver version " FFMPEG_VERSION "\n"
1502     "Copyright (c) 2000,2001 Gerard Lantau\n"
1503     "This program is free software; you can redistribute it and/or modify\n"
1504     "it under the terms of the GNU General Public License as published by\n"
1505     "the Free Software Foundation; either version 2 of the License, or\n"
1506     "(at your option) any later version.\n"
1507     "\n"
1508     "This program is distributed in the hope that it will be useful,\n"
1509     "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1510     "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
1511     "GNU General Public License for more details.\n"
1512     "\n"
1513     "You should have received a copy of the GNU General Public License\n"
1514     "along with this program; if not, write to the Free Software\n"
1515     "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
1516     );
1517 }
1518
1519 int main(int argc, char **argv)
1520 {
1521     const char *config_filename;
1522     int c;
1523
1524     register_all();
1525
1526     config_filename = "/etc/ffserver.conf";
1527
1528     for(;;) {
1529         c = getopt_long_only(argc, argv, "Lh?f:", NULL, NULL);
1530         if (c == -1)
1531             break;
1532         switch(c) {
1533         case 'L':
1534             licence();
1535             exit(1);
1536         case '?':
1537         case 'h':
1538             help();
1539             exit(1);
1540         case 'f':
1541             config_filename = optarg;
1542             break;
1543         default:
1544             exit(2);
1545         }
1546     }
1547
1548     /* address on which the server will handle connections */
1549     my_addr.sin_family = AF_INET;
1550     my_addr.sin_port = htons (8080);
1551     my_addr.sin_addr.s_addr = htonl (INADDR_ANY);
1552     nb_max_connections = 5;
1553     first_stream = NULL;
1554     logfilename[0] = '\0';
1555
1556     if (parse_ffconfig(config_filename) < 0) {
1557         fprintf(stderr, "Incorrect config file - exiting.\n");
1558         exit(1);
1559     }
1560
1561     build_feed_streams();
1562
1563     /* signal init */
1564     signal(SIGPIPE, SIG_IGN);
1565
1566     /* open log file if needed */
1567     if (logfilename[0] != '\0') {
1568         if (!strcmp(logfilename, "-"))
1569             logfile = stdout;
1570         else
1571             logfile = fopen(logfilename, "w");
1572     }
1573
1574     if (http_server(my_addr) < 0) {
1575         fprintf(stderr, "Could start http server\n");
1576         exit(1);
1577     }
1578
1579     return 0;
1580 }