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