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