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