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