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