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