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