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