]> git.sesse.net Git - ffmpeg/blob - ffserver.c
moved simple c utils
[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 <sys/wait.h>
35 #include <arpa/inet.h>
36 #include <netdb.h>
37 #include <ctype.h>
38 #include <signal.h>
39
40 /* maximum number of simultaneous HTTP connections */
41 #define HTTP_MAX_CONNECTIONS 2000
42
43 enum HTTPState {
44     HTTPSTATE_WAIT_REQUEST,
45     HTTPSTATE_SEND_HEADER,
46     HTTPSTATE_SEND_DATA_HEADER,
47     HTTPSTATE_SEND_DATA,
48     HTTPSTATE_SEND_DATA_TRAILER,
49     HTTPSTATE_RECEIVE_DATA,
50     HTTPSTATE_WAIT_FEED,
51 };
52
53 const char *http_state[] = {
54     "WAIT_REQUEST",
55     "SEND_HEADER",
56     "SEND_DATA_HEADER",
57     "SEND_DATA",
58     "SEND_DATA_TRAILER",
59     "RECEIVE_DATA",
60     "WAIT_FEED",
61 };
62
63 #define IOBUFFER_INIT_SIZE 8192
64 #define PBUFFER_INIT_SIZE 8192
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 typedef struct {
74     INT64 count1, count2;
75     long time1, time2;
76 } DataRateData;
77
78 /* context associated with one connection */
79 typedef struct HTTPContext {
80     enum HTTPState state;
81     int fd; /* socket file descriptor */
82     struct sockaddr_in from_addr; /* origin */
83     struct pollfd *poll_entry; /* used when polling */
84     long timeout;
85     UINT8 *buffer_ptr, *buffer_end;
86     int http_error;
87     struct HTTPContext *next;
88     int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
89     INT64 data_count;
90     /* feed input */
91     int feed_fd;
92     /* input format handling */
93     AVFormatContext *fmt_in;
94     /* output format handling */
95     struct FFStream *stream;
96     /* -1 is invalid stream */
97     int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
98     int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
99     int switch_pending;
100     AVFormatContext fmt_ctx;
101     int last_packet_sent; /* true if last data packet was sent */
102     int suppress_log;
103     int bandwidth;
104     long start_time;            /* In milliseconds - this wraps fairly often */
105     DataRateData datarate;
106     int wmp_client_id;
107     char protocol[16];
108     char method[16];
109     char url[128];
110     int buffer_size;
111     UINT8 *buffer;
112     int pbuffer_size;
113     UINT8 *pbuffer;
114 } HTTPContext;
115
116 /* each generated stream is described here */
117 enum StreamType {
118     STREAM_TYPE_LIVE,
119     STREAM_TYPE_STATUS,
120     STREAM_TYPE_REDIRECT,
121 };
122
123 /* description of each stream of the ffserver.conf file */
124 typedef struct FFStream {
125     enum StreamType stream_type;
126     char filename[1024];     /* stream filename */
127     struct FFStream *feed;
128     AVOutputFormat *fmt;
129     int nb_streams;
130     int prebuffer;      /* Number of millseconds early to start */
131     long max_time;      /* Number of milliseconds to run */
132     int send_on_key;
133     AVStream *streams[MAX_STREAMS];
134     int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
135     char feed_filename[1024]; /* file name of the feed storage, or
136                                  input file name for a stream */
137     char author[512];
138     char title[512];
139     char copyright[512];
140     char comment[512];
141     pid_t pid;  /* Of ffmpeg process */
142     time_t pid_start;  /* Of ffmpeg process */
143     char **child_argv;
144     struct FFStream *next;
145     /* feed specific */
146     int feed_opened;     /* true if someone if writing to feed */
147     int is_feed;         /* true if it is a feed */
148     int conns_served;
149     INT64 bytes_served;
150     INT64 feed_max_size;      /* maximum storage size */
151     INT64 feed_write_index;   /* current write position in feed (it wraps round) */
152     INT64 feed_size;          /* current size of feed */
153     struct FFStream *next_feed;
154 } FFStream;
155
156 typedef struct FeedData {
157     long long data_count;
158     float avg_frame_size;   /* frame size averraged over last frames with exponential mean */
159 } FeedData;
160
161 struct sockaddr_in my_addr;
162 char logfilename[1024];
163 HTTPContext *first_http_ctx;
164 FFStream *first_feed;   /* contains only feeds */
165 FFStream *first_stream; /* contains all streams, including feeds */
166
167 static int handle_http(HTTPContext *c);
168 static int http_parse_request(HTTPContext *c);
169 static int http_send_data(HTTPContext *c);
170 static void compute_stats(HTTPContext *c);
171 static int open_input_stream(HTTPContext *c, const char *info);
172 static int http_start_receive_data(HTTPContext *c);
173 static int http_receive_data(HTTPContext *c);
174
175 static const char *my_program_name;
176
177 static int ffserver_debug;
178 static int no_launch;
179 static int need_to_start_children;
180
181 int nb_max_connections;
182 int nb_connections;
183
184 int nb_max_bandwidth;
185 int nb_bandwidth;
186
187 static long cur_time;           // Making this global saves on passing it around everywhere
188
189 static long gettime_ms(void)
190 {
191     struct timeval tv;
192
193     gettimeofday(&tv,NULL);
194     return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
195 }
196
197 static FILE *logfile = NULL;
198
199 static void http_log(char *fmt, ...)
200 {
201     va_list ap;
202     va_start(ap, fmt);
203     
204     if (logfile) {
205         vfprintf(logfile, fmt, ap);
206         fflush(logfile);
207     }
208     va_end(ap);
209 }
210
211 static void log_connection(HTTPContext *c)
212 {
213     char buf1[32], buf2[32], *p;
214     time_t ti;
215
216     if (c->suppress_log) 
217         return;
218
219     /* XXX: reentrant function ? */
220     p = inet_ntoa(c->from_addr.sin_addr);
221     strcpy(buf1, p);
222     ti = time(NULL);
223     p = ctime(&ti);
224     strcpy(buf2, p);
225     p = buf2 + strlen(p) - 1;
226     if (*p == '\n')
227         *p = '\0';
228     http_log("%s - - [%s] \"%s %s %s\" %d %lld\n", 
229              buf1, buf2, c->method, c->url, c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
230 }
231
232 static void update_datarate(DataRateData *drd, INT64 count)
233 {
234     if (!drd->time1 && !drd->count1) {
235         drd->time1 = drd->time2 = cur_time;
236         drd->count1 = drd->count2 = count;
237     } else {
238         if (cur_time - drd->time2 > 5000) {
239             drd->time1 = drd->time2;
240             drd->count1 = drd->count2;
241             drd->time2 = cur_time;
242             drd->count2 = count;
243         }
244     }
245 }
246
247 /* In bytes per second */
248 static int compute_datarate(DataRateData *drd, INT64 count)
249 {
250     if (cur_time == drd->time1)
251         return 0;
252
253     return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
254 }
255
256 static void start_children(FFStream *feed)
257 {
258     if (no_launch)
259         return;
260
261     for (; feed; feed = feed->next) {
262         if (feed->child_argv && !feed->pid) {
263             feed->pid_start = time(0);
264
265             feed->pid = fork();
266
267             if (feed->pid < 0) {
268                 fprintf(stderr, "Unable to create children\n");
269                 exit(1);
270             }
271             if (!feed->pid) {
272                 /* In child */
273                 char pathname[1024];
274                 char *slash;
275                 int i;
276
277                 for (i = 3; i < 256; i++) {
278                     close(i);
279                 }
280
281                 if (!ffserver_debug) {
282                     i = open("/dev/null", O_RDWR);
283                     if (i)
284                         dup2(i, 0);
285                     dup2(i, 1);
286                     dup2(i, 2);
287                     if (i)
288                         close(i);
289                 }
290
291                 pstrcpy(pathname, sizeof(pathname), my_program_name);
292
293                 slash = strrchr(pathname, '/');
294                 if (!slash) {
295                     slash = pathname;
296                 } else {
297                     slash++;
298                 }
299                 strcpy(slash, "ffmpeg");
300
301                 execvp(pathname, feed->child_argv);
302
303                 _exit(1);
304             }
305         }
306     }
307 }
308
309 /* main loop of the http server */
310 static int http_server(struct sockaddr_in my_addr)
311 {
312     int server_fd, tmp, ret;
313     struct sockaddr_in from_addr;
314     struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 1], *poll_entry;
315     HTTPContext *c, **cp;
316
317     server_fd = socket(AF_INET,SOCK_STREAM,0);
318     if (server_fd < 0) {
319         perror ("socket");
320         return -1;
321     }
322         
323     tmp = 1;
324     setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
325
326     if (bind (server_fd, (struct sockaddr *) &my_addr, sizeof (my_addr)) < 0) {
327         perror ("bind");
328         close(server_fd);
329         return -1;
330     }
331   
332     if (listen (server_fd, 5) < 0) {
333         perror ("listen");
334         close(server_fd);
335         return -1;
336     }
337
338     http_log("ffserver started.\n");
339
340     start_children(first_feed);
341
342     fcntl(server_fd, F_SETFL, O_NONBLOCK);
343     first_http_ctx = NULL;
344     nb_connections = 0;
345     first_http_ctx = NULL;
346     for(;;) {
347         poll_entry = poll_table;
348         poll_entry->fd = server_fd;
349         poll_entry->events = POLLIN;
350         poll_entry++;
351
352         /* wait for events on each HTTP handle */
353         c = first_http_ctx;
354         while (c != NULL) {
355             int fd;
356             fd = c->fd;
357             switch(c->state) {
358             case HTTPSTATE_WAIT_REQUEST:
359                 c->poll_entry = poll_entry;
360                 poll_entry->fd = fd;
361                 poll_entry->events = POLLIN;
362                 poll_entry++;
363                 break;
364             case HTTPSTATE_SEND_HEADER:
365             case HTTPSTATE_SEND_DATA_HEADER:
366             case HTTPSTATE_SEND_DATA:
367             case HTTPSTATE_SEND_DATA_TRAILER:
368                 c->poll_entry = poll_entry;
369                 poll_entry->fd = fd;
370                 poll_entry->events = POLLOUT;
371                 poll_entry++;
372                 break;
373             case HTTPSTATE_RECEIVE_DATA:
374                 c->poll_entry = poll_entry;
375                 poll_entry->fd = fd;
376                 poll_entry->events = POLLIN;
377                 poll_entry++;
378                 break;
379             case HTTPSTATE_WAIT_FEED:
380                 /* need to catch errors */
381                 c->poll_entry = poll_entry;
382                 poll_entry->fd = fd;
383                 poll_entry->events = POLLIN;/* Maybe this will work */
384                 poll_entry++;
385                 break;
386             default:
387                 c->poll_entry = NULL;
388                 break;
389             }
390             c = c->next;
391         }
392
393         /* wait for an event on one connection. We poll at least every
394            second to handle timeouts */
395         do {
396             ret = poll(poll_table, poll_entry - poll_table, 1000);
397         } while (ret == -1);
398         
399         cur_time = gettime_ms();
400
401         if (need_to_start_children) {
402             need_to_start_children = 0;
403             start_children(first_feed);
404         }
405
406         /* now handle the events */
407
408         cp = &first_http_ctx;
409         while ((*cp) != NULL) {
410             c = *cp;
411             if (handle_http (c) < 0) {
412                 /* close and free the connection */
413                 log_connection(c);
414                 close(c->fd);
415                 if (c->fmt_in)
416                     av_close_input_file(c->fmt_in);
417                 *cp = c->next;
418                 nb_bandwidth -= c->bandwidth;
419                 av_free(c->buffer);
420                 av_free(c->pbuffer);
421                 av_free(c);
422                 nb_connections--;
423             } else {
424                 cp = &c->next;
425             }
426         }
427
428         /* new connection request ? */
429         poll_entry = poll_table;
430         if (poll_entry->revents & POLLIN) {
431             int fd, len;
432
433             len = sizeof(from_addr);
434             fd = accept(server_fd, (struct sockaddr *)&from_addr, 
435                         &len);
436             if (fd >= 0) {
437                 fcntl(fd, F_SETFL, O_NONBLOCK);
438                 /* XXX: should output a warning page when coming
439                    close to the connection limit */
440                 if (nb_connections >= nb_max_connections) {
441                     c = NULL;
442                 } else {
443                     /* add a new connection */
444                     c = av_mallocz(sizeof(HTTPContext));
445                     if (c) {
446                         c->next = first_http_ctx;
447                         first_http_ctx = c;
448                         c->fd = fd;
449                         c->poll_entry = NULL;
450                         c->from_addr = from_addr;
451                         c->state = HTTPSTATE_WAIT_REQUEST;
452                         c->buffer = av_malloc(c->buffer_size = IOBUFFER_INIT_SIZE);
453                         c->pbuffer = av_malloc(c->pbuffer_size = PBUFFER_INIT_SIZE);
454                         if (!c->buffer || !c->pbuffer) {
455                             av_free(c->buffer);
456                             av_free(c->pbuffer);
457                             av_freep(&c);
458                         } else {
459                             c->buffer_ptr = c->buffer;
460                             c->buffer_end = c->buffer + c->buffer_size;
461                             c->timeout = cur_time + REQUEST_TIMEOUT;
462                             c->start_time = cur_time;
463                             nb_connections++;
464                         }
465                     }
466                 }
467                 if (!c) {
468                     close(fd);
469                 }
470             }
471         }
472         poll_entry++;
473     }
474 }
475
476 static int handle_http(HTTPContext *c)
477 {
478     int len;
479     
480     switch(c->state) {
481     case HTTPSTATE_WAIT_REQUEST:
482         /* timeout ? */
483         if ((c->timeout - cur_time) < 0)
484             return -1;
485         if (c->poll_entry->revents & (POLLERR | POLLHUP))
486             return -1;
487
488         /* no need to read if no events */
489         if (!(c->poll_entry->revents & POLLIN))
490             return 0;
491         /* read the data */
492         len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
493         if (len < 0) {
494             if (errno != EAGAIN && errno != EINTR)
495                 return -1;
496         } else if (len == 0) {
497             return -1;
498         } else {
499             /* search for end of request. XXX: not fully correct since garbage could come after the end */
500             UINT8 *ptr;
501             c->buffer_ptr += len;
502             ptr = c->buffer_ptr;
503             if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
504                 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
505                 /* request found : parse it and reply */
506                 if (http_parse_request(c) < 0)
507                     return -1;
508             } else if (ptr >= c->buffer_end) {
509                 /* request too long: cannot do anything */
510                 return -1;
511             }
512         }
513         break;
514
515     case HTTPSTATE_SEND_HEADER:
516         if (c->poll_entry->revents & (POLLERR | POLLHUP))
517             return -1;
518
519         /* no need to read if no events */
520         if (!(c->poll_entry->revents & POLLOUT))
521             return 0;
522         len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
523         if (len < 0) {
524             if (errno != EAGAIN && errno != EINTR) {
525                 /* error : close connection */
526                 return -1;
527             }
528         } else {
529             c->buffer_ptr += len;
530             if (c->stream)
531                 c->stream->bytes_served += len;
532             c->data_count += len;
533             if (c->buffer_ptr >= c->buffer_end) {
534                 /* if error, exit */
535                 if (c->http_error)
536                     return -1;
537                 /* all the buffer was send : synchronize to the incoming stream */
538                 c->state = HTTPSTATE_SEND_DATA_HEADER;
539                 c->buffer_ptr = c->buffer_end = c->buffer;
540             }
541         }
542         break;
543
544     case HTTPSTATE_SEND_DATA:
545     case HTTPSTATE_SEND_DATA_HEADER:
546     case HTTPSTATE_SEND_DATA_TRAILER:
547         /* no need to read if no events */
548         if (c->poll_entry->revents & (POLLERR | POLLHUP))
549             return -1;
550         
551         if (!(c->poll_entry->revents & POLLOUT))
552             return 0;
553         if (http_send_data(c) < 0)
554             return -1;
555         break;
556     case HTTPSTATE_RECEIVE_DATA:
557         /* no need to read if no events */
558         if (c->poll_entry->revents & (POLLERR | POLLHUP))
559             return -1;
560         if (!(c->poll_entry->revents & POLLIN))
561             return 0;
562         if (http_receive_data(c) < 0)
563             return -1;
564         break;
565     case HTTPSTATE_WAIT_FEED:
566         /* no need to read if no events */
567         if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
568             return -1;
569
570         /* nothing to do, we'll be waken up by incoming feed packets */
571         break;
572     default:
573         return -1;
574     }
575     return 0;
576 }
577
578 static int extract_rates(char *rates, int ratelen, const char *request)
579 {
580     const char *p;
581
582     for (p = request; *p && *p != '\r' && *p != '\n'; ) {
583         if (strncasecmp(p, "Pragma:", 7) == 0) {
584             const char *q = p + 7;
585
586             while (*q && *q != '\n' && isspace(*q))
587                 q++;
588
589             if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
590                 int stream_no;
591                 int rate_no;
592
593                 q += 20;
594
595                 memset(rates, 0xff, ratelen);
596
597                 while (1) {
598                     while (*q && *q != '\n' && *q != ':')
599                         q++;
600
601                     if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
602                         break;
603                     }
604                     stream_no--;
605                     if (stream_no < ratelen && stream_no >= 0) {
606                         rates[stream_no] = rate_no;
607                     }
608
609                     while (*q && *q != '\n' && !isspace(*q))
610                         q++;
611                 }
612
613                 return 1;
614             }
615         }
616         p = strchr(p, '\n');
617         if (!p)
618             break;
619
620         p++;
621     }
622
623     return 0;
624 }
625
626 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
627 {
628     int i;
629     int best_bitrate = 100000000;
630     int best = -1;
631
632     for (i = 0; i < feed->nb_streams; i++) {
633         AVCodecContext *feed_codec = &feed->streams[i]->codec;
634
635         if (feed_codec->codec_id != codec->codec_id ||
636             feed_codec->sample_rate != codec->sample_rate ||
637             feed_codec->width != codec->width ||
638             feed_codec->height != codec->height) {
639             continue;
640         }
641
642         /* Potential stream */
643
644         /* We want the fastest stream less than bit_rate, or the slowest 
645          * faster than bit_rate
646          */
647
648         if (feed_codec->bit_rate <= bit_rate) {
649             if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
650                 best_bitrate = feed_codec->bit_rate;
651                 best = i;
652             }
653         } else {
654             if (feed_codec->bit_rate < best_bitrate) {
655                 best_bitrate = feed_codec->bit_rate;
656                 best = i;
657             }
658         }
659     }
660
661     return best;
662 }
663
664 static int modify_current_stream(HTTPContext *c, char *rates)
665 {
666     int i;
667     FFStream *req = c->stream;
668     int action_required = 0;
669
670     for (i = 0; i < req->nb_streams; i++) {
671         AVCodecContext *codec = &req->streams[i]->codec;
672
673         switch(rates[i]) {
674             case 0:
675                 c->switch_feed_streams[i] = req->feed_streams[i];
676                 break;
677             case 1:
678                 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
679                 break;
680             case 2:
681                 /* Wants off or slow */
682                 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
683 #ifdef WANTS_OFF
684                 /* This doesn't work well when it turns off the only stream! */
685                 c->switch_feed_streams[i] = -2;
686                 c->feed_streams[i] = -2;
687 #endif
688                 break;
689         }
690
691         if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
692             action_required = 1;
693     }
694
695     return action_required;
696 }
697
698
699 static void do_switch_stream(HTTPContext *c, int i)
700 {
701     if (c->switch_feed_streams[i] >= 0) {
702 #ifdef PHILIP        
703         c->feed_streams[i] = c->switch_feed_streams[i];
704 #endif
705
706         /* Now update the stream */
707     }
708     c->switch_feed_streams[i] = -1;
709 }
710
711 /* parse http request and prepare header */
712 static int http_parse_request(HTTPContext *c)
713 {
714     char *p;
715     int post;
716     int doing_asx;
717     int doing_asf_redirector;
718     int doing_ram;
719     char cmd[32];
720     char info[1024], *filename;
721     char url[1024], *q;
722     char protocol[32];
723     char msg[1024];
724     const char *mime_type;
725     FFStream *stream;
726     int i;
727     char ratebuf[32];
728     char *useragent = 0;
729
730     p = c->buffer;
731     q = cmd;
732     while (!isspace(*p) && *p != '\0') {
733         if ((q - cmd) < sizeof(cmd) - 1)
734             *q++ = *p;
735         p++;
736     }
737     *q = '\0';
738
739     pstrcpy(c->method, sizeof(c->method), cmd);
740
741     if (!strcmp(cmd, "GET"))
742         post = 0;
743     else if (!strcmp(cmd, "POST"))
744         post = 1;
745     else
746         return -1;
747
748     while (isspace(*p)) p++;
749     q = url;
750     while (!isspace(*p) && *p != '\0') {
751         if ((q - url) < sizeof(url) - 1)
752             *q++ = *p;
753         p++;
754     }
755     *q = '\0';
756
757     pstrcpy(c->url, sizeof(c->url), url);
758
759     while (isspace(*p)) p++;
760     q = protocol;
761     while (!isspace(*p) && *p != '\0') {
762         if ((q - protocol) < sizeof(protocol) - 1)
763             *q++ = *p;
764         p++;
765     }
766     *q = '\0';
767     if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
768         return -1;
769
770     pstrcpy(c->protocol, sizeof(c->protocol), protocol);
771     
772     /* find the filename and the optional info string in the request */
773     p = url;
774     if (*p == '/')
775         p++;
776     filename = p;
777     p = strchr(p, '?');
778     if (p) {
779         pstrcpy(info, sizeof(info), p);
780         *p = '\0';
781     } else {
782         info[0] = '\0';
783     }
784
785     for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
786         if (strncasecmp(p, "User-Agent:", 11) == 0) {
787             useragent = p + 11;
788             if (*useragent && *useragent != '\n' && isspace(*useragent))
789                 useragent++;
790             break;
791         }
792         p = strchr(p, '\n');
793         if (!p)
794             break;
795
796         p++;
797     }
798
799     if (strlen(filename) > 4 && strcmp(".asx", filename + strlen(filename) - 4) == 0) {
800         doing_asx = 1;
801         filename[strlen(filename)-1] = 'f';
802     } else {
803         doing_asx = 0;
804     }
805
806     if (strlen(filename) > 4 && strcmp(".asf", filename + strlen(filename) - 4) == 0 &&
807         (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
808         /* if this isn't WMP or lookalike, return the redirector file */
809         doing_asf_redirector = 1;
810     } else {
811         doing_asf_redirector = 0;
812     }
813
814     if (strlen(filename) > 4 && 
815         (strcmp(".rpm", filename + strlen(filename) - 4) == 0 ||
816          strcmp(".ram", filename + strlen(filename) - 4) == 0)) {
817         doing_ram = 1;
818         strcpy(filename + strlen(filename)-2, "m");
819     } else {
820         doing_ram = 0;
821     }
822
823     stream = first_stream;
824     while (stream != NULL) {
825         if (!strcmp(stream->filename, filename))
826             break;
827         stream = stream->next;
828     }
829     if (stream == NULL) {
830         sprintf(msg, "File '%s' not found", url);
831         goto send_error;
832     }
833
834     c->stream = stream;
835     memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
836     memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
837
838     if (stream->stream_type == STREAM_TYPE_REDIRECT) {
839         c->http_error = 301;
840         q = c->buffer;
841         q += sprintf(q, "HTTP/1.0 301 Moved\r\n");
842         q += sprintf(q, "Location: %s\r\n", stream->feed_filename);
843         q += sprintf(q, "Content-type: text/html\r\n");
844         q += sprintf(q, "\r\n");
845         q += sprintf(q, "<html><head><title>Moved</title></head><body>\r\n");
846         q += sprintf(q, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
847         q += sprintf(q, "</body></html>\r\n");
848
849         /* prepare output buffer */
850         c->buffer_ptr = c->buffer;
851         c->buffer_end = q;
852         c->state = HTTPSTATE_SEND_HEADER;
853         return 0;
854     }
855
856     /* If this is WMP, get the rate information */
857     if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
858         if (modify_current_stream(c, ratebuf)) {
859             for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
860                 if (c->switch_feed_streams[i] >= 0)
861                     do_switch_stream(c, i);
862             }
863         }
864     }
865
866     if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
867         /* See if we meet the bandwidth requirements */
868         for(i=0;i<stream->nb_streams;i++) {
869             AVStream *st = stream->streams[i];
870             switch(st->codec.codec_type) {
871             case CODEC_TYPE_AUDIO:
872                 c->bandwidth += st->codec.bit_rate;
873                 break;
874             case CODEC_TYPE_VIDEO:
875                 c->bandwidth += st->codec.bit_rate;
876                 break;
877             default:
878                 av_abort();
879             }
880         }
881     }
882
883     c->bandwidth /= 1000;
884     nb_bandwidth += c->bandwidth;
885
886     if (post == 0 && nb_max_bandwidth < nb_bandwidth) {
887         c->http_error = 200;
888         q = c->buffer;
889         q += sprintf(q, "HTTP/1.0 200 Server too busy\r\n");
890         q += sprintf(q, "Content-type: text/html\r\n");
891         q += sprintf(q, "\r\n");
892         q += sprintf(q, "<html><head><title>Too busy</title></head><body>\r\n");
893         q += sprintf(q, "The server is too busy to serve your request at this time.<p>\r\n");
894         q += sprintf(q, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
895             nb_bandwidth, nb_max_bandwidth);
896         q += sprintf(q, "</body></html>\r\n");
897
898         /* prepare output buffer */
899         c->buffer_ptr = c->buffer;
900         c->buffer_end = q;
901         c->state = HTTPSTATE_SEND_HEADER;
902         return 0;
903     }
904     
905     if (doing_asx || doing_ram || doing_asf_redirector) {
906         char *hostinfo = 0;
907         
908         for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
909             if (strncasecmp(p, "Host:", 5) == 0) {
910                 hostinfo = p + 5;
911                 break;
912             }
913             p = strchr(p, '\n');
914             if (!p)
915                 break;
916
917             p++;
918         }
919
920         if (hostinfo) {
921             char *eoh;
922             char hostbuf[260];
923
924             while (isspace(*hostinfo))
925                 hostinfo++;
926
927             eoh = strchr(hostinfo, '\n');
928             if (eoh) {
929                 if (eoh[-1] == '\r')
930                     eoh--;
931
932                 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
933                     memcpy(hostbuf, hostinfo, eoh - hostinfo);
934                     hostbuf[eoh - hostinfo] = 0;
935
936                     c->http_error = 200;
937                     q = c->buffer;
938                     if (doing_asx) {
939                         q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n");
940                         q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
941                         q += sprintf(q, "\r\n");
942                         q += sprintf(q, "<ASX Version=\"3\">\r\n");
943                         q += sprintf(q, "<!-- Autogenerated by ffserver -->\r\n");
944                         q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n", 
945                                 hostbuf, filename, info);
946                         q += sprintf(q, "</ASX>\r\n");
947                     } else if (doing_ram) {
948                         q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n");
949                         q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n");
950                         q += sprintf(q, "\r\n");
951                         q += sprintf(q, "# Autogenerated by ffserver\r\n");
952                         q += sprintf(q, "http://%s/%s%s\r\n", 
953                                 hostbuf, filename, info);
954                     } else if (doing_asf_redirector) {
955                         q += sprintf(q, "HTTP/1.0 200 ASF Redirect follows\r\n");
956                         q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
957                         q += sprintf(q, "\r\n");
958                         q += sprintf(q, "[Reference]\r\n");
959                         q += sprintf(q, "Ref1=http://%s/%s%s\r\n", 
960                                 hostbuf, filename, info);
961                     } else
962                         av_abort();
963
964                     /* prepare output buffer */
965                     c->buffer_ptr = c->buffer;
966                     c->buffer_end = q;
967                     c->state = HTTPSTATE_SEND_HEADER;
968                     return 0;
969                 }
970             }
971         }
972
973         sprintf(msg, "ASX/RAM file not handled");
974         goto send_error;
975     }
976
977     stream->conns_served++;
978
979     /* XXX: add there authenticate and IP match */
980
981     if (post) {
982         /* if post, it means a feed is being sent */
983         if (!stream->is_feed) {
984             /* However it might be a status report from WMP! Lets log the data
985              * as it might come in handy one day
986              */
987             char *logline = 0;
988             int client_id = 0;
989             
990             for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
991                 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
992                     logline = p;
993                     break;
994                 }
995                 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
996                     client_id = strtol(p + 18, 0, 10);
997                 }
998                 p = strchr(p, '\n');
999                 if (!p)
1000                     break;
1001
1002                 p++;
1003             }
1004
1005             if (logline) {
1006                 char *eol = strchr(logline, '\n');
1007
1008                 logline += 17;
1009
1010                 if (eol) {
1011                     if (eol[-1] == '\r')
1012                         eol--;
1013                     http_log("%.*s\n", eol - logline, logline);
1014                     c->suppress_log = 1;
1015                 }
1016             }
1017
1018 #ifdef DEBUG_WMP
1019             http_log("\nGot request:\n%s\n", c->buffer);
1020 #endif
1021
1022             if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1023                 HTTPContext *wmpc;
1024
1025                 /* Now we have to find the client_id */
1026                 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1027                     if (wmpc->wmp_client_id == client_id)
1028                         break;
1029                 }
1030
1031                 if (wmpc) {
1032                     if (modify_current_stream(wmpc, ratebuf)) {
1033                         wmpc->switch_pending = 1;
1034                     }
1035                 }
1036             }
1037             
1038             sprintf(msg, "POST command not handled");
1039             goto send_error;
1040         }
1041         if (http_start_receive_data(c) < 0) {
1042             sprintf(msg, "could not open feed");
1043             goto send_error;
1044         }
1045         c->http_error = 0;
1046         c->state = HTTPSTATE_RECEIVE_DATA;
1047         return 0;
1048     }
1049
1050 #ifdef DEBUG_WMP
1051     if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1052         http_log("\nGot request:\n%s\n", c->buffer);
1053     }
1054 #endif
1055
1056     if (c->stream->stream_type == STREAM_TYPE_STATUS)
1057         goto send_stats;
1058
1059     /* open input stream */
1060     if (open_input_stream(c, info) < 0) {
1061         sprintf(msg, "Input stream corresponding to '%s' not found", url);
1062         goto send_error;
1063     }
1064
1065     /* prepare http header */
1066     q = c->buffer;
1067     q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1068     mime_type = c->stream->fmt->mime_type;
1069     if (!mime_type)
1070         mime_type = "application/x-octet_stream";
1071     q += sprintf(q, "Pragma: no-cache\r\n");
1072
1073     /* for asf, we need extra headers */
1074     if (!strcmp(c->stream->fmt->name,"asf")) {
1075         /* Need to allocate a client id */
1076         static int wmp_session;
1077
1078         if (!wmp_session)
1079             wmp_session = time(0) & 0xffffff;
1080
1081         c->wmp_client_id = ++wmp_session;
1082
1083         q += sprintf(q, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c->wmp_client_id);
1084         mime_type = "application/octet-stream"; 
1085     }
1086     q += sprintf(q, "Content-Type: %s\r\n", mime_type);
1087     q += sprintf(q, "\r\n");
1088     
1089     /* prepare output buffer */
1090     c->http_error = 0;
1091     c->buffer_ptr = c->buffer;
1092     c->buffer_end = q;
1093     c->state = HTTPSTATE_SEND_HEADER;
1094     return 0;
1095  send_error:
1096     c->http_error = 404;
1097     q = c->buffer;
1098     q += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
1099     q += sprintf(q, "Content-type: %s\r\n", "text/html");
1100     q += sprintf(q, "\r\n");
1101     q += sprintf(q, "<HTML>\n");
1102     q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1103     q += sprintf(q, "<BODY>%s</BODY>\n", msg);
1104     q += sprintf(q, "</HTML>\n");
1105
1106     /* prepare output buffer */
1107     c->buffer_ptr = c->buffer;
1108     c->buffer_end = q;
1109     c->state = HTTPSTATE_SEND_HEADER;
1110     return 0;
1111  send_stats:
1112     compute_stats(c);
1113     c->http_error = 200; /* horrible : we use this value to avoid
1114                             going to the send data state */
1115     c->state = HTTPSTATE_SEND_HEADER;
1116     return 0;
1117 }
1118
1119 static int fmt_bytecount(char *q, INT64 count)
1120 {
1121     static const char *suffix = " kMGTP";
1122     const char *s;
1123
1124     for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1125     }
1126
1127     return sprintf(q, "%lld%c", count, *s);
1128 }
1129
1130 static void compute_stats(HTTPContext *c)
1131 {
1132     HTTPContext *c1;
1133     FFStream *stream;
1134     char *q, *p;
1135     time_t ti;
1136     int i;
1137     char *new_buffer;
1138
1139     new_buffer = av_malloc(65536);
1140     if (new_buffer) {
1141         av_free(c->buffer);
1142         c->buffer_size = 65536;
1143         c->buffer = new_buffer;
1144         c->buffer_ptr = c->buffer;
1145         c->buffer_end = c->buffer + c->buffer_size;
1146     }
1147
1148     q = c->buffer;
1149     q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1150     q += sprintf(q, "Content-type: %s\r\n", "text/html");
1151     q += sprintf(q, "Pragma: no-cache\r\n");
1152     q += sprintf(q, "\r\n");
1153     
1154     q += sprintf(q, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1155     if (c->stream->feed_filename) {
1156         q += sprintf(q, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1157     }
1158     q += sprintf(q, "</HEAD>\n<BODY>");
1159     q += sprintf(q, "<H1>FFServer Status</H1>\n");
1160     /* format status */
1161     q += sprintf(q, "<H2>Available Streams</H2>\n");
1162     q += sprintf(q, "<TABLE cellspacing=0 cellpadding=4>\n");
1163     q += sprintf(q, "<TR><Th valign=top>Path<th align=left>Served<br>Conns<Th><br>bytes<Th valign=top>Format<Th>Bit rate<br>kbits/s<Th align=left>Video<br>kbits/s<th><br>Codec<Th align=left>Audio<br>kbits/s<th><br>Codec<Th align=left valign=top>Feed\n");
1164     stream = first_stream;
1165     while (stream != NULL) {
1166         char sfilename[1024];
1167         char *eosf;
1168
1169         if (stream->feed != stream) {
1170             pstrcpy(sfilename, sizeof(sfilename) - 1, stream->filename);
1171             eosf = sfilename + strlen(sfilename);
1172             if (eosf - sfilename >= 4) {
1173                 if (strcmp(eosf - 4, ".asf") == 0) {
1174                     strcpy(eosf - 4, ".asx");
1175                 } else if (strcmp(eosf - 3, ".rm") == 0) {
1176                     strcpy(eosf - 3, ".ram");
1177                 }
1178             }
1179             
1180             q += sprintf(q, "<TR><TD><A HREF=\"/%s\">%s</A> ", 
1181                          sfilename, stream->filename);
1182             q += sprintf(q, "<td align=right> %d <td align=right> ",
1183                         stream->conns_served);
1184             q += fmt_bytecount(q, stream->bytes_served);
1185             switch(stream->stream_type) {
1186             case STREAM_TYPE_LIVE:
1187                 {
1188                     int audio_bit_rate = 0;
1189                     int video_bit_rate = 0;
1190                     char *audio_codec_name = "";
1191                     char *video_codec_name = "";
1192                     char *audio_codec_name_extra = "";
1193                     char *video_codec_name_extra = "";
1194
1195                     for(i=0;i<stream->nb_streams;i++) {
1196                         AVStream *st = stream->streams[i];
1197                         AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1198                         switch(st->codec.codec_type) {
1199                         case CODEC_TYPE_AUDIO:
1200                             audio_bit_rate += st->codec.bit_rate;
1201                             if (codec) {
1202                                 if (*audio_codec_name)
1203                                     audio_codec_name_extra = "...";
1204                                 audio_codec_name = codec->name;
1205                             }
1206                             break;
1207                         case CODEC_TYPE_VIDEO:
1208                             video_bit_rate += st->codec.bit_rate;
1209                             if (codec) {
1210                                 if (*video_codec_name)
1211                                     video_codec_name_extra = "...";
1212                                 video_codec_name = codec->name;
1213                             }
1214                             break;
1215                         default:
1216                             av_abort();
1217                         }
1218                     }
1219                     q += sprintf(q, "<TD align=center> %s <TD align=right> %d <TD align=right> %d <TD> %s %s <TD align=right> %d <TD> %s %s", 
1220                                  stream->fmt->name,
1221                                  (audio_bit_rate + video_bit_rate) / 1000,
1222                                  video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1223                                  audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1224                     if (stream->feed) {
1225                         q += sprintf(q, "<TD>%s", stream->feed->filename);
1226                     } else {
1227                         q += sprintf(q, "<TD>%s", stream->feed_filename);
1228                     }
1229                     q += sprintf(q, "\n");
1230                 }
1231                 break;
1232             default:
1233                 q += sprintf(q, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1234                 break;
1235             }
1236         }
1237         stream = stream->next;
1238     }
1239     q += sprintf(q, "</TABLE>\n");
1240
1241     stream = first_stream;
1242     while (stream != NULL) {
1243         if (stream->feed == stream) {
1244             q += sprintf(q, "<h2>Feed %s</h2>", stream->filename);
1245             if (stream->pid) {
1246                 FILE *pid_stat;
1247                 char ps_cmd[64];
1248
1249                 q += sprintf(q, "Running as pid %d.\n", stream->pid);
1250
1251 #ifdef linux
1252                 /* This is somewhat linux specific I guess */
1253                 snprintf(ps_cmd, sizeof(ps_cmd), "ps -o \"%%cpu,bsdtime\" --no-headers %d", stream->pid);
1254
1255                 pid_stat = popen(ps_cmd, "r");
1256                 if (pid_stat) {
1257                     char cpuperc[10];
1258                     char cpuused[64];
1259
1260                     if (fscanf(pid_stat, "%10s %64s", cpuperc, cpuused) == 2) {
1261                         q += sprintf(q, "Currently using %s%% of the cpu. Total time used %s.\n",
1262                             cpuperc, cpuused);
1263                     }
1264                     fclose(pid_stat);
1265                 }
1266 #endif
1267
1268                 q += sprintf(q, "<p>");
1269             }
1270             q += sprintf(q, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
1271
1272             for (i = 0; i < stream->nb_streams; i++) {
1273                 AVStream *st = stream->streams[i];
1274                 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1275                 char *type = "unknown";
1276                 char parameters[64];
1277
1278                 parameters[0] = 0;
1279
1280                 switch(st->codec.codec_type) {
1281                 case CODEC_TYPE_AUDIO:
1282                     type = "audio";
1283                     break;
1284                 case CODEC_TYPE_VIDEO:
1285                     type = "video";
1286                     sprintf(parameters, "%dx%d, q=%d-%d, fps=%d", st->codec.width, st->codec.height,
1287                                 st->codec.qmin, st->codec.qmax, st->codec.frame_rate / FRAME_RATE_BASE);
1288                     break;
1289                 default:
1290                     av_abort();
1291                 }
1292                 q += sprintf(q, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1293                         i, type, st->codec.bit_rate/1000, codec ? codec->name : "", parameters);
1294             }
1295             q += sprintf(q, "</table>\n");
1296
1297         }       
1298         stream = stream->next;
1299     }
1300     
1301 #if 0
1302     {
1303         float avg;
1304         AVCodecContext *enc;
1305         char buf[1024];
1306         
1307         /* feed status */
1308         stream = first_feed;
1309         while (stream != NULL) {
1310             q += sprintf(q, "<H1>Feed '%s'</H1>\n", stream->filename);
1311             q += sprintf(q, "<TABLE>\n");
1312             q += sprintf(q, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1313             for(i=0;i<stream->nb_streams;i++) {
1314                 AVStream *st = stream->streams[i];
1315                 FeedData *fdata = st->priv_data;
1316                 enc = &st->codec;
1317             
1318                 avcodec_string(buf, sizeof(buf), enc);
1319                 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1320                 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1321                     avg /= enc->frame_size;
1322                 q += sprintf(q, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n", 
1323                              buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1324             }
1325             q += sprintf(q, "</TABLE>\n");
1326             stream = stream->next_feed;
1327         }
1328     }
1329 #endif
1330
1331     /* connection status */
1332     q += sprintf(q, "<H2>Connection Status</H2>\n");
1333
1334     q += sprintf(q, "Number of connections: %d / %d<BR>\n",
1335                  nb_connections, nb_max_connections);
1336
1337     q += sprintf(q, "Bandwidth in use: %dk / %dk<BR>\n",
1338                  nb_bandwidth, nb_max_bandwidth);
1339
1340     q += sprintf(q, "<TABLE>\n");
1341     q += sprintf(q, "<TR><Th>#<Th>File<Th>IP<Th>State<Th>Target bits/sec<Th>Actual bits/sec<Th>Bytes transferred\n");
1342     c1 = first_http_ctx;
1343     i = 0;
1344     while (c1 != NULL && q < (char *) c->buffer + c->buffer_size - 2048) {
1345         int bitrate;
1346         int j;
1347
1348         bitrate = 0;
1349         for (j = 0; j < c1->stream->nb_streams; j++) {
1350             if (c1->feed_streams[j] >= 0) {
1351                 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec.bit_rate;
1352             }
1353         }
1354
1355         i++;
1356         p = inet_ntoa(c1->from_addr.sin_addr);
1357         q += sprintf(q, "<TR><TD><B>%d</B><TD>%s%s <TD> %s <TD> %s <td align=right>", 
1358                      i, c1->stream->filename, 
1359                      c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1360                      p, 
1361                      http_state[c1->state]);
1362         q += fmt_bytecount(q, bitrate);
1363         q += sprintf(q, "<td align=right>");
1364         q += fmt_bytecount(q, compute_datarate(&c1->datarate, c1->data_count) * 8);
1365         q += sprintf(q, "<td align=right>");
1366         q += fmt_bytecount(q, c1->data_count);
1367         *q++ = '\n';
1368         c1 = c1->next;
1369     }
1370     q += sprintf(q, "</TABLE>\n");
1371     
1372     /* date */
1373     ti = time(NULL);
1374     p = ctime(&ti);
1375     q += sprintf(q, "<HR size=1 noshade>Generated at %s", p);
1376     q += sprintf(q, "</BODY>\n</HTML>\n");
1377
1378     c->buffer_ptr = c->buffer;
1379     c->buffer_end = q;
1380 }
1381
1382
1383 static void http_write_packet(void *opaque, 
1384                               unsigned char *buf, int size)
1385 {
1386     HTTPContext *c = opaque;
1387
1388     if (c->buffer_ptr == c->buffer_end || !c->buffer_ptr)
1389         c->buffer_ptr = c->buffer_end = c->buffer;
1390
1391     if (c->buffer_end - c->buffer + size > c->buffer_size) {
1392         int new_buffer_size = c->buffer_size * 2;
1393         UINT8 *new_buffer;
1394
1395         if (new_buffer_size <= c->buffer_end - c->buffer + size) {
1396             new_buffer_size = c->buffer_end - c->buffer + size + c->buffer_size;
1397         }
1398
1399         new_buffer = av_malloc(new_buffer_size);
1400         if (new_buffer) {
1401             memcpy(new_buffer, c->buffer, c->buffer_end - c->buffer);
1402             c->buffer_end += (new_buffer - c->buffer);
1403             c->buffer_ptr += (new_buffer - c->buffer);
1404             av_free(c->buffer);
1405             c->buffer = new_buffer;
1406             c->buffer_size = new_buffer_size;
1407         } else {
1408             av_abort();
1409         }
1410     }
1411
1412     memcpy(c->buffer_end, buf, size);
1413     c->buffer_end += size;
1414 }
1415
1416 static int open_input_stream(HTTPContext *c, const char *info)
1417 {
1418     char buf[128];
1419     char input_filename[1024];
1420     AVFormatContext *s;
1421     int buf_size;
1422     INT64 stream_pos;
1423
1424     /* find file name */
1425     if (c->stream->feed) {
1426         strcpy(input_filename, c->stream->feed->feed_filename);
1427         buf_size = FFM_PACKET_SIZE;
1428         /* compute position (absolute time) */
1429         if (find_info_tag(buf, sizeof(buf), "date", info)) {
1430             stream_pos = parse_date(buf, 0);
1431         } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1432             int prebuffer = strtol(buf, 0, 10);
1433             stream_pos = gettime() - prebuffer * 1000000;
1434         } else {
1435             stream_pos = gettime() - c->stream->prebuffer * 1000;
1436         }
1437     } else {
1438         strcpy(input_filename, c->stream->feed_filename);
1439         buf_size = 0;
1440         /* compute position (relative time) */
1441         if (find_info_tag(buf, sizeof(buf), "date", info)) {
1442             stream_pos = parse_date(buf, 1);
1443         } else {
1444             stream_pos = 0;
1445         }
1446     }
1447     if (input_filename[0] == '\0')
1448         return -1;
1449
1450     /* open stream */
1451     if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0)
1452         return -1;
1453     c->fmt_in = s;
1454
1455     if (c->fmt_in->iformat->read_seek) {
1456         c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
1457     }
1458     
1459     //    printf("stream %s opened pos=%0.6f\n", input_filename, stream_pos / 1000000.0);
1460     return 0;
1461 }
1462
1463 static int http_prepare_data(HTTPContext *c)
1464 {
1465     int i;
1466
1467     switch(c->state) {
1468     case HTTPSTATE_SEND_DATA_HEADER:
1469         memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
1470         pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author), c->stream->author);
1471         pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment), c->stream->comment);
1472         pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright), c->stream->copyright);
1473         pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title), c->stream->title);
1474
1475         if (c->stream->feed) {
1476             /* open output stream by using specified codecs */
1477             c->fmt_ctx.oformat = c->stream->fmt;
1478             c->fmt_ctx.nb_streams = c->stream->nb_streams;
1479             for(i=0;i<c->fmt_ctx.nb_streams;i++) {
1480                 AVStream *st;
1481                 st = av_mallocz(sizeof(AVStream));
1482                 c->fmt_ctx.streams[i] = st;
1483                 if (c->stream->feed == c->stream)
1484                     memcpy(st, c->stream->streams[i], sizeof(AVStream));
1485                 else
1486                     memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]], sizeof(AVStream));
1487
1488                 st->codec.frame_number = 0; /* XXX: should be done in
1489                                                AVStream, not in codec */
1490             }
1491             c->got_key_frame = 0;
1492         } else {
1493             /* open output stream by using codecs in specified file */
1494             c->fmt_ctx.oformat = c->stream->fmt;
1495             c->fmt_ctx.nb_streams = c->fmt_in->nb_streams;
1496             for(i=0;i<c->fmt_ctx.nb_streams;i++) {
1497                 AVStream *st;
1498                 st = av_mallocz(sizeof(AVStream));
1499                 c->fmt_ctx.streams[i] = st;
1500                 memcpy(st, c->fmt_in->streams[i], sizeof(AVStream));
1501                 st->codec.frame_number = 0; /* XXX: should be done in
1502                                                AVStream, not in codec */
1503             }
1504             c->got_key_frame = 0;
1505         }
1506         init_put_byte(&c->fmt_ctx.pb, c->pbuffer, c->pbuffer_size,
1507                       1, c, NULL, http_write_packet, NULL);
1508         c->fmt_ctx.pb.is_streamed = 1;
1509         /* prepare header */
1510         av_write_header(&c->fmt_ctx);
1511         c->state = HTTPSTATE_SEND_DATA;
1512         c->last_packet_sent = 0;
1513         break;
1514     case HTTPSTATE_SEND_DATA:
1515         /* find a new packet */
1516 #if 0
1517         fifo_total_size = http_fifo_write_count - c->last_http_fifo_write_count;
1518         if (fifo_total_size >= ((3 * FIFO_MAX_SIZE) / 4)) {
1519             /* overflow : resync. We suppose that wptr is at this
1520                point a pointer to a valid packet */
1521             c->rptr = http_fifo.wptr;
1522             c->got_key_frame = 0;
1523         }
1524         
1525         start_rptr = c->rptr;
1526         if (fifo_read(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &c->rptr) < 0)
1527             return 0;
1528         payload_size = ntohs(hdr.payload_size);
1529         payload = av_malloc(payload_size);
1530         if (fifo_read(&http_fifo, payload, payload_size, &c->rptr) < 0) {
1531             /* cannot read all the payload */
1532             av_free(payload);
1533             c->rptr = start_rptr;
1534             return 0;
1535         }
1536         
1537         c->last_http_fifo_write_count = http_fifo_write_count - 
1538             fifo_size(&http_fifo, c->rptr);
1539         
1540         if (c->stream->stream_type != STREAM_TYPE_MASTER) {
1541             /* test if the packet can be handled by this format */
1542             ret = 0;
1543             for(i=0;i<c->fmt_ctx.nb_streams;i++) {
1544                 AVStream *st = c->fmt_ctx.streams[i];
1545                 if (test_header(&hdr, &st->codec)) {
1546                     /* only begin sending when got a key frame */
1547                     if (st->codec.key_frame)
1548                         c->got_key_frame |= 1 << i;
1549                     if (c->got_key_frame & (1 << i)) {
1550                         ret = c->fmt_ctx.format->write_packet(&c->fmt_ctx, i,
1551                                                                    payload, payload_size);
1552                     }
1553                     break;
1554                 }
1555             }
1556             if (ret) {
1557                 /* must send trailer now */
1558                 c->state = HTTPSTATE_SEND_DATA_TRAILER;
1559             }
1560         } else {
1561             /* master case : send everything */
1562             char *q;
1563             q = c->buffer;
1564             memcpy(q, &hdr, sizeof(hdr));
1565             q += sizeof(hdr);
1566             memcpy(q, payload, payload_size);
1567             q += payload_size;
1568             c->buffer_ptr = c->buffer;
1569             c->buffer_end = q;
1570         }
1571         av_free(payload);
1572 #endif
1573         {
1574             AVPacket pkt;
1575
1576             /* read a packet from the input stream */
1577             if (c->stream->feed) {
1578                 ffm_set_write_index(c->fmt_in, 
1579                                     c->stream->feed->feed_write_index,
1580                                     c->stream->feed->feed_size);
1581             }
1582
1583             if (c->stream->max_time && 
1584                 c->stream->max_time + c->start_time - cur_time < 0) {
1585                 /* We have timed out */
1586                 c->state = HTTPSTATE_SEND_DATA_TRAILER;
1587             } else if (av_read_packet(c->fmt_in, &pkt) < 0) {
1588                 if (c->stream->feed && c->stream->feed->feed_opened) {
1589                     /* if coming from feed, it means we reached the end of the
1590                        ffm file, so must wait for more data */
1591                     c->state = HTTPSTATE_WAIT_FEED;
1592                     return 1; /* state changed */
1593                 } else {
1594                     /* must send trailer now because eof or error */
1595                     c->state = HTTPSTATE_SEND_DATA_TRAILER;
1596                 }
1597             } else {
1598                 /* send it to the appropriate stream */
1599                 if (c->stream->feed) {
1600                     /* if coming from a feed, select the right stream */
1601                     if (c->switch_pending) {
1602                         c->switch_pending = 0;
1603                         for(i=0;i<c->stream->nb_streams;i++) {
1604                             if (c->switch_feed_streams[i] == pkt.stream_index) {
1605                                 if (pkt.flags & PKT_FLAG_KEY) {
1606                                     do_switch_stream(c, i);
1607                                 }
1608                             }
1609                             if (c->switch_feed_streams[i] >= 0) {
1610                                 c->switch_pending = 1;
1611                             }
1612                         }
1613                     }
1614                     for(i=0;i<c->stream->nb_streams;i++) {
1615                         if (c->feed_streams[i] == pkt.stream_index) {
1616                             pkt.stream_index = i;
1617                             if (pkt.flags & PKT_FLAG_KEY) {
1618                                 c->got_key_frame |= 1 << i;
1619                             }
1620                             /* See if we have all the key frames, then 
1621                              * we start to send. This logic is not quite
1622                              * right, but it works for the case of a 
1623                              * single video stream with one or more
1624                              * audio streams (for which every frame is 
1625                              * typically a key frame). 
1626                              */
1627                             if (!c->stream->send_on_key || ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
1628                                 goto send_it;
1629                             }
1630                         }
1631                     }
1632                 } else {
1633                     AVCodecContext *codec;
1634                 send_it:
1635                     /* Fudge here */
1636                     codec = &c->fmt_ctx.streams[pkt.stream_index]->codec;
1637
1638                     codec->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
1639
1640 #ifdef PJSG
1641                     if (codec->codec_type == CODEC_TYPE_AUDIO) {
1642                         codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
1643                         /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
1644                     }
1645 #endif
1646
1647                     if (av_write_packet(&c->fmt_ctx, &pkt, 0))
1648                         c->state = HTTPSTATE_SEND_DATA_TRAILER;
1649
1650                     codec->frame_number++;
1651                 }
1652
1653                 av_free_packet(&pkt);
1654             }
1655         }
1656         break;
1657     default:
1658     case HTTPSTATE_SEND_DATA_TRAILER:
1659         /* last packet test ? */
1660         if (c->last_packet_sent)
1661             return -1;
1662         /* prepare header */
1663         av_write_trailer(&c->fmt_ctx);
1664         c->last_packet_sent = 1;
1665         break;
1666     }
1667     return 0;
1668 }
1669
1670 /* should convert the format at the same time */
1671 static int http_send_data(HTTPContext *c)
1672 {
1673     int len, ret;
1674
1675     while (c->buffer_ptr >= c->buffer_end) {
1676         ret = http_prepare_data(c);
1677         if (ret < 0)
1678             return -1;
1679         else if (ret == 0) {
1680             continue;
1681         } else {
1682             /* state change requested */
1683             return 0;
1684         }
1685     }
1686
1687     if (c->buffer_end > c->buffer_ptr) {
1688         len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
1689         if (len < 0) {
1690             if (errno != EAGAIN && errno != EINTR) {
1691                 /* error : close connection */
1692                 return -1;
1693             }
1694         } else {
1695             c->buffer_ptr += len;
1696             c->data_count += len;
1697             update_datarate(&c->datarate, c->data_count);
1698             if (c->stream)
1699                 c->stream->bytes_served += len;
1700         }
1701     }
1702     return 0;
1703 }
1704
1705 static int http_start_receive_data(HTTPContext *c)
1706 {
1707     int fd;
1708
1709     if (c->stream->feed_opened)
1710         return -1;
1711
1712     /* open feed */
1713     fd = open(c->stream->feed_filename, O_RDWR);
1714     if (fd < 0)
1715         return -1;
1716     c->feed_fd = fd;
1717     
1718     c->stream->feed_write_index = ffm_read_write_index(fd);
1719     c->stream->feed_size = lseek(fd, 0, SEEK_END);
1720     lseek(fd, 0, SEEK_SET);
1721
1722     /* init buffer input */
1723     c->buffer_ptr = c->buffer;
1724     c->buffer_end = c->buffer + FFM_PACKET_SIZE;
1725     c->stream->feed_opened = 1;
1726     return 0;
1727 }
1728     
1729 static int http_receive_data(HTTPContext *c)
1730 {
1731     HTTPContext *c1;
1732
1733     if (c->buffer_end > c->buffer_ptr) {
1734         int len;
1735
1736         len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
1737         if (len < 0) {
1738             if (errno != EAGAIN && errno != EINTR) {
1739                 /* error : close connection */
1740                 goto fail;
1741             }
1742         } else if (len == 0) {
1743             /* end of connection : close it */
1744             goto fail;
1745         } else {
1746             c->buffer_ptr += len;
1747             c->data_count += len;
1748             update_datarate(&c->datarate, c->data_count);
1749         }
1750     }
1751
1752     if (c->buffer_ptr >= c->buffer_end) {
1753         FFStream *feed = c->stream;
1754         /* a packet has been received : write it in the store, except
1755            if header */
1756         if (c->data_count > FFM_PACKET_SIZE) {
1757             
1758             //            printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
1759             /* XXX: use llseek or url_seek */
1760             lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
1761             write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
1762             
1763             feed->feed_write_index += FFM_PACKET_SIZE;
1764             /* update file size */
1765             if (feed->feed_write_index > c->stream->feed_size)
1766                 feed->feed_size = feed->feed_write_index;
1767
1768             /* handle wrap around if max file size reached */
1769             if (feed->feed_write_index >= c->stream->feed_max_size)
1770                 feed->feed_write_index = FFM_PACKET_SIZE;
1771
1772             /* write index */
1773             ffm_write_write_index(c->feed_fd, feed->feed_write_index);
1774
1775             /* wake up any waiting connections */
1776             for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
1777                 if (c1->state == HTTPSTATE_WAIT_FEED && 
1778                     c1->stream->feed == c->stream->feed) {
1779                     c1->state = HTTPSTATE_SEND_DATA;
1780                 }
1781             }
1782         } else {
1783             /* We have a header in our hands that contains useful data */
1784             AVFormatContext s;
1785             AVInputFormat *fmt_in;
1786             ByteIOContext *pb = &s.pb;
1787             int i;
1788
1789             memset(&s, 0, sizeof(s));
1790
1791             url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
1792             pb->buf_end = c->buffer_end;        /* ?? */
1793             pb->is_streamed = 1;
1794
1795             /* use feed output format name to find corresponding input format */
1796             fmt_in = av_find_input_format(feed->fmt->name);
1797             if (!fmt_in)
1798                 goto fail;
1799
1800             s.priv_data = av_mallocz(fmt_in->priv_data_size);
1801             if (!s.priv_data)
1802                 goto fail;
1803
1804             if (fmt_in->read_header(&s, 0) < 0) {
1805                 av_freep(&s.priv_data);
1806                 goto fail;
1807             }
1808
1809             /* Now we have the actual streams */
1810             if (s.nb_streams != feed->nb_streams) {
1811                 av_freep(&s.priv_data);
1812                 goto fail;
1813             }
1814             for (i = 0; i < s.nb_streams; i++) {
1815                 memcpy(&feed->streams[i]->codec, 
1816                        &s.streams[i]->codec, sizeof(AVCodecContext));
1817             } 
1818             av_freep(&s.priv_data);
1819         }
1820         c->buffer_ptr = c->buffer;
1821     }
1822
1823     return 0;
1824  fail:
1825     c->stream->feed_opened = 0;
1826     close(c->feed_fd);
1827     return -1;
1828 }
1829
1830 /* return the stream number in the feed */
1831 int add_av_stream(FFStream *feed,
1832                   AVStream *st)
1833 {
1834     AVStream *fst;
1835     AVCodecContext *av, *av1;
1836     int i;
1837
1838     av = &st->codec;
1839     for(i=0;i<feed->nb_streams;i++) {
1840         st = feed->streams[i];
1841         av1 = &st->codec;
1842         if (av1->codec_id == av->codec_id &&
1843             av1->codec_type == av->codec_type &&
1844             av1->bit_rate == av->bit_rate) {
1845
1846             switch(av->codec_type) {
1847             case CODEC_TYPE_AUDIO:
1848                 if (av1->channels == av->channels &&
1849                     av1->sample_rate == av->sample_rate)
1850                     goto found;
1851                 break;
1852             case CODEC_TYPE_VIDEO:
1853                 if (av1->width == av->width &&
1854                     av1->height == av->height &&
1855                     av1->frame_rate == av->frame_rate &&
1856                     av1->gop_size == av->gop_size)
1857                     goto found;
1858                 break;
1859             default:
1860                 av_abort();
1861             }
1862         }
1863     }
1864     
1865     fst = av_mallocz(sizeof(AVStream));
1866     if (!fst)
1867         return -1;
1868     fst->priv_data = av_mallocz(sizeof(FeedData));
1869     memcpy(&fst->codec, av, sizeof(AVCodecContext));
1870     feed->streams[feed->nb_streams++] = fst;
1871     return feed->nb_streams - 1;
1872  found:
1873     return i;
1874 }
1875
1876 /* compute the needed AVStream for each feed */
1877 void build_feed_streams(void)
1878 {
1879     FFStream *stream, *feed;
1880     int i;
1881
1882     /* gather all streams */
1883     for(stream = first_stream; stream != NULL; stream = stream->next) {
1884         feed = stream->feed;
1885         if (feed) {
1886             if (!stream->is_feed) {
1887                 for(i=0;i<stream->nb_streams;i++) {
1888                     stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
1889                 }
1890             }
1891         }
1892     }
1893
1894     /* gather all streams */
1895     for(stream = first_stream; stream != NULL; stream = stream->next) {
1896         feed = stream->feed;
1897         if (feed) {
1898             if (stream->is_feed) {
1899                 for(i=0;i<stream->nb_streams;i++) {
1900                     stream->feed_streams[i] = i;
1901                 }
1902             }
1903         }
1904     }
1905
1906     /* create feed files if needed */
1907     for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
1908         int fd;
1909
1910         if (!url_exist(feed->feed_filename)) {
1911             AVFormatContext s1, *s = &s1;
1912
1913             /* only write the header of the ffm file */
1914             if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
1915                 fprintf(stderr, "Could not open output feed file '%s'\n",
1916                         feed->feed_filename);
1917                 exit(1);
1918             }
1919             s->oformat = feed->fmt;
1920             s->nb_streams = feed->nb_streams;
1921             for(i=0;i<s->nb_streams;i++) {
1922                 AVStream *st;
1923                 st = feed->streams[i];
1924                 s->streams[i] = st;
1925             }
1926             av_write_header(s);
1927             /* XXX: need better api */
1928             av_freep(&s->priv_data);
1929             url_fclose(&s->pb);
1930         }
1931         /* get feed size and write index */
1932         fd = open(feed->feed_filename, O_RDONLY);
1933         if (fd < 0) {
1934             fprintf(stderr, "Could not open output feed file '%s'\n",
1935                     feed->feed_filename);
1936             exit(1);
1937         }
1938
1939         feed->feed_write_index = ffm_read_write_index(fd);
1940         feed->feed_size = lseek(fd, 0, SEEK_END);
1941         /* ensure that we do not wrap before the end of file */
1942         if (feed->feed_max_size < feed->feed_size)
1943             feed->feed_max_size = feed->feed_size;
1944
1945         close(fd);
1946     }
1947 }
1948
1949 static void get_arg(char *buf, int buf_size, const char **pp)
1950 {
1951     const char *p;
1952     char *q;
1953     int quote;
1954
1955     p = *pp;
1956     while (isspace(*p)) p++;
1957     q = buf;
1958     quote = 0;
1959     if (*p == '\"' || *p == '\'')
1960         quote = *p++;
1961     for(;;) {
1962         if (quote) {
1963             if (*p == quote)
1964                 break;
1965         } else {
1966             if (isspace(*p))
1967                 break;
1968         }
1969         if (*p == '\0')
1970             break;
1971         if ((q - buf) < buf_size - 1)
1972             *q++ = *p;
1973         p++;
1974     }
1975     *q = '\0';
1976     if (quote && *p == quote)
1977         p++;
1978     *pp = p;
1979 }
1980
1981 /* add a codec and set the default parameters */
1982 void add_codec(FFStream *stream, AVCodecContext *av)
1983 {
1984     AVStream *st;
1985
1986     /* compute default parameters */
1987     switch(av->codec_type) {
1988     case CODEC_TYPE_AUDIO:
1989         if (av->bit_rate == 0)
1990             av->bit_rate = 64000;
1991         if (av->sample_rate == 0)
1992             av->sample_rate = 22050;
1993         if (av->channels == 0)
1994             av->channels = 1;
1995         break;
1996     case CODEC_TYPE_VIDEO:
1997         if (av->bit_rate == 0)
1998             av->bit_rate = 64000;
1999         if (av->frame_rate == 0)
2000             av->frame_rate = 5 * FRAME_RATE_BASE;
2001         if (av->width == 0 || av->height == 0) {
2002             av->width = 160;
2003             av->height = 128;
2004         }
2005         /* Bitrate tolerance is less for streaming */
2006         if (av->bit_rate_tolerance == 0)
2007             av->bit_rate_tolerance = av->bit_rate / 4;
2008         if (av->qmin == 0)
2009             av->qmin = 3;
2010         if (av->qmax == 0)
2011             av->qmax = 31;
2012         if (av->max_qdiff == 0)
2013             av->max_qdiff = 3;
2014         av->qcompress = 0.5;
2015         av->qblur = 0.5;
2016
2017         break;
2018     default:
2019         av_abort();
2020     }
2021
2022     st = av_mallocz(sizeof(AVStream));
2023     if (!st)
2024         return;
2025     stream->streams[stream->nb_streams++] = st;
2026     memcpy(&st->codec, av, sizeof(AVCodecContext));
2027 }
2028
2029 int opt_audio_codec(const char *arg)
2030 {
2031     AVCodec *p;
2032
2033     p = first_avcodec;
2034     while (p) {
2035         if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
2036             break;
2037         p = p->next;
2038     }
2039     if (p == NULL) {
2040         return CODEC_ID_NONE;
2041     }
2042
2043     return p->id;
2044 }
2045
2046 int opt_video_codec(const char *arg)
2047 {
2048     AVCodec *p;
2049
2050     p = first_avcodec;
2051     while (p) {
2052         if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
2053             break;
2054         p = p->next;
2055     }
2056     if (p == NULL) {
2057         return CODEC_ID_NONE;
2058     }
2059
2060     return p->id;
2061 }
2062
2063 int parse_ffconfig(const char *filename)
2064 {
2065     FILE *f;
2066     char line[1024];
2067     char cmd[64];
2068     char arg[1024];
2069     const char *p;
2070     int val, errors, line_num;
2071     FFStream **last_stream, *stream, *redirect;
2072     FFStream **last_feed, *feed;
2073     AVCodecContext audio_enc, video_enc;
2074     int audio_id, video_id;
2075
2076     f = fopen(filename, "r");
2077     if (!f) {
2078         perror(filename);
2079         return -1;
2080     }
2081     
2082     errors = 0;
2083     line_num = 0;
2084     first_stream = NULL;
2085     last_stream = &first_stream;
2086     first_feed = NULL;
2087     last_feed = &first_feed;
2088     stream = NULL;
2089     feed = NULL;
2090     redirect = NULL;
2091     audio_id = CODEC_ID_NONE;
2092     video_id = CODEC_ID_NONE;
2093     for(;;) {
2094         if (fgets(line, sizeof(line), f) == NULL)
2095             break;
2096         line_num++;
2097         p = line;
2098         while (isspace(*p)) 
2099             p++;
2100         if (*p == '\0' || *p == '#')
2101             continue;
2102
2103         get_arg(cmd, sizeof(cmd), &p);
2104         
2105         if (!strcasecmp(cmd, "Port")) {
2106             get_arg(arg, sizeof(arg), &p);
2107             my_addr.sin_port = htons (atoi(arg));
2108         } else if (!strcasecmp(cmd, "BindAddress")) {
2109             get_arg(arg, sizeof(arg), &p);
2110             if (!inet_aton(arg, &my_addr.sin_addr)) {
2111                 fprintf(stderr, "%s:%d: Invalid IP address: %s\n", 
2112                         filename, line_num, arg);
2113                 errors++;
2114             }
2115         } else if (!strcasecmp(cmd, "MaxClients")) {
2116             get_arg(arg, sizeof(arg), &p);
2117             val = atoi(arg);
2118             if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
2119                 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n", 
2120                         filename, line_num, arg);
2121                 errors++;
2122             } else {
2123                 nb_max_connections = val;
2124             }
2125         } else if (!strcasecmp(cmd, "MaxBandwidth")) {
2126             get_arg(arg, sizeof(arg), &p);
2127             val = atoi(arg);
2128             if (val < 10 || val > 100000) {
2129                 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n", 
2130                         filename, line_num, arg);
2131                 errors++;
2132             } else {
2133                 nb_max_bandwidth = val;
2134             }
2135         } else if (!strcasecmp(cmd, "CustomLog")) {
2136             get_arg(logfilename, sizeof(logfilename), &p);
2137         } else if (!strcasecmp(cmd, "<Feed")) {
2138             /*********************************************/
2139             /* Feed related options */
2140             char *q;
2141             if (stream || feed) {
2142                 fprintf(stderr, "%s:%d: Already in a tag\n",
2143                         filename, line_num);
2144             } else {
2145                 feed = av_mallocz(sizeof(FFStream));
2146                 /* add in stream list */
2147                 *last_stream = feed;
2148                 last_stream = &feed->next;
2149                 /* add in feed list */
2150                 *last_feed = feed;
2151                 last_feed = &feed->next_feed;
2152                 
2153                 get_arg(feed->filename, sizeof(feed->filename), &p);
2154                 q = strrchr(feed->filename, '>');
2155                 if (*q)
2156                     *q = '\0';
2157                 feed->fmt = guess_format("ffm", NULL, NULL);
2158                 /* defaut feed file */
2159                 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
2160                          "/tmp/%s.ffm", feed->filename);
2161                 feed->feed_max_size = 5 * 1024 * 1024;
2162                 feed->is_feed = 1;
2163                 feed->feed = feed; /* self feeding :-) */
2164             }
2165         } else if (!strcasecmp(cmd, "Launch")) {
2166             if (feed) {
2167                 int i;
2168
2169                 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
2170
2171                 feed->child_argv[0] = av_malloc(7);
2172                 strcpy(feed->child_argv[0], "ffmpeg");
2173
2174                 for (i = 1; i < 62; i++) {
2175                     char argbuf[256];
2176
2177                     get_arg(argbuf, sizeof(argbuf), &p);
2178                     if (!argbuf[0])
2179                         break;
2180
2181                     feed->child_argv[i] = av_malloc(strlen(argbuf + 1));
2182                     strcpy(feed->child_argv[i], argbuf);
2183                 }
2184
2185                 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
2186
2187                 snprintf(feed->child_argv[i], 256, "http://127.0.0.1:%d/%s", 
2188                     ntohs(my_addr.sin_port), feed->filename);
2189             }
2190         } else if (!strcasecmp(cmd, "File")) {
2191             if (feed) {
2192                 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
2193             } else if (stream) {
2194                 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
2195             }
2196         } else if (!strcasecmp(cmd, "FileMaxSize")) {
2197             if (feed) {
2198                 const char *p1;
2199                 double fsize;
2200
2201                 get_arg(arg, sizeof(arg), &p);
2202                 p1 = arg;
2203                 fsize = strtod(p1, (char **)&p1);
2204                 switch(toupper(*p1)) {
2205                 case 'K':
2206                     fsize *= 1024;
2207                     break;
2208                 case 'M':
2209                     fsize *= 1024 * 1024;
2210                     break;
2211                 case 'G':
2212                     fsize *= 1024 * 1024 * 1024;
2213                     break;
2214                 }
2215                 feed->feed_max_size = (INT64)fsize;
2216             }
2217         } else if (!strcasecmp(cmd, "</Feed>")) {
2218             if (!feed) {
2219                 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
2220                         filename, line_num);
2221                 errors++;
2222             } else {
2223                 /* Make sure that we start out clean */
2224                 if (unlink(feed->feed_filename) < 0 
2225                     && errno != ENOENT) {
2226                     fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
2227                         filename, line_num, feed->feed_filename, strerror(errno));
2228                     errors++;
2229                 }
2230             }
2231             feed = NULL;
2232         } else if (!strcasecmp(cmd, "<Stream")) {
2233             /*********************************************/
2234             /* Stream related options */
2235             char *q;
2236             if (stream || feed) {
2237                 fprintf(stderr, "%s:%d: Already in a tag\n",
2238                         filename, line_num);
2239             } else {
2240                 stream = av_mallocz(sizeof(FFStream));
2241                 *last_stream = stream;
2242                 last_stream = &stream->next;
2243
2244                 get_arg(stream->filename, sizeof(stream->filename), &p);
2245                 q = strrchr(stream->filename, '>');
2246                 if (*q)
2247                     *q = '\0';
2248                 stream->fmt = guess_format(NULL, stream->filename, NULL);
2249                 memset(&audio_enc, 0, sizeof(AVCodecContext));
2250                 memset(&video_enc, 0, sizeof(AVCodecContext));
2251                 audio_id = CODEC_ID_NONE;
2252                 video_id = CODEC_ID_NONE;
2253                 if (stream->fmt) {
2254                     audio_id = stream->fmt->audio_codec;
2255                     video_id = stream->fmt->video_codec;
2256                 }
2257             }
2258         } else if (!strcasecmp(cmd, "Feed")) {
2259             get_arg(arg, sizeof(arg), &p);
2260             if (stream) {
2261                 FFStream *sfeed;
2262                 
2263                 sfeed = first_feed;
2264                 while (sfeed != NULL) {
2265                     if (!strcmp(sfeed->filename, arg))
2266                         break;
2267                     sfeed = sfeed->next_feed;
2268                 }
2269                 if (!sfeed) {
2270                     fprintf(stderr, "%s:%d: feed '%s' not defined\n",
2271                             filename, line_num, arg);
2272                 } else {
2273                     stream->feed = sfeed;
2274                 }
2275             }
2276         } else if (!strcasecmp(cmd, "Format")) {
2277             get_arg(arg, sizeof(arg), &p);
2278             if (!strcmp(arg, "status")) {
2279                 stream->stream_type = STREAM_TYPE_STATUS;
2280                 stream->fmt = NULL;
2281             } else {
2282                 stream->stream_type = STREAM_TYPE_LIVE;
2283                 /* jpeg cannot be used here, so use single frame jpeg */
2284                 if (!strcmp(arg, "jpeg"))
2285                     strcpy(arg, "singlejpeg");
2286                 stream->fmt = guess_format(arg, NULL, NULL);
2287                 if (!stream->fmt) {
2288                     fprintf(stderr, "%s:%d: Unknown Format: %s\n", 
2289                             filename, line_num, arg);
2290                     errors++;
2291                 }
2292             }
2293             if (stream->fmt) {
2294                 audio_id = stream->fmt->audio_codec;
2295                 video_id = stream->fmt->video_codec;
2296             }
2297         } else if (!strcasecmp(cmd, "FaviconURL")) {
2298             if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
2299                 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
2300             } else {
2301                 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n", 
2302                             filename, line_num);
2303                 errors++;
2304             }
2305         } else if (!strcasecmp(cmd, "Author")) {
2306             if (stream) {
2307                 get_arg(stream->author, sizeof(stream->author), &p);
2308             }
2309         } else if (!strcasecmp(cmd, "Comment")) {
2310             if (stream) {
2311                 get_arg(stream->comment, sizeof(stream->comment), &p);
2312             }
2313         } else if (!strcasecmp(cmd, "Copyright")) {
2314             if (stream) {
2315                 get_arg(stream->copyright, sizeof(stream->copyright), &p);
2316             }
2317         } else if (!strcasecmp(cmd, "Title")) {
2318             if (stream) {
2319                 get_arg(stream->title, sizeof(stream->title), &p);
2320             }
2321         } else if (!strcasecmp(cmd, "Preroll")) {
2322             get_arg(arg, sizeof(arg), &p);
2323             if (stream) {
2324                 stream->prebuffer = atoi(arg) * 1000;
2325             }
2326         } else if (!strcasecmp(cmd, "StartSendOnKey")) {
2327             if (stream) {
2328                 stream->send_on_key = 1;
2329             }
2330         } else if (!strcasecmp(cmd, "AudioCodec")) {
2331             get_arg(arg, sizeof(arg), &p);
2332             audio_id = opt_audio_codec(arg);
2333             if (audio_id == CODEC_ID_NONE) {
2334                 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n", 
2335                         filename, line_num, arg);
2336                 errors++;
2337             }
2338         } else if (!strcasecmp(cmd, "VideoCodec")) {
2339             get_arg(arg, sizeof(arg), &p);
2340             video_id = opt_video_codec(arg);
2341             if (video_id == CODEC_ID_NONE) {
2342                 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n", 
2343                         filename, line_num, arg);
2344                 errors++;
2345             }
2346         } else if (!strcasecmp(cmd, "MaxTime")) {
2347             get_arg(arg, sizeof(arg), &p);
2348             if (stream) {
2349                 stream->max_time = atoi(arg) * 1000;
2350             }
2351         } else if (!strcasecmp(cmd, "AudioBitRate")) {
2352             get_arg(arg, sizeof(arg), &p);
2353             if (stream) {
2354                 audio_enc.bit_rate = atoi(arg) * 1000;
2355             }
2356         } else if (!strcasecmp(cmd, "AudioChannels")) {
2357             get_arg(arg, sizeof(arg), &p);
2358             if (stream) {
2359                 audio_enc.channels = atoi(arg);
2360             }
2361         } else if (!strcasecmp(cmd, "AudioSampleRate")) {
2362             get_arg(arg, sizeof(arg), &p);
2363             if (stream) {
2364                 audio_enc.sample_rate = atoi(arg);
2365             }
2366         } else if (!strcasecmp(cmd, "VideoBitRate")) {
2367             get_arg(arg, sizeof(arg), &p);
2368             if (stream) {
2369                 video_enc.bit_rate = atoi(arg) * 1000;
2370             }
2371         } else if (!strcasecmp(cmd, "VideoSize")) {
2372             get_arg(arg, sizeof(arg), &p);
2373             if (stream) {
2374                 parse_image_size(&video_enc.width, &video_enc.height, arg);
2375                 if ((video_enc.width % 16) != 0 ||
2376                     (video_enc.height % 16) != 0) {
2377                     fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
2378                             filename, line_num);
2379                     errors++;
2380                 }
2381             }
2382         } else if (!strcasecmp(cmd, "VideoFrameRate")) {
2383             get_arg(arg, sizeof(arg), &p);
2384             if (stream) {
2385                 video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
2386             }
2387         } else if (!strcasecmp(cmd, "VideoGopSize")) {
2388             get_arg(arg, sizeof(arg), &p);
2389             if (stream) {
2390                 video_enc.gop_size = atoi(arg);
2391             }
2392         } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
2393             if (stream) {
2394                 video_enc.gop_size = 1;
2395             }
2396         } else if (!strcasecmp(cmd, "VideoHighQuality")) {
2397             if (stream) {
2398                 video_enc.flags |= CODEC_FLAG_HQ;
2399             }
2400         } else if (!strcasecmp(cmd, "VideoQDiff")) {
2401             if (stream) {
2402                 video_enc.max_qdiff = atoi(arg);
2403                 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
2404                     fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
2405                             filename, line_num);
2406                     errors++;
2407                 }
2408             }
2409         } else if (!strcasecmp(cmd, "VideoQMax")) {
2410             if (stream) {
2411                 video_enc.qmax = atoi(arg);
2412                 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
2413                     fprintf(stderr, "%s:%d: VideoQMax out of range\n",
2414                             filename, line_num);
2415                     errors++;
2416                 }
2417             }
2418         } else if (!strcasecmp(cmd, "VideoQMin")) {
2419             if (stream) {
2420                 video_enc.qmin = atoi(arg);
2421                 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
2422                     fprintf(stderr, "%s:%d: VideoQMin out of range\n",
2423                             filename, line_num);
2424                     errors++;
2425                 }
2426             }
2427         } else if (!strcasecmp(cmd, "NoVideo")) {
2428             video_id = CODEC_ID_NONE;
2429         } else if (!strcasecmp(cmd, "NoAudio")) {
2430             audio_id = CODEC_ID_NONE;
2431         } else if (!strcasecmp(cmd, "</Stream>")) {
2432             if (!stream) {
2433                 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
2434                         filename, line_num);
2435                 errors++;
2436             }
2437             if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
2438                 if (audio_id != CODEC_ID_NONE) {
2439                     audio_enc.codec_type = CODEC_TYPE_AUDIO;
2440                     audio_enc.codec_id = audio_id;
2441                     add_codec(stream, &audio_enc);
2442                 }
2443                 if (video_id != CODEC_ID_NONE) {
2444                     video_enc.codec_type = CODEC_TYPE_VIDEO;
2445                     video_enc.codec_id = video_id;
2446                     add_codec(stream, &video_enc);
2447                 }
2448             }
2449             stream = NULL;
2450         } else if (!strcasecmp(cmd, "<Redirect")) {
2451             /*********************************************/
2452             char *q;
2453             if (stream || feed || redirect) {
2454                 fprintf(stderr, "%s:%d: Already in a tag\n",
2455                         filename, line_num);
2456                 errors++;
2457             } else {
2458                 redirect = av_mallocz(sizeof(FFStream));
2459                 *last_stream = redirect;
2460                 last_stream = &redirect->next;
2461
2462                 get_arg(redirect->filename, sizeof(redirect->filename), &p);
2463                 q = strrchr(redirect->filename, '>');
2464                 if (*q)
2465                     *q = '\0';
2466                 redirect->stream_type = STREAM_TYPE_REDIRECT;
2467             }
2468         } else if (!strcasecmp(cmd, "URL")) {
2469             if (redirect) {
2470                 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
2471             }
2472         } else if (!strcasecmp(cmd, "</Redirect>")) {
2473             if (!redirect) {
2474                 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
2475                         filename, line_num);
2476                 errors++;
2477             }
2478             if (!redirect->feed_filename[0]) {
2479                 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
2480                         filename, line_num);
2481                 errors++;
2482             }
2483             redirect = NULL;
2484         } else {
2485             fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n", 
2486                     filename, line_num, cmd);
2487             errors++;
2488         }
2489     }
2490
2491     fclose(f);
2492     if (errors)
2493         return -1;
2494     else
2495         return 0;
2496 }
2497
2498
2499 void *http_server_thread(void *arg)
2500 {
2501     http_server(my_addr);
2502     return NULL;
2503 }
2504
2505 #if 0
2506 static void write_packet(FFCodec *ffenc,
2507                          UINT8 *buf, int size)
2508 {
2509     PacketHeader hdr;
2510     AVCodecContext *enc = &ffenc->enc;
2511     UINT8 *wptr;
2512     mk_header(&hdr, enc, size);
2513     wptr = http_fifo.wptr;
2514     fifo_write(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &wptr);
2515     fifo_write(&http_fifo, buf, size, &wptr);
2516     /* atomic modification of wptr */
2517     http_fifo.wptr = wptr;
2518     ffenc->data_count += size;
2519     ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
2520 }
2521 #endif
2522
2523 void help(void)
2524 {
2525     printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
2526            "usage: ffserver [-L] [-h] [-f configfile]\n"
2527            "Hyper fast multi format Audio/Video streaming server\n"
2528            "\n"
2529            "-L            : print the LICENCE\n"
2530            "-h            : this help\n"
2531            "-f configfile : use configfile instead of /etc/ffserver.conf\n"
2532            );
2533 }
2534
2535 void licence(void)
2536 {
2537     printf(
2538     "ffserver version " FFMPEG_VERSION "\n"
2539     "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
2540     "This library is free software; you can redistribute it and/or\n"
2541     "modify it under the terms of the GNU Lesser General Public\n"
2542     "License as published by the Free Software Foundation; either\n"
2543     "version 2 of the License, or (at your option) any later version.\n"
2544     "\n"
2545     "This library is distributed in the hope that it will be useful,\n"
2546     "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
2547     "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n"
2548     "Lesser General Public License for more details.\n"
2549     "\n"
2550     "You should have received a copy of the GNU Lesser General Public\n"
2551     "License along with this library; if not, write to the Free Software\n"
2552     "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n"
2553     );
2554 }
2555
2556 static void handle_child_exit(int sig)
2557 {
2558     pid_t pid;
2559     int status;
2560
2561     while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
2562         FFStream *feed;
2563
2564         for (feed = first_feed; feed; feed = feed->next) {
2565             if (feed->pid == pid) {
2566                 int uptime = time(0) - feed->pid_start;
2567
2568                 feed->pid = 0;
2569                 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
2570
2571                 if (uptime < 30) {
2572                     /* Turn off any more restarts */
2573                     feed->child_argv = 0;
2574                 }    
2575             }
2576         }
2577     }
2578
2579     need_to_start_children = 1;
2580 }
2581
2582 int main(int argc, char **argv)
2583 {
2584     const char *config_filename;
2585     int c;
2586     struct sigaction sigact;
2587
2588     register_all();
2589
2590     config_filename = "/etc/ffserver.conf";
2591
2592     my_program_name = argv[0];
2593
2594     for(;;) {
2595         c = getopt_long_only(argc, argv, "ndLh?f:", NULL, NULL);
2596         if (c == -1)
2597             break;
2598         switch(c) {
2599         case 'L':
2600             licence();
2601             exit(1);
2602         case '?':
2603         case 'h':
2604             help();
2605             exit(1);
2606         case 'n':
2607             no_launch = 1;
2608             break;
2609         case 'd':
2610             ffserver_debug = 1;
2611             break;
2612         case 'f':
2613             config_filename = optarg;
2614             break;
2615         default:
2616             exit(2);
2617         }
2618     }
2619
2620     putenv("http_proxy");               /* Kill the http_proxy */
2621
2622     /* address on which the server will handle connections */
2623     my_addr.sin_family = AF_INET;
2624     my_addr.sin_port = htons (8080);
2625     my_addr.sin_addr.s_addr = htonl (INADDR_ANY);
2626     nb_max_connections = 5;
2627     nb_max_bandwidth = 1000;
2628     first_stream = NULL;
2629     logfilename[0] = '\0';
2630
2631     memset(&sigact, 0, sizeof(sigact));
2632     sigact.sa_handler = handle_child_exit;
2633     sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
2634     sigaction(SIGCHLD, &sigact, 0);
2635
2636     if (parse_ffconfig(config_filename) < 0) {
2637         fprintf(stderr, "Incorrect config file - exiting.\n");
2638         exit(1);
2639     }
2640
2641     build_feed_streams();
2642
2643     /* signal init */
2644     signal(SIGPIPE, SIG_IGN);
2645
2646     /* open log file if needed */
2647     if (logfilename[0] != '\0') {
2648         if (!strcmp(logfilename, "-"))
2649             logfile = stdout;
2650         else
2651             logfile = fopen(logfilename, "w");
2652     }
2653
2654     if (http_server(my_addr) < 0) {
2655         fprintf(stderr, "Could not start http server\n");
2656         exit(1);
2657     }
2658
2659     return 0;
2660 }