build stripped and unstripped ffmpeg - ffserver exports all its symbols for modules...
[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>
5eb765ef 34#include <sys/wait.h>
85f07f22
FB
35#include <arpa/inet.h>
36#include <netdb.h>
37#include <ctype.h>
38#include <signal.h>
85f07f22
FB
39
40/* maximum number of simultaneous HTTP connections */
41#define HTTP_MAX_CONNECTIONS 2000
42
43enum 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
53const 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
cde25790
PG
63#define IOBUFFER_INIT_SIZE 8192
64#define PBUFFER_INIT_SIZE 8192
85f07f22
FB
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
5eb765ef
PG
73typedef struct {
74 INT64 count1, count2;
75 long time1, time2;
76} DataRateData;
77
85f07f22
FB
78/* context associated with one connection */
79typedef 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;
85f07f22
FB
85 UINT8 *buffer_ptr, *buffer_end;
86 int http_error;
87 struct HTTPContext *next;
42a63c6a 88 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
85f07f22
FB
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;
cde25790
PG
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;
85f07f22
FB
100 AVFormatContext fmt_ctx;
101 int last_packet_sent; /* true if last data packet was sent */
7434ba6d 102 int suppress_log;
42a63c6a 103 int bandwidth;
2ac887ba 104 long start_time; /* In milliseconds - this wraps fairly often */
5eb765ef 105 DataRateData datarate;
3120d2a2 106 int wmp_client_id;
7434ba6d
PG
107 char protocol[16];
108 char method[16];
109 char url[128];
cde25790
PG
110 int buffer_size;
111 UINT8 *buffer;
112 int pbuffer_size;
113 UINT8 *pbuffer;
85f07f22
FB
114} HTTPContext;
115
116/* each generated stream is described here */
117enum StreamType {
118 STREAM_TYPE_LIVE,
119 STREAM_TYPE_STATUS,
cde25790 120 STREAM_TYPE_REDIRECT,
85f07f22
FB
121};
122
123/* description of each stream of the ffserver.conf file */
124typedef struct FFStream {
125 enum StreamType stream_type;
126 char filename[1024]; /* stream filename */
127 struct FFStream *feed;
bd7cf6ad 128 AVOutputFormat *fmt;
85f07f22 129 int nb_streams;
42a63c6a 130 int prebuffer; /* Number of millseconds early to start */
2ac887ba 131 long max_time; /* Number of milliseconds to run */
79c4ea3c 132 int send_on_key;
85f07f22
FB
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 */
2ac887ba
PG
137 char author[512];
138 char title[512];
139 char copyright[512];
140 char comment[512];
cde25790 141 pid_t pid; /* Of ffmpeg process */
5eb765ef 142 time_t pid_start; /* Of ffmpeg process */
cde25790 143 char **child_argv;
85f07f22
FB
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 */
a6e14edd
PG
148 int conns_served;
149 INT64 bytes_served;
85f07f22
FB
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
156typedef struct FeedData {
157 long long data_count;
158 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
159} FeedData;
160
161struct sockaddr_in my_addr;
162char logfilename[1024];
163HTTPContext *first_http_ctx;
164FFStream *first_feed; /* contains only feeds */
165FFStream *first_stream; /* contains all streams, including feeds */
166
5eb765ef 167static int handle_http(HTTPContext *c);
85f07f22 168static int http_parse_request(HTTPContext *c);
5eb765ef 169static int http_send_data(HTTPContext *c);
85f07f22
FB
170static void compute_stats(HTTPContext *c);
171static int open_input_stream(HTTPContext *c, const char *info);
172static int http_start_receive_data(HTTPContext *c);
173static int http_receive_data(HTTPContext *c);
174
cde25790
PG
175static const char *my_program_name;
176
2ac887ba
PG
177static int ffserver_debug;
178static int no_launch;
5eb765ef 179static int need_to_start_children;
2ac887ba 180
85f07f22
FB
181int nb_max_connections;
182int nb_connections;
183
42a63c6a
PG
184int nb_max_bandwidth;
185int nb_bandwidth;
186
5eb765ef
PG
187static long cur_time; // Making this global saves on passing it around everywhere
188
85f07f22
FB
189static 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
197static FILE *logfile = NULL;
198
199static void http_log(char *fmt, ...)
200{
201 va_list ap;
202 va_start(ap, fmt);
203
7434ba6d 204 if (logfile) {
85f07f22 205 vfprintf(logfile, fmt, ap);
7434ba6d
PG
206 fflush(logfile);
207 }
85f07f22
FB
208 va_end(ap);
209}
210
7434ba6d
PG
211static 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';
cde25790
PG
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
5eb765ef
PG
232static 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 */
248static 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
cde25790
PG
256static void start_children(FFStream *feed)
257{
2ac887ba
PG
258 if (no_launch)
259 return;
260
cde25790 261 for (; feed; feed = feed->next) {
5eb765ef
PG
262 if (feed->child_argv && !feed->pid) {
263 feed->pid_start = time(0);
264
cde25790
PG
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
5eb765ef
PG
277 for (i = 3; i < 256; i++) {
278 close(i);
279 }
cde25790 280
5eb765ef 281 if (!ffserver_debug) {
2ac887ba
PG
282 i = open("/dev/null", O_RDWR);
283 if (i)
284 dup2(i, 0);
285 dup2(i, 1);
286 dup2(i, 2);
5eb765ef
PG
287 if (i)
288 close(i);
2ac887ba 289 }
cde25790
PG
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 }
7434ba6d
PG
307}
308
85f07f22
FB
309/* main loop of the http server */
310static 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;
85f07f22
FB
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
cde25790
PG
340 start_children(first_feed);
341
85f07f22
FB
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;
a6e14edd 383 poll_entry->events = POLLIN;/* Maybe this will work */
85f07f22
FB
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
5eb765ef
PG
401 if (need_to_start_children) {
402 need_to_start_children = 0;
403 start_children(first_feed);
404 }
405
85f07f22
FB
406 /* now handle the events */
407
408 cp = &first_http_ctx;
409 while ((*cp) != NULL) {
410 c = *cp;
5eb765ef 411 if (handle_http (c) < 0) {
85f07f22 412 /* close and free the connection */
7434ba6d 413 log_connection(c);
85f07f22
FB
414 close(c->fd);
415 if (c->fmt_in)
416 av_close_input_file(c->fmt_in);
417 *cp = c->next;
42a63c6a 418 nb_bandwidth -= c->bandwidth;
cde25790
PG
419 av_free(c->buffer);
420 av_free(c->pbuffer);
0f1578af 421 av_free(c);
85f07f22
FB
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) {
cde25790 441 c = NULL;
85f07f22
FB
442 } else {
443 /* add a new connection */
444 c = av_mallocz(sizeof(HTTPContext));
cde25790
PG
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;
2ac887ba 462 c->start_time = cur_time;
cde25790
PG
463 nb_connections++;
464 }
465 }
466 }
467 if (!c) {
468 close(fd);
85f07f22
FB
469 }
470 }
471 }
472 poll_entry++;
473 }
474}
475
5eb765ef 476static int handle_http(HTTPContext *c)
85f07f22
FB
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;
2e04edb3
PG
530 if (c->stream)
531 c->stream->bytes_served += len;
a6e14edd 532 c->data_count += len;
85f07f22
FB
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;
5eb765ef 553 if (http_send_data(c) < 0)
85f07f22
FB
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 */
a6e14edd 567 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
85f07f22
FB
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
3120d2a2
PG
578static 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
cde25790 595 memset(rates, 0xff, ratelen);
3120d2a2
PG
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
cde25790 626static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
3120d2a2
PG
627{
628 int i;
cde25790
PG
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
664static int modify_current_stream(HTTPContext *c, char *rates)
665{
666 int i;
667 FFStream *req = c->stream;
668 int action_required = 0;
3120d2a2
PG
669
670 for (i = 0; i < req->nb_streams; i++) {
671 AVCodecContext *codec = &req->streams[i]->codec;
672
3120d2a2
PG
673 switch(rates[i]) {
674 case 0:
cde25790 675 c->switch_feed_streams[i] = req->feed_streams[i];
3120d2a2
PG
676 break;
677 case 1:
cde25790 678 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
3120d2a2
PG
679 break;
680 case 2:
cde25790
PG
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
3120d2a2
PG
688 break;
689 }
3120d2a2 690
cde25790
PG
691 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
692 action_required = 1;
693 }
3120d2a2 694
cde25790
PG
695 return action_required;
696}
3120d2a2 697
3120d2a2 698
cde25790
PG
699static 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
3120d2a2 705
cde25790 706 /* Now update the stream */
3120d2a2 707 }
cde25790 708 c->switch_feed_streams[i] = -1;
3120d2a2 709}
7434ba6d 710
85f07f22
FB
711/* parse http request and prepare header */
712static int http_parse_request(HTTPContext *c)
713{
714 char *p;
715 int post;
7434ba6d 716 int doing_asx;
cde25790 717 int doing_asf_redirector;
42a63c6a 718 int doing_ram;
85f07f22
FB
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;
42a63c6a 726 int i;
3120d2a2 727 char ratebuf[32];
cde25790 728 char *useragent = 0;
85f07f22
FB
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';
7434ba6d 738
bd7cf6ad 739 pstrcpy(c->method, sizeof(c->method), cmd);
7434ba6d 740
85f07f22
FB
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
bd7cf6ad 757 pstrcpy(c->url, sizeof(c->url), url);
7434ba6d 758
85f07f22
FB
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;
7434ba6d 769
bd7cf6ad 770 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
85f07f22
FB
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) {
bd7cf6ad 779 pstrcpy(info, sizeof(info), p);
85f07f22
FB
780 *p = '\0';
781 } else {
782 info[0] = '\0';
783 }
784
cde25790
PG
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
7434ba6d
PG
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
cde25790
PG
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
42a63c6a
PG
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
85f07f22
FB
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 }
42a63c6a 833
cde25790
PG
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
3120d2a2
PG
856 /* If this is WMP, get the rate information */
857 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
cde25790
PG
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 }
3120d2a2
PG
864 }
865
42a63c6a
PG
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:
ec3b2232 878 av_abort();
42a63c6a
PG
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
cde25790 905 if (doing_asx || doing_ram || doing_asf_redirector) {
7434ba6d
PG
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;
42a63c6a
PG
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);
cde25790
PG
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);
42a63c6a 961 } else
ec3b2232 962 av_abort();
7434ba6d
PG
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
42a63c6a 973 sprintf(msg, "ASX/RAM file not handled");
7434ba6d 974 goto send_error;
85f07f22
FB
975 }
976
a6e14edd 977 stream->conns_served++;
7434ba6d 978
85f07f22
FB
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) {
7434ba6d
PG
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;
3120d2a2 988 int client_id = 0;
7434ba6d
PG
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 }
3120d2a2
PG
995 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
996 client_id = strtol(p + 18, 0, 10);
997 }
7434ba6d
PG
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 }
3120d2a2 1017
cde25790
PG
1018#ifdef DEBUG_WMP
1019 http_log("\nGot request:\n%s\n", c->buffer);
3120d2a2
PG
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) {
cde25790
PG
1032 if (modify_current_stream(wmpc, ratebuf)) {
1033 wmpc->switch_pending = 1;
3120d2a2
PG
1034 }
1035 }
1036 }
7434ba6d 1037
85f07f22
FB
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
cde25790 1050#ifdef DEBUG_WMP
3120d2a2 1051 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
cde25790 1052 http_log("\nGot request:\n%s\n", c->buffer);
3120d2a2
PG
1053 }
1054#endif
1055
85f07f22
FB
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";
85f07f22
FB
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")) {
3120d2a2
PG
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);
cde25790 1084 mime_type = "application/octet-stream";
85f07f22 1085 }
f747e6d3 1086 q += sprintf(q, "Content-Type: %s\r\n", mime_type);
85f07f22
FB
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
2ac887ba
PG
1119static 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
85f07f22
FB
1130static void compute_stats(HTTPContext *c)
1131{
1132 HTTPContext *c1;
1133 FFStream *stream;
1134 char *q, *p;
1135 time_t ti;
1136 int i;
cde25790
PG
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 }
85f07f22
FB
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
cde25790
PG
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>");
85f07f22
FB
1159 q += sprintf(q, "<H1>FFServer Status</H1>\n");
1160 /* format status */
79c4ea3c 1161 q += sprintf(q, "<H2>Available Streams</H2>\n");
a6e14edd 1162 q += sprintf(q, "<TABLE cellspacing=0 cellpadding=4>\n");
2ac887ba 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");
85f07f22
FB
1164 stream = first_stream;
1165 while (stream != NULL) {
42a63c6a
PG
1166 char sfilename[1024];
1167 char *eosf;
1168
a6e14edd 1169 if (stream->feed != stream) {
bd7cf6ad 1170 pstrcpy(sfilename, sizeof(sfilename) - 1, stream->filename);
a6e14edd
PG
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 }
42a63c6a 1178 }
a6e14edd
PG
1179
1180 q += sprintf(q, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1181 sfilename, stream->filename);
2ac887ba
PG
1182 q += sprintf(q, "<td align=right> %d <td align=right> ",
1183 stream->conns_served);
1184 q += fmt_bytecount(q, stream->bytes_served);
a6e14edd
PG
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:
ec3b2232 1216 av_abort();
79c4ea3c 1217 }
85f07f22 1218 }
a6e14edd
PG
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");
85f07f22 1230 }
a6e14edd
PG
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;
85f07f22 1235 }
85f07f22
FB
1236 }
1237 stream = stream->next;
1238 }
1239 q += sprintf(q, "</TABLE>\n");
a6e14edd
PG
1240
1241 stream = first_stream;
1242 while (stream != NULL) {
1243 if (stream->feed == stream) {
1244 q += sprintf(q, "<h2>Feed %s</h2>", stream->filename);
cde25790
PG
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 */
5eb765ef 1253 snprintf(ps_cmd, sizeof(ps_cmd), "ps -o \"%%cpu,bsdtime\" --no-headers %d", stream->pid);
cde25790
PG
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 }
b582f314 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");
a6e14edd
PG
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";
b582f314
PG
1276 char parameters[64];
1277
1278 parameters[0] = 0;
a6e14edd
PG
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";
cde25790
PG
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);
a6e14edd
PG
1288 break;
1289 default:
ec3b2232 1290 av_abort();
a6e14edd 1291 }
b582f314
PG
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);
a6e14edd
PG
1294 }
1295 q += sprintf(q, "</table>\n");
1296
1297 }
1298 stream = stream->next;
1299 }
85f07f22
FB
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 */
79c4ea3c 1332 q += sprintf(q, "<H2>Connection Status</H2>\n");
85f07f22
FB
1333
1334 q += sprintf(q, "Number of connections: %d / %d<BR>\n",
1335 nb_connections, nb_max_connections);
1336
42a63c6a
PG
1337 q += sprintf(q, "Bandwidth in use: %dk / %dk<BR>\n",
1338 nb_bandwidth, nb_max_bandwidth);
1339
85f07f22 1340 q += sprintf(q, "<TABLE>\n");
5eb765ef 1341 q += sprintf(q, "<TR><Th>#<Th>File<Th>IP<Th>State<Th>Target bits/sec<Th>Actual bits/sec<Th>Bytes transferred\n");
85f07f22
FB
1342 c1 = first_http_ctx;
1343 i = 0;
cde25790
PG
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
85f07f22
FB
1355 i++;
1356 p = inet_ntoa(c1->from_addr.sin_addr);
5eb765ef 1357 q += sprintf(q, "<TR><TD><B>%d</B><TD>%s%s <TD> %s <TD> %s <td align=right>",
85f07f22
FB
1358 i, c1->stream->filename,
1359 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1360 p,
5eb765ef
PG
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>");
2ac887ba
PG
1366 q += fmt_bytecount(q, c1->data_count);
1367 *q++ = '\n';
85f07f22
FB
1368 c1 = c1->next;
1369 }
1370 q += sprintf(q, "</TABLE>\n");
1371
1372 /* date */
1373 ti = time(NULL);
1374 p = ctime(&ti);
a6e14edd 1375 q += sprintf(q, "<HR size=1 noshade>Generated at %s", p);
85f07f22
FB
1376 q += sprintf(q, "</BODY>\n</HTML>\n");
1377
1378 c->buffer_ptr = c->buffer;
1379 c->buffer_end = q;
1380}
1381
1382
1383static void http_write_packet(void *opaque,
1384 unsigned char *buf, int size)
1385{
1386 HTTPContext *c = opaque;
f747e6d3
PG
1387
1388 if (c->buffer_ptr == c->buffer_end || !c->buffer_ptr)
1389 c->buffer_ptr = c->buffer_end = c->buffer;
1390
cde25790 1391 if (c->buffer_end - c->buffer + size > c->buffer_size) {
31def229
PG
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);
cde25790
PG
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;
31def229 1406 c->buffer_size = new_buffer_size;
cde25790
PG
1407 } else {
1408 av_abort();
1409 }
1410 }
f747e6d3
PG
1411
1412 memcpy(c->buffer_end, buf, size);
1413 c->buffer_end += size;
85f07f22
FB
1414}
1415
1416static 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);
f747e6d3
PG
1431 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1432 int prebuffer = strtol(buf, 0, 10);
2c4ae653 1433 stream_pos = av_gettime() - prebuffer * 1000000;
85f07f22 1434 } else {
2c4ae653 1435 stream_pos = av_gettime() - c->stream->prebuffer * 1000;
85f07f22
FB
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 */
bd7cf6ad 1451 if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0)
85f07f22
FB
1452 return -1;
1453 c->fmt_in = s;
1454
bd7cf6ad
FB
1455 if (c->fmt_in->iformat->read_seek) {
1456 c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
85f07f22
FB
1457 }
1458
1459 // printf("stream %s opened pos=%0.6f\n", input_filename, stream_pos / 1000000.0);
1460 return 0;
1461}
1462
5eb765ef 1463static int http_prepare_data(HTTPContext *c)
85f07f22
FB
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));
2ac887ba
PG
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
85f07f22
FB
1475 if (c->stream->feed) {
1476 /* open output stream by using specified codecs */
bd7cf6ad 1477 c->fmt_ctx.oformat = c->stream->fmt;
85f07f22
FB
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;
f747e6d3
PG
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
85f07f22
FB
1488 st->codec.frame_number = 0; /* XXX: should be done in
1489 AVStream, not in codec */
85f07f22 1490 }
42a63c6a 1491 c->got_key_frame = 0;
85f07f22
FB
1492 } else {
1493 /* open output stream by using codecs in specified file */
bd7cf6ad 1494 c->fmt_ctx.oformat = c->stream->fmt;
85f07f22
FB
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 */
85f07f22 1503 }
42a63c6a 1504 c->got_key_frame = 0;
85f07f22 1505 }
cde25790 1506 init_put_byte(&c->fmt_ctx.pb, c->pbuffer, c->pbuffer_size,
85f07f22
FB
1507 1, c, NULL, http_write_packet, NULL);
1508 c->fmt_ctx.pb.is_streamed = 1;
1509 /* prepare header */
bd7cf6ad 1510 av_write_header(&c->fmt_ctx);
85f07f22
FB
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;
42a63c6a 1522 c->got_key_frame = 0;
85f07f22
FB
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);
0f1578af 1529 payload = av_malloc(payload_size);
85f07f22
FB
1530 if (fifo_read(&http_fifo, payload, payload_size, &c->rptr) < 0) {
1531 /* cannot read all the payload */
0f1578af 1532 av_free(payload);
85f07f22
FB
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)
42a63c6a
PG
1548 c->got_key_frame |= 1 << i;
1549 if (c->got_key_frame & (1 << i)) {
85f07f22
FB
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 }
0f1578af 1571 av_free(payload);
85f07f22
FB
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 }
ec3b2232
PG
1582
1583 if (c->stream->max_time &&
2ac887ba 1584 c->stream->max_time + c->start_time - cur_time < 0) {
ec3b2232
PG
1585 /* We have timed out */
1586 c->state = HTTPSTATE_SEND_DATA_TRAILER;
1587 } else if (av_read_packet(c->fmt_in, &pkt) < 0) {
85f07f22
FB
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 */
cde25790
PG
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 }
85f07f22 1614 for(i=0;i<c->stream->nb_streams;i++) {
cde25790 1615 if (c->feed_streams[i] == pkt.stream_index) {
85f07f22 1616 pkt.stream_index = i;
42a63c6a
PG
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 */
79c4ea3c 1627 if (!c->stream->send_on_key || ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
42a63c6a
PG
1628 goto send_it;
1629 }
85f07f22
FB
1630 }
1631 }
1632 } else {
f747e6d3
PG
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
10bb7023 1647 if (av_write_packet(&c->fmt_ctx, &pkt, 0))
f747e6d3
PG
1648 c->state = HTTPSTATE_SEND_DATA_TRAILER;
1649
1650 codec->frame_number++;
85f07f22 1651 }
42a63c6a 1652
85f07f22
FB
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 */
bd7cf6ad 1663 av_write_trailer(&c->fmt_ctx);
85f07f22
FB
1664 c->last_packet_sent = 1;
1665 break;
1666 }
1667 return 0;
1668}
1669
1670/* should convert the format at the same time */
5eb765ef 1671static int http_send_data(HTTPContext *c)
85f07f22
FB
1672{
1673 int len, ret;
1674
1675 while (c->buffer_ptr >= c->buffer_end) {
5eb765ef 1676 ret = http_prepare_data(c);
85f07f22
FB
1677 if (ret < 0)
1678 return -1;
1679 else if (ret == 0) {
a6e14edd 1680 continue;
85f07f22
FB
1681 } else {
1682 /* state change requested */
1683 return 0;
1684 }
1685 }
1686
f747e6d3
PG
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;
5eb765ef 1697 update_datarate(&c->datarate, c->data_count);
2e04edb3
PG
1698 if (c->stream)
1699 c->stream->bytes_served += len;
85f07f22 1700 }
85f07f22
FB
1701 }
1702 return 0;
1703}
1704
1705static 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
1729static int http_receive_data(HTTPContext *c)
1730{
85f07f22
FB
1731 HTTPContext *c1;
1732
a6e14edd
PG
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;
5eb765ef 1748 update_datarate(&c->datarate, c->data_count);
a6e14edd
PG
1749 }
1750 }
1751
85f07f22 1752 if (c->buffer_ptr >= c->buffer_end) {
f747e6d3 1753 FFStream *feed = c->stream;
85f07f22
FB
1754 /* a packet has been received : write it in the store, except
1755 if header */
1756 if (c->data_count > FFM_PACKET_SIZE) {
85f07f22
FB
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 }
f747e6d3
PG
1782 } else {
1783 /* We have a header in our hands that contains useful data */
1784 AVFormatContext s;
bd7cf6ad 1785 AVInputFormat *fmt_in;
f747e6d3
PG
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
bd7cf6ad
FB
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
ec3b2232
PG
1800 s.priv_data = av_mallocz(fmt_in->priv_data_size);
1801 if (!s.priv_data)
1802 goto fail;
1803
bd7cf6ad 1804 if (fmt_in->read_header(&s, 0) < 0) {
ec3b2232 1805 av_freep(&s.priv_data);
f747e6d3
PG
1806 goto fail;
1807 }
1808
1809 /* Now we have the actual streams */
1810 if (s.nb_streams != feed->nb_streams) {
ec3b2232 1811 av_freep(&s.priv_data);
f747e6d3
PG
1812 goto fail;
1813 }
1814 for (i = 0; i < s.nb_streams; i++) {
bd7cf6ad
FB
1815 memcpy(&feed->streams[i]->codec,
1816 &s.streams[i]->codec, sizeof(AVCodecContext));
f747e6d3 1817 }
ec3b2232 1818 av_freep(&s.priv_data);
85f07f22
FB
1819 }
1820 c->buffer_ptr = c->buffer;
1821 }
1822
85f07f22
FB
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 */
1831int 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;
f747e6d3
PG
1842 if (av1->codec_id == av->codec_id &&
1843 av1->codec_type == av->codec_type &&
85f07f22
FB
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;
f747e6d3 1859 default:
ec3b2232 1860 av_abort();
85f07f22
FB
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 */
1877void 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 }
cde25790
PG
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) {
85f07f22
FB
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 }
bd7cf6ad 1919 s->oformat = feed->fmt;
85f07f22
FB
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 }
bd7cf6ad
FB
1926 av_write_header(s);
1927 /* XXX: need better api */
1928 av_freep(&s->priv_data);
85f07f22
FB
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
1949static 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 */
1982void 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 }
ba9b374f 2005 /* Bitrate tolerance is less for streaming */
42a63c6a
PG
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;
ba9b374f
J
2014 av->qcompress = 0.5;
2015 av->qblur = 0.5;
68d7eef9 2016
85f07f22 2017 break;
f747e6d3 2018 default:
ec3b2232 2019 av_abort();
85f07f22
FB
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
f747e6d3
PG
2029int 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
2046int 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
85f07f22
FB
2063int 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;
cde25790 2071 FFStream **last_stream, *stream, *redirect;
85f07f22
FB
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;
cde25790 2090 redirect = NULL;
85f07f22
FB
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 }
42a63c6a
PG
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 }
85f07f22
FB
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 }
cde25790
PG
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 }
85f07f22
FB
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++;
f747e6d3
PG
2222 } else {
2223 /* Make sure that we start out clean */
42a63c6a
PG
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 }
85f07f22
FB
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;
dd2af5aa
FB
2283 /* jpeg cannot be used here, so use single frame jpeg */
2284 if (!strcmp(arg, "jpeg"))
2285 strcpy(arg, "singlejpeg");
85f07f22
FB
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 }
cde25790
PG
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 }
2ac887ba
PG
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 }
42a63c6a
PG
2321 } else if (!strcasecmp(cmd, "Preroll")) {
2322 get_arg(arg, sizeof(arg), &p);
2323 if (stream) {
2324 stream->prebuffer = atoi(arg) * 1000;
2325 }
79c4ea3c
PG
2326 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
2327 if (stream) {
2328 stream->send_on_key = 1;
2329 }
f747e6d3
PG
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 }
ec3b2232
PG
2346 } else if (!strcasecmp(cmd, "MaxTime")) {
2347 get_arg(arg, sizeof(arg), &p);
2348 if (stream) {
2ac887ba 2349 stream->max_time = atoi(arg) * 1000;
ec3b2232 2350 }
85f07f22
FB
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 }
e7f9c674
J
2396 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
2397 if (stream) {
2398 video_enc.flags |= CODEC_FLAG_HQ;
2399 }
42a63c6a
PG
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 }
85f07f22
FB
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;
cde25790
PG
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;
85f07f22
FB
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
2499void *http_server_thread(void *arg)
2500{
2501 http_server(my_addr);
2502 return NULL;
2503}
2504
2505#if 0
2506static 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
2523void help(void)
2524{
773a21b8 2525 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
85f07f22
FB
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
2535void licence(void)
2536{
2537 printf(
2538 "ffserver version " FFMPEG_VERSION "\n"
773a21b8
FB
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"
85f07f22 2544 "\n"
773a21b8 2545 "This library is distributed in the hope that it will be useful,\n"
85f07f22 2546 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
773a21b8
FB
2547 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
2548 "Lesser General Public License for more details.\n"
85f07f22 2549 "\n"
773a21b8
FB
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"
85f07f22
FB
2553 );
2554}
2555
5eb765ef
PG
2556static 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
85f07f22
FB
2582int main(int argc, char **argv)
2583{
2584 const char *config_filename;
2585 int c;
5eb765ef 2586 struct sigaction sigact;
85f07f22 2587
2c4ae653 2588 av_register_all();
85f07f22
FB
2589
2590 config_filename = "/etc/ffserver.conf";
2591
cde25790
PG
2592 my_program_name = argv[0];
2593
85f07f22 2594 for(;;) {
2ac887ba 2595 c = getopt_long_only(argc, argv, "ndLh?f:", NULL, NULL);
85f07f22
FB
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);
2ac887ba
PG
2606 case 'n':
2607 no_launch = 1;
2608 break;
2609 case 'd':
2610 ffserver_debug = 1;
2611 break;
85f07f22
FB
2612 case 'f':
2613 config_filename = optarg;
2614 break;
2615 default:
2616 exit(2);
2617 }
2618 }
2619
cde25790
PG
2620 putenv("http_proxy"); /* Kill the http_proxy */
2621
85f07f22
FB
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;
42a63c6a 2627 nb_max_bandwidth = 1000;
85f07f22
FB
2628 first_stream = NULL;
2629 logfilename[0] = '\0';
2630
5eb765ef
PG
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
85f07f22
FB
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) {
773a21b8 2655 fprintf(stderr, "Could not start http server\n");
85f07f22
FB
2656 exit(1);
2657 }
2658
2659 return 0;
2660}