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