* Removed a debug printf
[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>
85f07f22
FB
31#include <sys/types.h>
32#include <sys/socket.h>
5eb765ef 33#include <sys/wait.h>
85f07f22
FB
34#include <arpa/inet.h>
35#include <netdb.h>
36#include <ctype.h>
37#include <signal.h>
2effd274
FB
38#include <dlfcn.h>
39
40#include "ffserver.h"
85f07f22
FB
41
42/* maximum number of simultaneous HTTP connections */
43#define HTTP_MAX_CONNECTIONS 2000
44
45enum HTTPState {
46 HTTPSTATE_WAIT_REQUEST,
47 HTTPSTATE_SEND_HEADER,
48 HTTPSTATE_SEND_DATA_HEADER,
2effd274 49 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
85f07f22 50 HTTPSTATE_SEND_DATA_TRAILER,
2effd274
FB
51 HTTPSTATE_RECEIVE_DATA,
52 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
53 HTTPSTATE_WAIT, /* wait before sending next packets */
54 HTTPSTATE_WAIT_SHORT, /* short wait for short term
55 bandwidth limitation */
56 HTTPSTATE_READY,
57
58 RTSPSTATE_WAIT_REQUEST,
59 RTSPSTATE_SEND_REPLY,
85f07f22
FB
60};
61
62const char *http_state[] = {
2effd274
FB
63 "HTTP_WAIT_REQUEST",
64 "HTTP_SEND_HEADER",
65
85f07f22
FB
66 "SEND_DATA_HEADER",
67 "SEND_DATA",
68 "SEND_DATA_TRAILER",
69 "RECEIVE_DATA",
70 "WAIT_FEED",
2effd274
FB
71 "WAIT",
72 "WAIT_SHORT",
73 "READY",
74
75 "RTSP_WAIT_REQUEST",
76 "RTSP_SEND_REPLY",
85f07f22
FB
77};
78
cde25790 79#define IOBUFFER_INIT_SIZE 8192
85f07f22
FB
80
81/* coef for exponential mean for bitrate estimation in statistics */
82#define AVG_COEF 0.9
83
84/* timeouts are in ms */
2effd274
FB
85#define HTTP_REQUEST_TIMEOUT (15 * 1000)
86#define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
87
85f07f22
FB
88#define SYNC_TIMEOUT (10 * 1000)
89
5eb765ef
PG
90typedef struct {
91 INT64 count1, count2;
92 long time1, time2;
93} DataRateData;
94
85f07f22
FB
95/* context associated with one connection */
96typedef struct HTTPContext {
97 enum HTTPState state;
98 int fd; /* socket file descriptor */
99 struct sockaddr_in from_addr; /* origin */
100 struct pollfd *poll_entry; /* used when polling */
101 long timeout;
85f07f22
FB
102 UINT8 *buffer_ptr, *buffer_end;
103 int http_error;
104 struct HTTPContext *next;
42a63c6a 105 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
85f07f22
FB
106 INT64 data_count;
107 /* feed input */
108 int feed_fd;
109 /* input format handling */
110 AVFormatContext *fmt_in;
2effd274
FB
111 long start_time; /* In milliseconds - this wraps fairly often */
112 INT64 first_pts; /* initial pts value */
113 int pts_stream_index; /* stream we choose as clock reference */
85f07f22
FB
114 /* output format handling */
115 struct FFStream *stream;
cde25790
PG
116 /* -1 is invalid stream */
117 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
118 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
119 int switch_pending;
2effd274 120 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
85f07f22 121 int last_packet_sent; /* true if last data packet was sent */
7434ba6d 122 int suppress_log;
42a63c6a 123 int bandwidth;
5eb765ef 124 DataRateData datarate;
3120d2a2 125 int wmp_client_id;
7434ba6d
PG
126 char protocol[16];
127 char method[16];
128 char url[128];
cde25790
PG
129 int buffer_size;
130 UINT8 *buffer;
2effd274
FB
131 int is_packetized; /* if true, the stream is packetized */
132 int packet_stream_index; /* current stream for output in state machine */
133
134 /* RTSP state specific */
135 UINT8 *pb_buffer; /* XXX: use that in all the code */
136 ByteIOContext *pb;
137 int seq; /* RTSP sequence number */
138
139 /* RTP state specific */
140 enum RTSPProtocol rtp_protocol;
141 char session_id[32]; /* session id */
142 AVFormatContext *rtp_ctx[MAX_STREAMS];
143 URLContext *rtp_handles[MAX_STREAMS];
144 /* RTP short term bandwidth limitation */
145 int packet_byte_count;
146 int packet_start_time_us; /* used for short durations (a few
147 seconds max) */
85f07f22
FB
148} HTTPContext;
149
150/* each generated stream is described here */
151enum StreamType {
152 STREAM_TYPE_LIVE,
153 STREAM_TYPE_STATUS,
cde25790 154 STREAM_TYPE_REDIRECT,
85f07f22
FB
155};
156
8256c0a3
PG
157enum IPAddressAction {
158 IP_ALLOW = 1,
159 IP_DENY,
160};
161
162typedef struct IPAddressACL {
163 struct IPAddressACL *next;
164 enum IPAddressAction action;
165 struct in_addr first;
166 struct in_addr last;
167} IPAddressACL;
168
85f07f22
FB
169/* description of each stream of the ffserver.conf file */
170typedef struct FFStream {
171 enum StreamType stream_type;
172 char filename[1024]; /* stream filename */
2effd274
FB
173 struct FFStream *feed; /* feed we are using (can be null if
174 coming from file) */
bd7cf6ad 175 AVOutputFormat *fmt;
8256c0a3 176 IPAddressACL *acl;
85f07f22 177 int nb_streams;
42a63c6a 178 int prebuffer; /* Number of millseconds early to start */
2ac887ba 179 long max_time; /* Number of milliseconds to run */
79c4ea3c 180 int send_on_key;
85f07f22
FB
181 AVStream *streams[MAX_STREAMS];
182 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
183 char feed_filename[1024]; /* file name of the feed storage, or
184 input file name for a stream */
2ac887ba
PG
185 char author[512];
186 char title[512];
187 char copyright[512];
188 char comment[512];
cde25790 189 pid_t pid; /* Of ffmpeg process */
5eb765ef 190 time_t pid_start; /* Of ffmpeg process */
cde25790 191 char **child_argv;
85f07f22 192 struct FFStream *next;
2effd274
FB
193 /* RTSP options */
194 char *rtsp_option;
85f07f22 195 /* feed specific */
2effd274 196 int feed_opened; /* true if someone is writing to the feed */
85f07f22 197 int is_feed; /* true if it is a feed */
a6e14edd
PG
198 int conns_served;
199 INT64 bytes_served;
85f07f22
FB
200 INT64 feed_max_size; /* maximum storage size */
201 INT64 feed_write_index; /* current write position in feed (it wraps round) */
202 INT64 feed_size; /* current size of feed */
203 struct FFStream *next_feed;
204} FFStream;
205
206typedef struct FeedData {
207 long long data_count;
208 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
209} FeedData;
210
2effd274
FB
211struct sockaddr_in my_http_addr;
212struct sockaddr_in my_rtsp_addr;
213
85f07f22
FB
214char logfilename[1024];
215HTTPContext *first_http_ctx;
216FFStream *first_feed; /* contains only feeds */
217FFStream *first_stream; /* contains all streams, including feeds */
218
2effd274
FB
219static void new_connection(int server_fd, int is_rtsp);
220static void close_connection(HTTPContext *c);
221
222/* HTTP handling */
223static int handle_connection(HTTPContext *c);
85f07f22 224static int http_parse_request(HTTPContext *c);
5eb765ef 225static int http_send_data(HTTPContext *c);
85f07f22
FB
226static void compute_stats(HTTPContext *c);
227static int open_input_stream(HTTPContext *c, const char *info);
228static int http_start_receive_data(HTTPContext *c);
229static int http_receive_data(HTTPContext *c);
2effd274
FB
230static int compute_send_delay(HTTPContext *c);
231
232/* RTSP handling */
233static int rtsp_parse_request(HTTPContext *c);
234static void rtsp_cmd_describe(HTTPContext *c, const char *url);
235static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
236static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
237static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
238static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
239
240/* RTP handling */
241static HTTPContext *rtp_new_connection(HTTPContext *rtsp_c,
242 FFStream *stream, const char *session_id);
243static int rtp_new_av_stream(HTTPContext *c,
244 int stream_index, struct sockaddr_in *dest_addr);
85f07f22 245
cde25790
PG
246static const char *my_program_name;
247
2ac887ba 248static int ffserver_debug;
2effd274 249static int ffserver_daemon;
2ac887ba 250static int no_launch;
5eb765ef 251static int need_to_start_children;
2ac887ba 252
85f07f22
FB
253int nb_max_connections;
254int nb_connections;
255
42a63c6a
PG
256int nb_max_bandwidth;
257int nb_bandwidth;
258
5eb765ef
PG
259static long cur_time; // Making this global saves on passing it around everywhere
260
85f07f22
FB
261static long gettime_ms(void)
262{
263 struct timeval tv;
264
265 gettimeofday(&tv,NULL);
266 return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
267}
268
269static FILE *logfile = NULL;
270
271static void http_log(char *fmt, ...)
272{
273 va_list ap;
274 va_start(ap, fmt);
275
7434ba6d 276 if (logfile) {
85f07f22 277 vfprintf(logfile, fmt, ap);
7434ba6d
PG
278 fflush(logfile);
279 }
85f07f22
FB
280 va_end(ap);
281}
282
7434ba6d
PG
283static void log_connection(HTTPContext *c)
284{
285 char buf1[32], buf2[32], *p;
286 time_t ti;
287
288 if (c->suppress_log)
289 return;
290
291 /* XXX: reentrant function ? */
292 p = inet_ntoa(c->from_addr.sin_addr);
293 strcpy(buf1, p);
294 ti = time(NULL);
295 p = ctime(&ti);
296 strcpy(buf2, p);
297 p = buf2 + strlen(p) - 1;
298 if (*p == '\n')
299 *p = '\0';
cde25790
PG
300 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
301 buf1, buf2, c->method, c->url, c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
302}
303
5eb765ef
PG
304static void update_datarate(DataRateData *drd, INT64 count)
305{
306 if (!drd->time1 && !drd->count1) {
307 drd->time1 = drd->time2 = cur_time;
308 drd->count1 = drd->count2 = count;
309 } else {
310 if (cur_time - drd->time2 > 5000) {
311 drd->time1 = drd->time2;
312 drd->count1 = drd->count2;
313 drd->time2 = cur_time;
314 drd->count2 = count;
315 }
316 }
317}
318
319/* In bytes per second */
320static int compute_datarate(DataRateData *drd, INT64 count)
321{
322 if (cur_time == drd->time1)
323 return 0;
324
325 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
326}
327
cde25790
PG
328static void start_children(FFStream *feed)
329{
2ac887ba
PG
330 if (no_launch)
331 return;
332
cde25790 333 for (; feed; feed = feed->next) {
5eb765ef
PG
334 if (feed->child_argv && !feed->pid) {
335 feed->pid_start = time(0);
336
cde25790
PG
337 feed->pid = fork();
338
339 if (feed->pid < 0) {
340 fprintf(stderr, "Unable to create children\n");
341 exit(1);
342 }
343 if (!feed->pid) {
344 /* In child */
345 char pathname[1024];
346 char *slash;
347 int i;
348
5eb765ef
PG
349 for (i = 3; i < 256; i++) {
350 close(i);
351 }
cde25790 352
5eb765ef 353 if (!ffserver_debug) {
2ac887ba
PG
354 i = open("/dev/null", O_RDWR);
355 if (i)
356 dup2(i, 0);
357 dup2(i, 1);
358 dup2(i, 2);
5eb765ef
PG
359 if (i)
360 close(i);
2ac887ba 361 }
cde25790
PG
362
363 pstrcpy(pathname, sizeof(pathname), my_program_name);
364
365 slash = strrchr(pathname, '/');
366 if (!slash) {
367 slash = pathname;
368 } else {
369 slash++;
370 }
371 strcpy(slash, "ffmpeg");
372
373 execvp(pathname, feed->child_argv);
374
375 _exit(1);
376 }
377 }
378 }
7434ba6d
PG
379}
380
2effd274
FB
381/* open a listening socket */
382static int socket_open_listen(struct sockaddr_in *my_addr)
85f07f22 383{
2effd274 384 int server_fd, tmp;
85f07f22
FB
385
386 server_fd = socket(AF_INET,SOCK_STREAM,0);
387 if (server_fd < 0) {
388 perror ("socket");
389 return -1;
390 }
391
392 tmp = 1;
393 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
394
2effd274 395 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
85f07f22
FB
396 perror ("bind");
397 close(server_fd);
398 return -1;
399 }
400
401 if (listen (server_fd, 5) < 0) {
402 perror ("listen");
403 close(server_fd);
404 return -1;
405 }
2effd274
FB
406 fcntl(server_fd, F_SETFL, O_NONBLOCK);
407
408 return server_fd;
409}
410
411
412/* main loop of the http server */
413static int http_server(void)
414{
415 int server_fd, ret, rtsp_server_fd, delay, delay1;
416 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
417 HTTPContext *c, *c_next;
418
419 server_fd = socket_open_listen(&my_http_addr);
420 if (server_fd < 0)
421 return -1;
85f07f22 422
2effd274
FB
423 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
424 if (rtsp_server_fd < 0)
425 return -1;
426
85f07f22
FB
427 http_log("ffserver started.\n");
428
cde25790
PG
429 start_children(first_feed);
430
85f07f22
FB
431 first_http_ctx = NULL;
432 nb_connections = 0;
433 first_http_ctx = NULL;
434 for(;;) {
435 poll_entry = poll_table;
436 poll_entry->fd = server_fd;
437 poll_entry->events = POLLIN;
438 poll_entry++;
439
2effd274
FB
440 poll_entry->fd = rtsp_server_fd;
441 poll_entry->events = POLLIN;
442 poll_entry++;
443
85f07f22
FB
444 /* wait for events on each HTTP handle */
445 c = first_http_ctx;
2effd274 446 delay = 1000;
85f07f22
FB
447 while (c != NULL) {
448 int fd;
449 fd = c->fd;
450 switch(c->state) {
2effd274
FB
451 case HTTPSTATE_SEND_HEADER:
452 case RTSPSTATE_SEND_REPLY:
85f07f22
FB
453 c->poll_entry = poll_entry;
454 poll_entry->fd = fd;
2effd274 455 poll_entry->events = POLLOUT;
85f07f22
FB
456 poll_entry++;
457 break;
85f07f22
FB
458 case HTTPSTATE_SEND_DATA_HEADER:
459 case HTTPSTATE_SEND_DATA:
460 case HTTPSTATE_SEND_DATA_TRAILER:
2effd274
FB
461 if (!c->is_packetized) {
462 /* for TCP, we output as much as we can (may need to put a limit) */
463 c->poll_entry = poll_entry;
464 poll_entry->fd = fd;
465 poll_entry->events = POLLOUT;
466 poll_entry++;
467 } else {
468 /* not strictly correct, but currently cannot add
469 more than one fd in poll entry */
470 delay = 0;
471 }
85f07f22 472 break;
2effd274 473 case HTTPSTATE_WAIT_REQUEST:
85f07f22 474 case HTTPSTATE_RECEIVE_DATA:
85f07f22 475 case HTTPSTATE_WAIT_FEED:
2effd274 476 case RTSPSTATE_WAIT_REQUEST:
85f07f22
FB
477 /* need to catch errors */
478 c->poll_entry = poll_entry;
479 poll_entry->fd = fd;
a6e14edd 480 poll_entry->events = POLLIN;/* Maybe this will work */
85f07f22
FB
481 poll_entry++;
482 break;
2effd274
FB
483 case HTTPSTATE_WAIT:
484 c->poll_entry = NULL;
485 delay1 = compute_send_delay(c);
486 if (delay1 < delay)
487 delay = delay1;
488 break;
489 case HTTPSTATE_WAIT_SHORT:
490 c->poll_entry = NULL;
491 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
492 if (delay1 < delay)
493 delay = delay1;
494 break;
85f07f22
FB
495 default:
496 c->poll_entry = NULL;
497 break;
498 }
499 c = c->next;
500 }
501
502 /* wait for an event on one connection. We poll at least every
503 second to handle timeouts */
504 do {
2effd274 505 ret = poll(poll_table, poll_entry - poll_table, delay);
85f07f22
FB
506 } while (ret == -1);
507
508 cur_time = gettime_ms();
509
5eb765ef
PG
510 if (need_to_start_children) {
511 need_to_start_children = 0;
512 start_children(first_feed);
513 }
514
85f07f22 515 /* now handle the events */
2effd274
FB
516 for(c = first_http_ctx; c != NULL; c = c_next) {
517 c_next = c->next;
518 if (handle_connection(c) < 0) {
85f07f22 519 /* close and free the connection */
7434ba6d 520 log_connection(c);
2effd274 521 close_connection(c);
85f07f22
FB
522 }
523 }
524
85f07f22 525 poll_entry = poll_table;
2effd274 526 /* new HTTP connection request ? */
85f07f22 527 if (poll_entry->revents & POLLIN) {
2effd274 528 new_connection(server_fd, 0);
85f07f22
FB
529 }
530 poll_entry++;
2effd274
FB
531 /* new RTSP connection request ? */
532 if (poll_entry->revents & POLLIN) {
533 new_connection(rtsp_server_fd, 1);
534 }
85f07f22
FB
535 }
536}
537
2effd274
FB
538/* start waiting for a new HTTP/RTSP request */
539static void start_wait_request(HTTPContext *c, int is_rtsp)
85f07f22 540{
2effd274
FB
541 c->buffer_ptr = c->buffer;
542 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
543
544 if (is_rtsp) {
545 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
546 c->state = RTSPSTATE_WAIT_REQUEST;
547 } else {
548 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
549 c->state = HTTPSTATE_WAIT_REQUEST;
550 }
551}
552
553static void new_connection(int server_fd, int is_rtsp)
554{
555 struct sockaddr_in from_addr;
556 int fd, len;
557 HTTPContext *c = NULL;
558
559 len = sizeof(from_addr);
560 fd = accept(server_fd, (struct sockaddr *)&from_addr,
561 &len);
562 if (fd < 0)
563 return;
564 fcntl(fd, F_SETFL, O_NONBLOCK);
565
566 /* XXX: should output a warning page when coming
567 close to the connection limit */
568 if (nb_connections >= nb_max_connections)
569 goto fail;
570
571 /* add a new connection */
572 c = av_mallocz(sizeof(HTTPContext));
573 if (!c)
574 goto fail;
575
576 c->next = first_http_ctx;
577 first_http_ctx = c;
578 c->fd = fd;
579 c->poll_entry = NULL;
580 c->from_addr = from_addr;
581 c->buffer_size = IOBUFFER_INIT_SIZE;
582 c->buffer = av_malloc(c->buffer_size);
583 if (!c->buffer)
584 goto fail;
585 nb_connections++;
586
587 start_wait_request(c, is_rtsp);
588
589 return;
590
591 fail:
592 if (c) {
593 av_free(c->buffer);
594 av_free(c);
595 }
596 close(fd);
597}
598
599static void close_connection(HTTPContext *c)
600{
601 HTTPContext **cp, *c1;
602 int i, nb_streams;
603 AVFormatContext *ctx;
604 URLContext *h;
605 AVStream *st;
606
607 /* remove connection from list */
608 cp = &first_http_ctx;
609 while ((*cp) != NULL) {
610 c1 = *cp;
611 if (c1 == c) {
612 *cp = c->next;
613 } else {
614 cp = &c1->next;
615 }
616 }
617
618 /* remove connection associated resources */
619 if (c->fd >= 0)
620 close(c->fd);
621 if (c->fmt_in) {
622 /* close each frame parser */
623 for(i=0;i<c->fmt_in->nb_streams;i++) {
624 st = c->fmt_in->streams[i];
625 if (st->codec.codec) {
626 avcodec_close(&st->codec);
627 }
628 }
629 av_close_input_file(c->fmt_in);
630 }
631
632 /* free RTP output streams if any */
633 nb_streams = 0;
634 if (c->stream)
635 nb_streams = c->stream->nb_streams;
636
637 for(i=0;i<nb_streams;i++) {
638 ctx = c->rtp_ctx[i];
639 if (ctx) {
640 av_write_trailer(ctx);
641 av_free(ctx);
642 }
643 h = c->rtp_handles[i];
644 if (h) {
645 url_close(h);
646 }
647 }
648
649 nb_bandwidth -= c->bandwidth;
650 av_freep(&c->pb_buffer);
651 av_free(c->buffer);
652 av_free(c);
653 nb_connections--;
654}
655
656static int handle_connection(HTTPContext *c)
657{
658 int len, ret;
85f07f22
FB
659
660 switch(c->state) {
661 case HTTPSTATE_WAIT_REQUEST:
2effd274 662 case RTSPSTATE_WAIT_REQUEST:
85f07f22
FB
663 /* timeout ? */
664 if ((c->timeout - cur_time) < 0)
665 return -1;
666 if (c->poll_entry->revents & (POLLERR | POLLHUP))
667 return -1;
668
669 /* no need to read if no events */
670 if (!(c->poll_entry->revents & POLLIN))
671 return 0;
672 /* read the data */
673 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
674 if (len < 0) {
675 if (errno != EAGAIN && errno != EINTR)
676 return -1;
677 } else if (len == 0) {
678 return -1;
679 } else {
680 /* search for end of request. XXX: not fully correct since garbage could come after the end */
681 UINT8 *ptr;
682 c->buffer_ptr += len;
683 ptr = c->buffer_ptr;
684 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
685 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
686 /* request found : parse it and reply */
2effd274
FB
687 if (c->state == HTTPSTATE_WAIT_REQUEST) {
688 ret = http_parse_request(c);
689 } else {
690 ret = rtsp_parse_request(c);
691 }
692 if (ret < 0)
85f07f22
FB
693 return -1;
694 } else if (ptr >= c->buffer_end) {
695 /* request too long: cannot do anything */
696 return -1;
697 }
698 }
699 break;
700
701 case HTTPSTATE_SEND_HEADER:
702 if (c->poll_entry->revents & (POLLERR | POLLHUP))
703 return -1;
704
2effd274 705 /* no need to write if no events */
85f07f22
FB
706 if (!(c->poll_entry->revents & POLLOUT))
707 return 0;
708 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
709 if (len < 0) {
710 if (errno != EAGAIN && errno != EINTR) {
711 /* error : close connection */
2effd274 712 av_freep(&c->pb_buffer);
85f07f22
FB
713 return -1;
714 }
715 } else {
716 c->buffer_ptr += len;
2e04edb3
PG
717 if (c->stream)
718 c->stream->bytes_served += len;
a6e14edd 719 c->data_count += len;
85f07f22 720 if (c->buffer_ptr >= c->buffer_end) {
2effd274 721 av_freep(&c->pb_buffer);
85f07f22 722 /* if error, exit */
2effd274 723 if (c->http_error) {
85f07f22 724 return -1;
2effd274
FB
725 }
726 /* all the buffer was sent : synchronize to the incoming stream */
85f07f22
FB
727 c->state = HTTPSTATE_SEND_DATA_HEADER;
728 c->buffer_ptr = c->buffer_end = c->buffer;
729 }
730 }
731 break;
732
733 case HTTPSTATE_SEND_DATA:
734 case HTTPSTATE_SEND_DATA_HEADER:
735 case HTTPSTATE_SEND_DATA_TRAILER:
2effd274
FB
736 /* for packetized output, we consider we can always write (the
737 input streams sets the speed). It may be better to verify
738 that we do not rely too much on the kernel queues */
739 if (!c->is_packetized) {
740 if (c->poll_entry->revents & (POLLERR | POLLHUP))
741 return -1;
742
743 /* no need to read if no events */
744 if (!(c->poll_entry->revents & POLLOUT))
745 return 0;
746 }
5eb765ef 747 if (http_send_data(c) < 0)
85f07f22
FB
748 return -1;
749 break;
750 case HTTPSTATE_RECEIVE_DATA:
751 /* no need to read if no events */
752 if (c->poll_entry->revents & (POLLERR | POLLHUP))
753 return -1;
754 if (!(c->poll_entry->revents & POLLIN))
755 return 0;
756 if (http_receive_data(c) < 0)
757 return -1;
758 break;
759 case HTTPSTATE_WAIT_FEED:
760 /* no need to read if no events */
a6e14edd 761 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
85f07f22
FB
762 return -1;
763
764 /* nothing to do, we'll be waken up by incoming feed packets */
765 break;
2effd274
FB
766
767 case HTTPSTATE_WAIT:
768 /* if the delay expired, we can send new packets */
769 if (compute_send_delay(c) <= 0)
770 c->state = HTTPSTATE_SEND_DATA;
771 break;
772 case HTTPSTATE_WAIT_SHORT:
773 /* just return back to send data */
774 c->state = HTTPSTATE_SEND_DATA;
775 break;
776
777 case RTSPSTATE_SEND_REPLY:
778 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
779 av_freep(&c->pb_buffer);
780 return -1;
781 }
782 /* no need to write if no events */
783 if (!(c->poll_entry->revents & POLLOUT))
784 return 0;
785 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
786 if (len < 0) {
787 if (errno != EAGAIN && errno != EINTR) {
788 /* error : close connection */
789 av_freep(&c->pb_buffer);
790 return -1;
791 }
792 } else {
793 c->buffer_ptr += len;
794 c->data_count += len;
795 if (c->buffer_ptr >= c->buffer_end) {
796 /* all the buffer was sent : wait for a new request */
797 av_freep(&c->pb_buffer);
798 start_wait_request(c, 1);
799 }
800 }
801 break;
802 case HTTPSTATE_READY:
803 /* nothing to do */
804 break;
85f07f22
FB
805 default:
806 return -1;
807 }
808 return 0;
809}
810
3120d2a2
PG
811static int extract_rates(char *rates, int ratelen, const char *request)
812{
813 const char *p;
814
815 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
816 if (strncasecmp(p, "Pragma:", 7) == 0) {
817 const char *q = p + 7;
818
819 while (*q && *q != '\n' && isspace(*q))
820 q++;
821
822 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
823 int stream_no;
824 int rate_no;
825
826 q += 20;
827
cde25790 828 memset(rates, 0xff, ratelen);
3120d2a2
PG
829
830 while (1) {
831 while (*q && *q != '\n' && *q != ':')
832 q++;
833
834 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
835 break;
836 }
837 stream_no--;
838 if (stream_no < ratelen && stream_no >= 0) {
839 rates[stream_no] = rate_no;
840 }
841
842 while (*q && *q != '\n' && !isspace(*q))
843 q++;
844 }
845
846 return 1;
847 }
848 }
849 p = strchr(p, '\n');
850 if (!p)
851 break;
852
853 p++;
854 }
855
856 return 0;
857}
858
cde25790 859static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
3120d2a2
PG
860{
861 int i;
cde25790
PG
862 int best_bitrate = 100000000;
863 int best = -1;
864
865 for (i = 0; i < feed->nb_streams; i++) {
866 AVCodecContext *feed_codec = &feed->streams[i]->codec;
867
868 if (feed_codec->codec_id != codec->codec_id ||
869 feed_codec->sample_rate != codec->sample_rate ||
870 feed_codec->width != codec->width ||
871 feed_codec->height != codec->height) {
872 continue;
873 }
874
875 /* Potential stream */
876
877 /* We want the fastest stream less than bit_rate, or the slowest
878 * faster than bit_rate
879 */
880
881 if (feed_codec->bit_rate <= bit_rate) {
882 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
883 best_bitrate = feed_codec->bit_rate;
884 best = i;
885 }
886 } else {
887 if (feed_codec->bit_rate < best_bitrate) {
888 best_bitrate = feed_codec->bit_rate;
889 best = i;
890 }
891 }
892 }
893
894 return best;
895}
896
897static int modify_current_stream(HTTPContext *c, char *rates)
898{
899 int i;
900 FFStream *req = c->stream;
901 int action_required = 0;
3120d2a2
PG
902
903 for (i = 0; i < req->nb_streams; i++) {
904 AVCodecContext *codec = &req->streams[i]->codec;
905
3120d2a2
PG
906 switch(rates[i]) {
907 case 0:
cde25790 908 c->switch_feed_streams[i] = req->feed_streams[i];
3120d2a2
PG
909 break;
910 case 1:
cde25790 911 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
3120d2a2
PG
912 break;
913 case 2:
cde25790
PG
914 /* Wants off or slow */
915 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
916#ifdef WANTS_OFF
917 /* This doesn't work well when it turns off the only stream! */
918 c->switch_feed_streams[i] = -2;
919 c->feed_streams[i] = -2;
920#endif
3120d2a2
PG
921 break;
922 }
3120d2a2 923
cde25790
PG
924 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
925 action_required = 1;
926 }
3120d2a2 927
cde25790
PG
928 return action_required;
929}
3120d2a2 930
3120d2a2 931
cde25790
PG
932static void do_switch_stream(HTTPContext *c, int i)
933{
934 if (c->switch_feed_streams[i] >= 0) {
935#ifdef PHILIP
936 c->feed_streams[i] = c->switch_feed_streams[i];
937#endif
3120d2a2 938
cde25790 939 /* Now update the stream */
3120d2a2 940 }
cde25790 941 c->switch_feed_streams[i] = -1;
3120d2a2 942}
7434ba6d 943
2effd274
FB
944/* XXX: factorize in utils.c ? */
945/* XXX: take care with different space meaning */
946static void skip_spaces(const char **pp)
947{
948 const char *p;
949 p = *pp;
950 while (*p == ' ' || *p == '\t')
951 p++;
952 *pp = p;
953}
954
955static void get_word(char *buf, int buf_size, const char **pp)
956{
957 const char *p;
958 char *q;
959
960 p = *pp;
961 skip_spaces(&p);
962 q = buf;
963 while (!isspace(*p) && *p != '\0') {
964 if ((q - buf) < buf_size - 1)
965 *q++ = *p;
966 p++;
967 }
968 if (buf_size > 0)
969 *q = '\0';
970 *pp = p;
971}
972
8256c0a3
PG
973static int validate_acl(FFStream *stream, HTTPContext *c)
974{
975 enum IPAddressAction last_action = IP_DENY;
976 IPAddressACL *acl;
977 struct in_addr *src = &c->from_addr.sin_addr;
978
979 for (acl = stream->acl; acl; acl = acl->next) {
980 if (src->s_addr >= acl->first.s_addr && src->s_addr <= acl->last.s_addr) {
981 return (acl->action == IP_ALLOW) ? 1 : 0;
982 }
983 last_action = acl->action;
984 }
985
986 /* Nothing matched, so return not the last action */
987 return (last_action == IP_DENY) ? 1 : 0;
988}
989
85f07f22
FB
990/* parse http request and prepare header */
991static int http_parse_request(HTTPContext *c)
992{
993 char *p;
994 int post;
7434ba6d 995 int doing_asx;
cde25790 996 int doing_asf_redirector;
42a63c6a 997 int doing_ram;
2effd274 998 int doing_rtsp_redirector;
85f07f22
FB
999 char cmd[32];
1000 char info[1024], *filename;
1001 char url[1024], *q;
1002 char protocol[32];
1003 char msg[1024];
1004 const char *mime_type;
1005 FFStream *stream;
42a63c6a 1006 int i;
3120d2a2 1007 char ratebuf[32];
cde25790 1008 char *useragent = 0;
85f07f22
FB
1009
1010 p = c->buffer;
2effd274 1011 get_word(cmd, sizeof(cmd), (const char **)&p);
bd7cf6ad 1012 pstrcpy(c->method, sizeof(c->method), cmd);
7434ba6d 1013
85f07f22
FB
1014 if (!strcmp(cmd, "GET"))
1015 post = 0;
1016 else if (!strcmp(cmd, "POST"))
1017 post = 1;
1018 else
1019 return -1;
1020
2effd274 1021 get_word(url, sizeof(url), (const char **)&p);
bd7cf6ad 1022 pstrcpy(c->url, sizeof(c->url), url);
7434ba6d 1023
2effd274 1024 get_word(protocol, sizeof(protocol), (const char **)&p);
85f07f22
FB
1025 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1026 return -1;
7434ba6d 1027
bd7cf6ad 1028 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
85f07f22
FB
1029
1030 /* find the filename and the optional info string in the request */
1031 p = url;
1032 if (*p == '/')
1033 p++;
1034 filename = p;
1035 p = strchr(p, '?');
1036 if (p) {
bd7cf6ad 1037 pstrcpy(info, sizeof(info), p);
85f07f22
FB
1038 *p = '\0';
1039 } else {
1040 info[0] = '\0';
1041 }
1042
cde25790
PG
1043 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1044 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1045 useragent = p + 11;
1046 if (*useragent && *useragent != '\n' && isspace(*useragent))
1047 useragent++;
1048 break;
1049 }
1050 p = strchr(p, '\n');
1051 if (!p)
1052 break;
1053
1054 p++;
1055 }
1056
7434ba6d
PG
1057 if (strlen(filename) > 4 && strcmp(".asx", filename + strlen(filename) - 4) == 0) {
1058 doing_asx = 1;
1059 filename[strlen(filename)-1] = 'f';
1060 } else {
1061 doing_asx = 0;
1062 }
1063
cde25790
PG
1064 if (strlen(filename) > 4 && strcmp(".asf", filename + strlen(filename) - 4) == 0 &&
1065 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1066 /* if this isn't WMP or lookalike, return the redirector file */
1067 doing_asf_redirector = 1;
1068 } else {
1069 doing_asf_redirector = 0;
1070 }
1071
42a63c6a
PG
1072 if (strlen(filename) > 4 &&
1073 (strcmp(".rpm", filename + strlen(filename) - 4) == 0 ||
1074 strcmp(".ram", filename + strlen(filename) - 4) == 0)) {
1075 doing_ram = 1;
1076 strcpy(filename + strlen(filename)-2, "m");
1077 } else {
1078 doing_ram = 0;
1079 }
1080
2effd274
FB
1081 if (strlen(filename) > 5 &&
1082 strcmp(".rtsp", filename + strlen(filename) - 5) == 0) {
1083 char file1[1024];
1084 char file2[1024];
1085 char *p;
1086
1087 doing_rtsp_redirector = 1;
1088 /* compute filename by matching without the file extensions */
1089 pstrcpy(file1, sizeof(file1), filename);
1090 p = strrchr(file1, '.');
1091 if (p)
1092 *p = '\0';
1093 for(stream = first_stream; stream != NULL; stream = stream->next) {
1094 pstrcpy(file2, sizeof(file2), stream->filename);
1095 p = strrchr(file2, '.');
1096 if (p)
1097 *p = '\0';
1098 if (!strcmp(file1, file2)) {
1099 pstrcpy(url, sizeof(url), stream->filename);
1100 filename = url;
1101 break;
1102 }
1103 }
1104 } else {
1105 doing_rtsp_redirector = 0;
1106 }
1107
85f07f22
FB
1108 stream = first_stream;
1109 while (stream != NULL) {
8256c0a3 1110 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
85f07f22
FB
1111 break;
1112 stream = stream->next;
1113 }
1114 if (stream == NULL) {
1115 sprintf(msg, "File '%s' not found", url);
1116 goto send_error;
1117 }
42a63c6a 1118
cde25790
PG
1119 c->stream = stream;
1120 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1121 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1122
1123 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1124 c->http_error = 301;
1125 q = c->buffer;
1126 q += sprintf(q, "HTTP/1.0 301 Moved\r\n");
1127 q += sprintf(q, "Location: %s\r\n", stream->feed_filename);
1128 q += sprintf(q, "Content-type: text/html\r\n");
1129 q += sprintf(q, "\r\n");
1130 q += sprintf(q, "<html><head><title>Moved</title></head><body>\r\n");
1131 q += sprintf(q, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1132 q += sprintf(q, "</body></html>\r\n");
1133
1134 /* prepare output buffer */
1135 c->buffer_ptr = c->buffer;
1136 c->buffer_end = q;
1137 c->state = HTTPSTATE_SEND_HEADER;
1138 return 0;
1139 }
1140
3120d2a2
PG
1141 /* If this is WMP, get the rate information */
1142 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
cde25790
PG
1143 if (modify_current_stream(c, ratebuf)) {
1144 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1145 if (c->switch_feed_streams[i] >= 0)
1146 do_switch_stream(c, i);
1147 }
1148 }
3120d2a2
PG
1149 }
1150
42a63c6a
PG
1151 if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1152 /* See if we meet the bandwidth requirements */
1153 for(i=0;i<stream->nb_streams;i++) {
1154 AVStream *st = stream->streams[i];
1155 switch(st->codec.codec_type) {
1156 case CODEC_TYPE_AUDIO:
1157 c->bandwidth += st->codec.bit_rate;
1158 break;
1159 case CODEC_TYPE_VIDEO:
1160 c->bandwidth += st->codec.bit_rate;
1161 break;
1162 default:
ec3b2232 1163 av_abort();
42a63c6a
PG
1164 }
1165 }
1166 }
1167
1168 c->bandwidth /= 1000;
1169 nb_bandwidth += c->bandwidth;
1170
1171 if (post == 0 && nb_max_bandwidth < nb_bandwidth) {
1172 c->http_error = 200;
1173 q = c->buffer;
1174 q += sprintf(q, "HTTP/1.0 200 Server too busy\r\n");
1175 q += sprintf(q, "Content-type: text/html\r\n");
1176 q += sprintf(q, "\r\n");
1177 q += sprintf(q, "<html><head><title>Too busy</title></head><body>\r\n");
1178 q += sprintf(q, "The server is too busy to serve your request at this time.<p>\r\n");
1179 q += sprintf(q, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
1180 nb_bandwidth, nb_max_bandwidth);
1181 q += sprintf(q, "</body></html>\r\n");
1182
1183 /* prepare output buffer */
1184 c->buffer_ptr = c->buffer;
1185 c->buffer_end = q;
1186 c->state = HTTPSTATE_SEND_HEADER;
1187 return 0;
1188 }
1189
2effd274
FB
1190 if (doing_asx || doing_ram || doing_asf_redirector ||
1191 doing_rtsp_redirector) {
7434ba6d
PG
1192 char *hostinfo = 0;
1193
1194 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1195 if (strncasecmp(p, "Host:", 5) == 0) {
1196 hostinfo = p + 5;
1197 break;
1198 }
1199 p = strchr(p, '\n');
1200 if (!p)
1201 break;
1202
1203 p++;
1204 }
1205
1206 if (hostinfo) {
1207 char *eoh;
1208 char hostbuf[260];
1209
1210 while (isspace(*hostinfo))
1211 hostinfo++;
1212
1213 eoh = strchr(hostinfo, '\n');
1214 if (eoh) {
1215 if (eoh[-1] == '\r')
1216 eoh--;
1217
1218 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1219 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1220 hostbuf[eoh - hostinfo] = 0;
1221
1222 c->http_error = 200;
1223 q = c->buffer;
42a63c6a
PG
1224 if (doing_asx) {
1225 q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n");
1226 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1227 q += sprintf(q, "\r\n");
1228 q += sprintf(q, "<ASX Version=\"3\">\r\n");
1229 q += sprintf(q, "<!-- Autogenerated by ffserver -->\r\n");
1230 q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1231 hostbuf, filename, info);
1232 q += sprintf(q, "</ASX>\r\n");
1233 } else if (doing_ram) {
1234 q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n");
1235 q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n");
1236 q += sprintf(q, "\r\n");
1237 q += sprintf(q, "# Autogenerated by ffserver\r\n");
1238 q += sprintf(q, "http://%s/%s%s\r\n",
1239 hostbuf, filename, info);
cde25790
PG
1240 } else if (doing_asf_redirector) {
1241 q += sprintf(q, "HTTP/1.0 200 ASF Redirect follows\r\n");
1242 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1243 q += sprintf(q, "\r\n");
1244 q += sprintf(q, "[Reference]\r\n");
1245 q += sprintf(q, "Ref1=http://%s/%s%s\r\n",
1246 hostbuf, filename, info);
2effd274
FB
1247 } else if (doing_rtsp_redirector) {
1248 char hostname[256], *p;
1249 /* extract only hostname */
1250 pstrcpy(hostname, sizeof(hostname), hostbuf);
1251 p = strrchr(hostname, ':');
1252 if (p)
1253 *p = '\0';
1254 q += sprintf(q, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1255 /* XXX: incorrect mime type ? */
1256 q += sprintf(q, "Content-type: application/x-rtsp\r\n");
1257 q += sprintf(q, "\r\n");
1258 q += sprintf(q, "rtsp://%s:%d/%s\r\n",
1259 hostname, ntohs(my_rtsp_addr.sin_port),
1260 filename);
1261 } else {
ec3b2232 1262 av_abort();
2effd274 1263 }
7434ba6d
PG
1264
1265 /* prepare output buffer */
1266 c->buffer_ptr = c->buffer;
1267 c->buffer_end = q;
1268 c->state = HTTPSTATE_SEND_HEADER;
1269 return 0;
1270 }
1271 }
1272 }
1273
42a63c6a 1274 sprintf(msg, "ASX/RAM file not handled");
7434ba6d 1275 goto send_error;
85f07f22
FB
1276 }
1277
a6e14edd 1278 stream->conns_served++;
7434ba6d 1279
85f07f22
FB
1280 /* XXX: add there authenticate and IP match */
1281
1282 if (post) {
1283 /* if post, it means a feed is being sent */
1284 if (!stream->is_feed) {
7434ba6d
PG
1285 /* However it might be a status report from WMP! Lets log the data
1286 * as it might come in handy one day
1287 */
1288 char *logline = 0;
3120d2a2 1289 int client_id = 0;
7434ba6d
PG
1290
1291 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1292 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1293 logline = p;
1294 break;
1295 }
3120d2a2
PG
1296 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1297 client_id = strtol(p + 18, 0, 10);
1298 }
7434ba6d
PG
1299 p = strchr(p, '\n');
1300 if (!p)
1301 break;
1302
1303 p++;
1304 }
1305
1306 if (logline) {
1307 char *eol = strchr(logline, '\n');
1308
1309 logline += 17;
1310
1311 if (eol) {
1312 if (eol[-1] == '\r')
1313 eol--;
1314 http_log("%.*s\n", eol - logline, logline);
1315 c->suppress_log = 1;
1316 }
1317 }
3120d2a2 1318
cde25790
PG
1319#ifdef DEBUG_WMP
1320 http_log("\nGot request:\n%s\n", c->buffer);
3120d2a2
PG
1321#endif
1322
1323 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1324 HTTPContext *wmpc;
1325
1326 /* Now we have to find the client_id */
1327 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1328 if (wmpc->wmp_client_id == client_id)
1329 break;
1330 }
1331
1332 if (wmpc) {
cde25790
PG
1333 if (modify_current_stream(wmpc, ratebuf)) {
1334 wmpc->switch_pending = 1;
3120d2a2
PG
1335 }
1336 }
1337 }
7434ba6d 1338
85f07f22
FB
1339 sprintf(msg, "POST command not handled");
1340 goto send_error;
1341 }
1342 if (http_start_receive_data(c) < 0) {
1343 sprintf(msg, "could not open feed");
1344 goto send_error;
1345 }
1346 c->http_error = 0;
1347 c->state = HTTPSTATE_RECEIVE_DATA;
1348 return 0;
1349 }
1350
cde25790 1351#ifdef DEBUG_WMP
3120d2a2 1352 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
cde25790 1353 http_log("\nGot request:\n%s\n", c->buffer);
3120d2a2
PG
1354 }
1355#endif
1356
85f07f22
FB
1357 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1358 goto send_stats;
1359
1360 /* open input stream */
1361 if (open_input_stream(c, info) < 0) {
1362 sprintf(msg, "Input stream corresponding to '%s' not found", url);
1363 goto send_error;
1364 }
1365
1366 /* prepare http header */
1367 q = c->buffer;
1368 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1369 mime_type = c->stream->fmt->mime_type;
1370 if (!mime_type)
1371 mime_type = "application/x-octet_stream";
85f07f22
FB
1372 q += sprintf(q, "Pragma: no-cache\r\n");
1373
1374 /* for asf, we need extra headers */
8256c0a3 1375 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
3120d2a2 1376 /* Need to allocate a client id */
3120d2a2 1377
8256c0a3 1378 c->wmp_client_id = random() & 0x7fffffff;
3120d2a2
PG
1379
1380 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 1381 mime_type = "application/octet-stream";
85f07f22 1382 }
f747e6d3 1383 q += sprintf(q, "Content-Type: %s\r\n", mime_type);
85f07f22
FB
1384 q += sprintf(q, "\r\n");
1385
1386 /* prepare output buffer */
1387 c->http_error = 0;
1388 c->buffer_ptr = c->buffer;
1389 c->buffer_end = q;
1390 c->state = HTTPSTATE_SEND_HEADER;
1391 return 0;
1392 send_error:
1393 c->http_error = 404;
1394 q = c->buffer;
1395 q += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
1396 q += sprintf(q, "Content-type: %s\r\n", "text/html");
1397 q += sprintf(q, "\r\n");
1398 q += sprintf(q, "<HTML>\n");
1399 q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1400 q += sprintf(q, "<BODY>%s</BODY>\n", msg);
1401 q += sprintf(q, "</HTML>\n");
1402
1403 /* prepare output buffer */
1404 c->buffer_ptr = c->buffer;
1405 c->buffer_end = q;
1406 c->state = HTTPSTATE_SEND_HEADER;
1407 return 0;
1408 send_stats:
1409 compute_stats(c);
1410 c->http_error = 200; /* horrible : we use this value to avoid
1411 going to the send data state */
1412 c->state = HTTPSTATE_SEND_HEADER;
1413 return 0;
1414}
1415
2effd274 1416static void fmt_bytecount(ByteIOContext *pb, INT64 count)
2ac887ba
PG
1417{
1418 static const char *suffix = " kMGTP";
1419 const char *s;
1420
1421 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1422 }
1423
2effd274 1424 url_fprintf(pb, "%lld%c", count, *s);
2ac887ba
PG
1425}
1426
85f07f22
FB
1427static void compute_stats(HTTPContext *c)
1428{
1429 HTTPContext *c1;
1430 FFStream *stream;
2effd274 1431 char *p;
85f07f22 1432 time_t ti;
2effd274
FB
1433 int i, len;
1434 ByteIOContext pb1, *pb = &pb1;
cde25790 1435
2effd274
FB
1436 if (url_open_dyn_buf(pb) < 0) {
1437 /* XXX: return an error ? */
cde25790 1438 c->buffer_ptr = c->buffer;
2effd274
FB
1439 c->buffer_end = c->buffer;
1440 return;
cde25790 1441 }
85f07f22 1442
2effd274
FB
1443 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1444 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1445 url_fprintf(pb, "Pragma: no-cache\r\n");
1446 url_fprintf(pb, "\r\n");
85f07f22 1447
2effd274 1448 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
cde25790 1449 if (c->stream->feed_filename) {
2effd274 1450 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
cde25790 1451 }
2effd274
FB
1452 url_fprintf(pb, "</HEAD>\n<BODY>");
1453 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
85f07f22 1454 /* format status */
2effd274
FB
1455 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1456 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1457 url_fprintf(pb, "<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");
85f07f22
FB
1458 stream = first_stream;
1459 while (stream != NULL) {
42a63c6a
PG
1460 char sfilename[1024];
1461 char *eosf;
1462
a6e14edd 1463 if (stream->feed != stream) {
2effd274 1464 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
a6e14edd
PG
1465 eosf = sfilename + strlen(sfilename);
1466 if (eosf - sfilename >= 4) {
1467 if (strcmp(eosf - 4, ".asf") == 0) {
1468 strcpy(eosf - 4, ".asx");
1469 } else if (strcmp(eosf - 3, ".rm") == 0) {
1470 strcpy(eosf - 3, ".ram");
2effd274
FB
1471 } else if (stream->fmt == &rtp_mux) {
1472 /* generate a sample RTSP director - maybe should
1473 generate a .sdp file ? */
1474 eosf = strrchr(sfilename, '.');
1475 if (!eosf)
1476 eosf = sfilename + strlen(sfilename);
1477 strcpy(eosf, ".rtsp");
a6e14edd 1478 }
42a63c6a 1479 }
a6e14edd 1480
2effd274 1481 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
a6e14edd 1482 sfilename, stream->filename);
2effd274 1483 url_fprintf(pb, "<td align=right> %d <td align=right> ",
2ac887ba 1484 stream->conns_served);
2effd274 1485 fmt_bytecount(pb, stream->bytes_served);
a6e14edd
PG
1486 switch(stream->stream_type) {
1487 case STREAM_TYPE_LIVE:
1488 {
1489 int audio_bit_rate = 0;
1490 int video_bit_rate = 0;
1491 char *audio_codec_name = "";
1492 char *video_codec_name = "";
1493 char *audio_codec_name_extra = "";
1494 char *video_codec_name_extra = "";
1495
1496 for(i=0;i<stream->nb_streams;i++) {
1497 AVStream *st = stream->streams[i];
1498 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1499 switch(st->codec.codec_type) {
1500 case CODEC_TYPE_AUDIO:
1501 audio_bit_rate += st->codec.bit_rate;
1502 if (codec) {
1503 if (*audio_codec_name)
1504 audio_codec_name_extra = "...";
1505 audio_codec_name = codec->name;
1506 }
1507 break;
1508 case CODEC_TYPE_VIDEO:
1509 video_bit_rate += st->codec.bit_rate;
1510 if (codec) {
1511 if (*video_codec_name)
1512 video_codec_name_extra = "...";
1513 video_codec_name = codec->name;
1514 }
1515 break;
1516 default:
ec3b2232 1517 av_abort();
79c4ea3c 1518 }
85f07f22 1519 }
2effd274 1520 url_fprintf(pb, "<TD align=center> %s <TD align=right> %d <TD align=right> %d <TD> %s %s <TD align=right> %d <TD> %s %s",
a6e14edd
PG
1521 stream->fmt->name,
1522 (audio_bit_rate + video_bit_rate) / 1000,
1523 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1524 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1525 if (stream->feed) {
2effd274 1526 url_fprintf(pb, "<TD>%s", stream->feed->filename);
a6e14edd 1527 } else {
2effd274 1528 url_fprintf(pb, "<TD>%s", stream->feed_filename);
a6e14edd 1529 }
2effd274 1530 url_fprintf(pb, "\n");
85f07f22 1531 }
a6e14edd
PG
1532 break;
1533 default:
2effd274 1534 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
a6e14edd 1535 break;
85f07f22 1536 }
85f07f22
FB
1537 }
1538 stream = stream->next;
1539 }
2effd274 1540 url_fprintf(pb, "</TABLE>\n");
a6e14edd
PG
1541
1542 stream = first_stream;
1543 while (stream != NULL) {
1544 if (stream->feed == stream) {
2effd274 1545 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
cde25790 1546 if (stream->pid) {
2effd274 1547 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
cde25790 1548
2effd274
FB
1549#if defined(linux) && !defined(CONFIG_NOCUTILS)
1550 {
1551 FILE *pid_stat;
1552 char ps_cmd[64];
1553
1554 /* This is somewhat linux specific I guess */
1555 snprintf(ps_cmd, sizeof(ps_cmd),
1556 "ps -o \"%%cpu,cputime\" --no-headers %d",
1557 stream->pid);
1558
1559 pid_stat = popen(ps_cmd, "r");
1560 if (pid_stat) {
1561 char cpuperc[10];
1562 char cpuused[64];
1563
1564 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1565 cpuused) == 2) {
1566 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1567 cpuperc, cpuused);
1568 }
1569 fclose(pid_stat);
cde25790 1570 }
cde25790
PG
1571 }
1572#endif
1573
2effd274 1574 url_fprintf(pb, "<p>");
cde25790 1575 }
2effd274 1576 url_fprintf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
a6e14edd
PG
1577
1578 for (i = 0; i < stream->nb_streams; i++) {
1579 AVStream *st = stream->streams[i];
1580 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1581 char *type = "unknown";
b582f314
PG
1582 char parameters[64];
1583
1584 parameters[0] = 0;
a6e14edd
PG
1585
1586 switch(st->codec.codec_type) {
1587 case CODEC_TYPE_AUDIO:
1588 type = "audio";
1589 break;
1590 case CODEC_TYPE_VIDEO:
1591 type = "video";
cde25790
PG
1592 sprintf(parameters, "%dx%d, q=%d-%d, fps=%d", st->codec.width, st->codec.height,
1593 st->codec.qmin, st->codec.qmax, st->codec.frame_rate / FRAME_RATE_BASE);
a6e14edd
PG
1594 break;
1595 default:
ec3b2232 1596 av_abort();
a6e14edd 1597 }
2effd274 1598 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
b582f314 1599 i, type, st->codec.bit_rate/1000, codec ? codec->name : "", parameters);
a6e14edd 1600 }
2effd274 1601 url_fprintf(pb, "</table>\n");
a6e14edd
PG
1602
1603 }
1604 stream = stream->next;
1605 }
85f07f22
FB
1606
1607#if 0
1608 {
1609 float avg;
1610 AVCodecContext *enc;
1611 char buf[1024];
1612
1613 /* feed status */
1614 stream = first_feed;
1615 while (stream != NULL) {
2effd274
FB
1616 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1617 url_fprintf(pb, "<TABLE>\n");
1618 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
85f07f22
FB
1619 for(i=0;i<stream->nb_streams;i++) {
1620 AVStream *st = stream->streams[i];
1621 FeedData *fdata = st->priv_data;
1622 enc = &st->codec;
1623
1624 avcodec_string(buf, sizeof(buf), enc);
1625 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1626 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1627 avg /= enc->frame_size;
2effd274 1628 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
85f07f22
FB
1629 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1630 }
2effd274 1631 url_fprintf(pb, "</TABLE>\n");
85f07f22
FB
1632 stream = stream->next_feed;
1633 }
1634 }
1635#endif
1636
1637 /* connection status */
2effd274 1638 url_fprintf(pb, "<H2>Connection Status</H2>\n");
85f07f22 1639
2effd274 1640 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
85f07f22
FB
1641 nb_connections, nb_max_connections);
1642
2effd274 1643 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
42a63c6a
PG
1644 nb_bandwidth, nb_max_bandwidth);
1645
2effd274
FB
1646 url_fprintf(pb, "<TABLE>\n");
1647 url_fprintf(pb, "<TR><th>#<th>File<th>IP<th>Proto<th>State<th>Target bits/sec<th>Actual bits/sec<th>Bytes transferred\n");
85f07f22
FB
1648 c1 = first_http_ctx;
1649 i = 0;
2effd274 1650 while (c1 != NULL) {
cde25790
PG
1651 int bitrate;
1652 int j;
1653
1654 bitrate = 0;
2effd274
FB
1655 if (c1->stream) {
1656 for (j = 0; j < c1->stream->nb_streams; j++) {
1657 if (!c1->stream->feed) {
1658 bitrate += c1->stream->streams[j]->codec.bit_rate;
1659 } else {
1660 if (c1->feed_streams[j] >= 0) {
1661 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec.bit_rate;
1662 }
1663 }
cde25790
PG
1664 }
1665 }
1666
85f07f22
FB
1667 i++;
1668 p = inet_ntoa(c1->from_addr.sin_addr);
2effd274
FB
1669 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1670 i,
1671 c1->stream ? c1->stream->filename : "",
1672 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1673 p,
1674 c1->protocol,
1675 http_state[c1->state]);
1676 fmt_bytecount(pb, bitrate);
1677 url_fprintf(pb, "<td align=right>");
1678 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1679 url_fprintf(pb, "<td align=right>");
1680 fmt_bytecount(pb, c1->data_count);
1681 url_fprintf(pb, "\n");
85f07f22
FB
1682 c1 = c1->next;
1683 }
2effd274 1684 url_fprintf(pb, "</TABLE>\n");
85f07f22
FB
1685
1686 /* date */
1687 ti = time(NULL);
1688 p = ctime(&ti);
2effd274
FB
1689 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1690 url_fprintf(pb, "</BODY>\n</HTML>\n");
85f07f22 1691
2effd274
FB
1692 len = url_close_dyn_buf(pb, &c->pb_buffer);
1693 c->buffer_ptr = c->pb_buffer;
1694 c->buffer_end = c->pb_buffer + len;
85f07f22
FB
1695}
1696
2effd274
FB
1697/* check if the parser needs to be opened for stream i */
1698static void open_parser(AVFormatContext *s, int i)
85f07f22 1699{
2effd274
FB
1700 AVStream *st = s->streams[i];
1701 AVCodec *codec;
31def229 1702
2effd274
FB
1703 if (!st->codec.codec) {
1704 codec = avcodec_find_decoder(st->codec.codec_id);
1705 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1706 st->codec.parse_only = 1;
1707 if (avcodec_open(&st->codec, codec) < 0) {
1708 st->codec.parse_only = 0;
1709 }
cde25790
PG
1710 }
1711 }
85f07f22
FB
1712}
1713
1714static int open_input_stream(HTTPContext *c, const char *info)
1715{
1716 char buf[128];
1717 char input_filename[1024];
1718 AVFormatContext *s;
2effd274 1719 int buf_size, i;
85f07f22
FB
1720 INT64 stream_pos;
1721
1722 /* find file name */
1723 if (c->stream->feed) {
1724 strcpy(input_filename, c->stream->feed->feed_filename);
1725 buf_size = FFM_PACKET_SIZE;
1726 /* compute position (absolute time) */
1727 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1728 stream_pos = parse_date(buf, 0);
f747e6d3
PG
1729 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1730 int prebuffer = strtol(buf, 0, 10);
2c4ae653 1731 stream_pos = av_gettime() - prebuffer * 1000000;
85f07f22 1732 } else {
2c4ae653 1733 stream_pos = av_gettime() - c->stream->prebuffer * 1000;
85f07f22
FB
1734 }
1735 } else {
1736 strcpy(input_filename, c->stream->feed_filename);
1737 buf_size = 0;
1738 /* compute position (relative time) */
1739 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1740 stream_pos = parse_date(buf, 1);
1741 } else {
1742 stream_pos = 0;
1743 }
1744 }
1745 if (input_filename[0] == '\0')
1746 return -1;
1747
8256c0a3
PG
1748#if 0
1749 { time_t when = stream_pos / 1000000;
1750 http_log("Stream pos = %lld, time=%s", stream_pos, ctime(&when));
1751 }
1752#endif
1753
85f07f22 1754 /* open stream */
2effd274
FB
1755 if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0) {
1756 http_log("%s not found", input_filename);
85f07f22 1757 return -1;
2effd274 1758 }
85f07f22 1759 c->fmt_in = s;
2effd274
FB
1760
1761 /* open each parser */
1762 for(i=0;i<s->nb_streams;i++)
1763 open_parser(s, i);
1764
1765 /* choose stream as clock source (we favorize video stream if
1766 present) for packet sending */
1767 c->pts_stream_index = 0;
1768 for(i=0;i<c->stream->nb_streams;i++) {
1769 if (c->pts_stream_index == 0 &&
1770 c->stream->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO) {
1771 c->pts_stream_index = i;
1772 }
1773 }
85f07f22 1774
bd7cf6ad
FB
1775 if (c->fmt_in->iformat->read_seek) {
1776 c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
85f07f22 1777 }
2effd274
FB
1778 /* set the start time (needed for maxtime and RTP packet timing) */
1779 c->start_time = cur_time;
1780 c->first_pts = AV_NOPTS_VALUE;
85f07f22
FB
1781 return 0;
1782}
1783
2effd274
FB
1784/* currently desactivated because the new PTS handling is not
1785 satisfactory yet */
1786//#define AV_READ_FRAME
1787#ifdef AV_READ_FRAME
85f07f22 1788
2effd274
FB
1789/* XXX: generalize that in ffmpeg for picture/audio/data. Currently
1790 the return packet MUST NOT be freed */
1791int av_read_frame(AVFormatContext *s, AVPacket *pkt)
1792{
1793 AVStream *st;
1794 int len, ret, old_nb_streams, i;
f747e6d3 1795
2effd274
FB
1796 /* see if remaining frames must be parsed */
1797 for(;;) {
1798 if (s->cur_len > 0) {
1799 st = s->streams[s->cur_pkt.stream_index];
1800 len = avcodec_parse_frame(&st->codec, &pkt->data, &pkt->size,
1801 s->cur_ptr, s->cur_len);
1802 if (len < 0) {
1803 /* error: get next packet */
1804 s->cur_len = 0;
1805 } else {
1806 s->cur_ptr += len;
1807 s->cur_len -= len;
1808 if (pkt->size) {
1809 /* init pts counter if not done */
1810 if (st->pts.den == 0) {
1811 switch(st->codec.codec_type) {
1812 case CODEC_TYPE_AUDIO:
1813 st->pts_incr = (INT64)s->pts_den;
1814 av_frac_init(&st->pts, st->pts.val, 0,
1815 (INT64)s->pts_num * st->codec.sample_rate);
1816 break;
1817 case CODEC_TYPE_VIDEO:
1818 st->pts_incr = (INT64)s->pts_den * FRAME_RATE_BASE;
1819 av_frac_init(&st->pts, st->pts.val, 0,
1820 (INT64)s->pts_num * st->codec.frame_rate);
1821 break;
1822 default:
1823 av_abort();
1824 }
1825 }
1826
1827 /* a frame was read: return it */
1828 pkt->pts = st->pts.val;
1829#if 0
1830 printf("add pts=%Lx num=%Lx den=%Lx incr=%Lx\n",
1831 st->pts.val, st->pts.num, st->pts.den, st->pts_incr);
1832#endif
1833 switch(st->codec.codec_type) {
1834 case CODEC_TYPE_AUDIO:
1835 av_frac_add(&st->pts, st->pts_incr * st->codec.frame_size);
1836 break;
1837 case CODEC_TYPE_VIDEO:
1838 av_frac_add(&st->pts, st->pts_incr);
1839 break;
1840 default:
1841 av_abort();
1842 }
1843 pkt->stream_index = s->cur_pkt.stream_index;
1844 /* we use the codec indication because it is
1845 more accurate than the demux flags */
1846 pkt->flags = 0;
1847 if (st->codec.key_frame)
1848 pkt->flags |= PKT_FLAG_KEY;
1849 return 0;
1850 }
85f07f22
FB
1851 }
1852 } else {
2effd274
FB
1853 /* free previous packet */
1854 av_free_packet(&s->cur_pkt);
1855
1856 old_nb_streams = s->nb_streams;
1857 ret = av_read_packet(s, &s->cur_pkt);
1858 if (ret)
1859 return ret;
1860 /* open parsers for each new streams */
1861 for(i = old_nb_streams; i < s->nb_streams; i++)
1862 open_parser(s, i);
1863 st = s->streams[s->cur_pkt.stream_index];
1864
1865 /* update current pts (XXX: dts handling) from packet, or
1866 use current pts if none given */
1867 if (s->cur_pkt.pts != AV_NOPTS_VALUE) {
1868 av_frac_set(&st->pts, s->cur_pkt.pts);
1869 } else {
1870 s->cur_pkt.pts = st->pts.val;
1871 }
1872 if (!st->codec.codec) {
1873 /* no codec opened: just return the raw packet */
1874 *pkt = s->cur_pkt;
1875
1876 /* no codec opened: just update the pts by considering we
1877 have one frame and free the packet */
1878 if (st->pts.den == 0) {
1879 switch(st->codec.codec_type) {
1880 case CODEC_TYPE_AUDIO:
1881 st->pts_incr = (INT64)s->pts_den * st->codec.frame_size;
1882 av_frac_init(&st->pts, st->pts.val, 0,
1883 (INT64)s->pts_num * st->codec.sample_rate);
1884 break;
1885 case CODEC_TYPE_VIDEO:
1886 st->pts_incr = (INT64)s->pts_den * FRAME_RATE_BASE;
1887 av_frac_init(&st->pts, st->pts.val, 0,
1888 (INT64)s->pts_num * st->codec.frame_rate);
1889 break;
1890 default:
1891 av_abort();
1892 }
1893 }
1894 av_frac_add(&st->pts, st->pts_incr);
1895 return 0;
1896 } else {
1897 s->cur_ptr = s->cur_pkt.data;
1898 s->cur_len = s->cur_pkt.size;
85f07f22
FB
1899 }
1900 }
2effd274
FB
1901 }
1902}
1903
1904static int compute_send_delay(HTTPContext *c)
1905{
1906 INT64 cur_pts, delta_pts, next_pts;
1907 int delay1;
1908
1909 /* compute current pts value from system time */
1910 cur_pts = ((INT64)(cur_time - c->start_time) * c->fmt_in->pts_den) /
1911 (c->fmt_in->pts_num * 1000LL);
1912 /* compute the delta from the stream we choose as
1913 main clock (we do that to avoid using explicit
1914 buffers to do exact packet reordering for each
1915 stream */
1916 /* XXX: really need to fix the number of streams */
1917 if (c->pts_stream_index >= c->fmt_in->nb_streams)
1918 next_pts = cur_pts;
1919 else
1920 next_pts = c->fmt_in->streams[c->pts_stream_index]->pts.val;
1921 delta_pts = next_pts - cur_pts;
1922 if (delta_pts <= 0) {
1923 delay1 = 0;
1924 } else {
1925 delay1 = (delta_pts * 1000 * c->fmt_in->pts_num) / c->fmt_in->pts_den;
1926 }
1927 return delay1;
1928}
1929#else
1930
1931/* just fall backs */
1932int av_read_frame(AVFormatContext *s, AVPacket *pkt)
1933{
1934 return av_read_packet(s, pkt);
1935}
1936
1937static int compute_send_delay(HTTPContext *c)
1938{
1939 return 0;
1940}
1941
1942#endif
1943
1944static int http_prepare_data(HTTPContext *c)
1945{
1946 int i, len, ret;
1947 AVFormatContext *ctx;
1948
1949 switch(c->state) {
1950 case HTTPSTATE_SEND_DATA_HEADER:
1951 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
1952 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
1953 c->stream->author);
1954 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
1955 c->stream->comment);
1956 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
1957 c->stream->copyright);
1958 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
1959 c->stream->title);
1960
1961 /* open output stream by using specified codecs */
1962 c->fmt_ctx.oformat = c->stream->fmt;
1963 c->fmt_ctx.nb_streams = c->stream->nb_streams;
1964 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
1965 AVStream *st;
1966 st = av_mallocz(sizeof(AVStream));
1967 c->fmt_ctx.streams[i] = st;
1968 /* if file or feed, then just take streams from FFStream struct */
1969 if (!c->stream->feed ||
1970 c->stream->feed == c->stream)
1971 memcpy(st, c->stream->streams[i], sizeof(AVStream));
1972 else
1973 memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]],
1974 sizeof(AVStream));
1975 st->codec.frame_number = 0; /* XXX: should be done in
1976 AVStream, not in codec */
1977 }
1978 c->got_key_frame = 0;
1979
1980 /* prepare header and save header data in a stream */
1981 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
1982 /* XXX: potential leak */
1983 return -1;
1984 }
1985 c->fmt_ctx.pb.is_streamed = 1;
1986
1987 av_write_header(&c->fmt_ctx);
1988
1989 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
1990 c->buffer_ptr = c->pb_buffer;
1991 c->buffer_end = c->pb_buffer + len;
1992
1993 c->state = HTTPSTATE_SEND_DATA;
85f07f22
FB
1994 c->last_packet_sent = 0;
1995 break;
1996 case HTTPSTATE_SEND_DATA:
1997 /* find a new packet */
85f07f22
FB
1998 {
1999 AVPacket pkt;
2effd274 2000
85f07f22
FB
2001 /* read a packet from the input stream */
2002 if (c->stream->feed) {
2003 ffm_set_write_index(c->fmt_in,
2004 c->stream->feed->feed_write_index,
2005 c->stream->feed->feed_size);
2006 }
ec3b2232
PG
2007
2008 if (c->stream->max_time &&
2ac887ba 2009 c->stream->max_time + c->start_time - cur_time < 0) {
ec3b2232
PG
2010 /* We have timed out */
2011 c->state = HTTPSTATE_SEND_DATA_TRAILER;
85f07f22 2012 } else {
2effd274
FB
2013 if (c->is_packetized) {
2014 if (compute_send_delay(c) > 0) {
2015 c->state = HTTPSTATE_WAIT;
2016 return 1; /* state changed */
2017 }
2018 }
2019 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2020 if (c->stream->feed && c->stream->feed->feed_opened) {
2021 /* if coming from feed, it means we reached the end of the
2022 ffm file, so must wait for more data */
2023 c->state = HTTPSTATE_WAIT_FEED;
2024 return 1; /* state changed */
2025 } else {
2026 /* must send trailer now because eof or error */
2027 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2028 }
2029 } else {
2030 /* update first pts if needed */
2031 if (c->first_pts == AV_NOPTS_VALUE)
2032 c->first_pts = pkt.pts;
2033
2034 /* send it to the appropriate stream */
2035 if (c->stream->feed) {
2036 /* if coming from a feed, select the right stream */
2037 if (c->switch_pending) {
2038 c->switch_pending = 0;
2039 for(i=0;i<c->stream->nb_streams;i++) {
2040 if (c->switch_feed_streams[i] == pkt.stream_index) {
2041 if (pkt.flags & PKT_FLAG_KEY) {
2042 do_switch_stream(c, i);
2043 }
2044 }
2045 if (c->switch_feed_streams[i] >= 0) {
2046 c->switch_pending = 1;
2047 }
2048 }
2049 }
cde25790 2050 for(i=0;i<c->stream->nb_streams;i++) {
2effd274
FB
2051 if (c->feed_streams[i] == pkt.stream_index) {
2052 pkt.stream_index = i;
cde25790 2053 if (pkt.flags & PKT_FLAG_KEY) {
2effd274
FB
2054 c->got_key_frame |= 1 << i;
2055 }
2056 /* See if we have all the key frames, then
2057 * we start to send. This logic is not quite
2058 * right, but it works for the case of a
2059 * single video stream with one or more
2060 * audio streams (for which every frame is
2061 * typically a key frame).
2062 */
2063 if (!c->stream->send_on_key ||
2064 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2065 goto send_it;
cde25790 2066 }
cde25790
PG
2067 }
2068 }
2effd274
FB
2069 } else {
2070 AVCodecContext *codec;
2071
2072 send_it:
2073 /* specific handling for RTP: we use several
2074 output stream (one for each RTP
2075 connection). XXX: need more abstract handling */
2076 if (c->is_packetized) {
2077 c->packet_stream_index = pkt.stream_index;
2078 ctx = c->rtp_ctx[c->packet_stream_index];
2079 codec = &ctx->streams[0]->codec;
2080 } else {
2081 ctx = &c->fmt_ctx;
2082 /* Fudge here */
2083 codec = &ctx->streams[pkt.stream_index]->codec;
85f07f22 2084 }
2effd274
FB
2085
2086 codec->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2087
f747e6d3 2088#ifdef PJSG
2effd274
FB
2089 if (codec->codec_type == CODEC_TYPE_AUDIO) {
2090 codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
2091 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
2092 }
2093#endif
2094
2095 if (c->is_packetized) {
2096 ret = url_open_dyn_packet_buf(&ctx->pb,
2097 url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]));
2098 c->packet_byte_count = 0;
2099 c->packet_start_time_us = av_gettime();
2100 } else {
2101 ret = url_open_dyn_buf(&ctx->pb);
2102 }
2103 if (ret < 0) {
2104 /* XXX: potential leak */
2105 return -1;
2106 }
2107 if (av_write_packet(ctx, &pkt, pkt.pts)) {
2108 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2109 }
2110
2111 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2112 c->buffer_ptr = c->pb_buffer;
2113 c->buffer_end = c->pb_buffer + len;
2114
2115 codec->frame_number++;
f747e6d3 2116 }
2effd274
FB
2117#ifndef AV_READ_FRAME
2118 av_free_packet(&pkt);
f747e6d3 2119#endif
85f07f22 2120 }
85f07f22
FB
2121 }
2122 }
2123 break;
2124 default:
2125 case HTTPSTATE_SEND_DATA_TRAILER:
2126 /* last packet test ? */
2effd274 2127 if (c->last_packet_sent || c->is_packetized)
85f07f22 2128 return -1;
2effd274 2129 ctx = &c->fmt_ctx;
85f07f22 2130 /* prepare header */
2effd274
FB
2131 if (url_open_dyn_buf(&ctx->pb) < 0) {
2132 /* XXX: potential leak */
2133 return -1;
2134 }
2135 av_write_trailer(ctx);
2136 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2137 c->buffer_ptr = c->pb_buffer;
2138 c->buffer_end = c->pb_buffer + len;
2139
85f07f22
FB
2140 c->last_packet_sent = 1;
2141 break;
2142 }
2143 return 0;
2144}
2145
2effd274
FB
2146/* in bit/s */
2147#define SHORT_TERM_BANDWIDTH 8000000
2148
85f07f22 2149/* should convert the format at the same time */
5eb765ef 2150static int http_send_data(HTTPContext *c)
85f07f22 2151{
2effd274
FB
2152 int len, ret, dt;
2153
85f07f22 2154 while (c->buffer_ptr >= c->buffer_end) {
2effd274 2155 av_freep(&c->pb_buffer);
5eb765ef 2156 ret = http_prepare_data(c);
85f07f22
FB
2157 if (ret < 0)
2158 return -1;
2159 else if (ret == 0) {
a6e14edd 2160 continue;
85f07f22
FB
2161 } else {
2162 /* state change requested */
2163 return 0;
2164 }
2165 }
2166
2effd274
FB
2167 if (c->buffer_ptr < c->buffer_end) {
2168 if (c->is_packetized) {
2169 /* RTP/UDP data output */
2170 len = c->buffer_end - c->buffer_ptr;
2171 if (len < 4) {
2172 /* fail safe - should never happen */
2173 fail1:
2174 c->buffer_ptr = c->buffer_end;
2175 return 0;
2176 }
2177 len = (c->buffer_ptr[0] << 24) |
2178 (c->buffer_ptr[1] << 16) |
2179 (c->buffer_ptr[2] << 8) |
2180 (c->buffer_ptr[3]);
2181 if (len > (c->buffer_end - c->buffer_ptr))
2182 goto fail1;
2183
2184 /* short term bandwidth limitation */
2185 dt = av_gettime() - c->packet_start_time_us;
2186 if (dt < 1)
2187 dt = 1;
2188
2189 if ((c->packet_byte_count + len) * (INT64)1000000 >=
2190 (SHORT_TERM_BANDWIDTH / 8) * (INT64)dt) {
2191 /* bandwidth overflow : wait at most one tick and retry */
2192 c->state = HTTPSTATE_WAIT_SHORT;
2193 return 0;
f747e6d3 2194 }
2effd274
FB
2195
2196 c->buffer_ptr += 4;
2197 url_write(c->rtp_handles[c->packet_stream_index],
2198 c->buffer_ptr, len);
f747e6d3 2199 c->buffer_ptr += len;
2effd274
FB
2200 c->packet_byte_count += len;
2201 } else {
2202 /* TCP data output */
2203 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2204 if (len < 0) {
2205 if (errno != EAGAIN && errno != EINTR) {
2206 /* error : close connection */
2207 return -1;
2208 } else {
2209 return 0;
2210 }
2211 } else {
2212 c->buffer_ptr += len;
2213 }
85f07f22 2214 }
2effd274
FB
2215 c->data_count += len;
2216 update_datarate(&c->datarate, c->data_count);
2217 if (c->stream)
2218 c->stream->bytes_served += len;
85f07f22
FB
2219 }
2220 return 0;
2221}
2222
2223static int http_start_receive_data(HTTPContext *c)
2224{
2225 int fd;
2226
2227 if (c->stream->feed_opened)
2228 return -1;
2229
2230 /* open feed */
2231 fd = open(c->stream->feed_filename, O_RDWR);
2232 if (fd < 0)
2233 return -1;
2234 c->feed_fd = fd;
2235
2236 c->stream->feed_write_index = ffm_read_write_index(fd);
2237 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2238 lseek(fd, 0, SEEK_SET);
2239
2240 /* init buffer input */
2241 c->buffer_ptr = c->buffer;
2242 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2243 c->stream->feed_opened = 1;
2244 return 0;
2245}
2246
2247static int http_receive_data(HTTPContext *c)
2248{
85f07f22
FB
2249 HTTPContext *c1;
2250
a6e14edd
PG
2251 if (c->buffer_end > c->buffer_ptr) {
2252 int len;
2253
2254 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2255 if (len < 0) {
2256 if (errno != EAGAIN && errno != EINTR) {
2257 /* error : close connection */
2258 goto fail;
2259 }
2260 } else if (len == 0) {
2261 /* end of connection : close it */
2262 goto fail;
2263 } else {
2264 c->buffer_ptr += len;
2265 c->data_count += len;
5eb765ef 2266 update_datarate(&c->datarate, c->data_count);
a6e14edd
PG
2267 }
2268 }
2269
85f07f22 2270 if (c->buffer_ptr >= c->buffer_end) {
f747e6d3 2271 FFStream *feed = c->stream;
85f07f22
FB
2272 /* a packet has been received : write it in the store, except
2273 if header */
2274 if (c->data_count > FFM_PACKET_SIZE) {
85f07f22
FB
2275
2276 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2277 /* XXX: use llseek or url_seek */
2278 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2279 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2280
2281 feed->feed_write_index += FFM_PACKET_SIZE;
2282 /* update file size */
2283 if (feed->feed_write_index > c->stream->feed_size)
2284 feed->feed_size = feed->feed_write_index;
2285
2286 /* handle wrap around if max file size reached */
2287 if (feed->feed_write_index >= c->stream->feed_max_size)
2288 feed->feed_write_index = FFM_PACKET_SIZE;
2289
2290 /* write index */
2291 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2292
2293 /* wake up any waiting connections */
2294 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2295 if (c1->state == HTTPSTATE_WAIT_FEED &&
2296 c1->stream->feed == c->stream->feed) {
2297 c1->state = HTTPSTATE_SEND_DATA;
2298 }
2299 }
f747e6d3
PG
2300 } else {
2301 /* We have a header in our hands that contains useful data */
2302 AVFormatContext s;
bd7cf6ad 2303 AVInputFormat *fmt_in;
f747e6d3
PG
2304 ByteIOContext *pb = &s.pb;
2305 int i;
2306
2307 memset(&s, 0, sizeof(s));
2308
2309 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2310 pb->buf_end = c->buffer_end; /* ?? */
2311 pb->is_streamed = 1;
2312
bd7cf6ad
FB
2313 /* use feed output format name to find corresponding input format */
2314 fmt_in = av_find_input_format(feed->fmt->name);
2315 if (!fmt_in)
2316 goto fail;
2317
ec3b2232
PG
2318 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2319 if (!s.priv_data)
2320 goto fail;
2321
bd7cf6ad 2322 if (fmt_in->read_header(&s, 0) < 0) {
ec3b2232 2323 av_freep(&s.priv_data);
f747e6d3
PG
2324 goto fail;
2325 }
2326
2327 /* Now we have the actual streams */
2328 if (s.nb_streams != feed->nb_streams) {
ec3b2232 2329 av_freep(&s.priv_data);
f747e6d3
PG
2330 goto fail;
2331 }
2332 for (i = 0; i < s.nb_streams; i++) {
bd7cf6ad
FB
2333 memcpy(&feed->streams[i]->codec,
2334 &s.streams[i]->codec, sizeof(AVCodecContext));
f747e6d3 2335 }
ec3b2232 2336 av_freep(&s.priv_data);
85f07f22
FB
2337 }
2338 c->buffer_ptr = c->buffer;
2339 }
2340
85f07f22
FB
2341 return 0;
2342 fail:
2343 c->stream->feed_opened = 0;
2344 close(c->feed_fd);
2345 return -1;
2346}
2347
2effd274
FB
2348/********************************************************************/
2349/* RTSP handling */
2350
2351static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2352{
2353 const char *str;
2354 time_t ti;
2355 char *p;
2356 char buf2[32];
2357
2358 switch(error_number) {
2359#define DEF(n, c, s) case c: str = s; break;
2360#include "rtspcodes.h"
2361#undef DEF
2362 default:
2363 str = "Unknown Error";
2364 break;
2365 }
2366
2367 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2368 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2369
2370 /* output GMT time */
2371 ti = time(NULL);
2372 p = ctime(&ti);
2373 strcpy(buf2, p);
2374 p = buf2 + strlen(p) - 1;
2375 if (*p == '\n')
2376 *p = '\0';
2377 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2378}
2379
2380static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2381{
2382 rtsp_reply_header(c, error_number);
2383 url_fprintf(c->pb, "\r\n");
2384}
2385
2386static int rtsp_parse_request(HTTPContext *c)
2387{
2388 const char *p, *p1, *p2;
2389 char cmd[32];
2390 char url[1024];
2391 char protocol[32];
2392 char line[1024];
2393 ByteIOContext pb1;
2394 int len;
2395 RTSPHeader header1, *header = &header1;
2396
2397 c->buffer_ptr[0] = '\0';
2398 p = c->buffer;
2399
2400 get_word(cmd, sizeof(cmd), &p);
2401 get_word(url, sizeof(url), &p);
2402 get_word(protocol, sizeof(protocol), &p);
2403
2404 pstrcpy(c->method, sizeof(c->method), cmd);
2405 pstrcpy(c->url, sizeof(c->url), url);
2406 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2407
2408 c->pb = &pb1;
2409 if (url_open_dyn_buf(c->pb) < 0) {
2410 /* XXX: cannot do more */
2411 c->pb = NULL; /* safety */
2412 return -1;
2413 }
2414
2415 /* check version name */
2416 if (strcmp(protocol, "RTSP/1.0") != 0) {
2417 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2418 goto the_end;
2419 }
2420
2421 /* parse each header line */
2422 memset(header, 0, sizeof(RTSPHeader));
2423 /* skip to next line */
2424 while (*p != '\n' && *p != '\0')
2425 p++;
2426 if (*p == '\n')
2427 p++;
2428 while (*p != '\0') {
2429 p1 = strchr(p, '\n');
2430 if (!p1)
2431 break;
2432 p2 = p1;
2433 if (p2 > p && p2[-1] == '\r')
2434 p2--;
2435 /* skip empty line */
2436 if (p2 == p)
2437 break;
2438 len = p2 - p;
2439 if (len > sizeof(line) - 1)
2440 len = sizeof(line) - 1;
2441 memcpy(line, p, len);
2442 line[len] = '\0';
2443 rtsp_parse_line(header, line);
2444 p = p1 + 1;
2445 }
2446
2447 /* handle sequence number */
2448 c->seq = header->seq;
2449
2450 if (!strcmp(cmd, "DESCRIBE")) {
2451 rtsp_cmd_describe(c, url);
2452 } else if (!strcmp(cmd, "SETUP")) {
2453 rtsp_cmd_setup(c, url, header);
2454 } else if (!strcmp(cmd, "PLAY")) {
2455 rtsp_cmd_play(c, url, header);
2456 } else if (!strcmp(cmd, "PAUSE")) {
2457 rtsp_cmd_pause(c, url, header);
2458 } else if (!strcmp(cmd, "TEARDOWN")) {
2459 rtsp_cmd_teardown(c, url, header);
2460 } else {
2461 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2462 }
2463 the_end:
2464 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2465 c->pb = NULL; /* safety */
2466 if (len < 0) {
2467 /* XXX: cannot do more */
2468 return -1;
2469 }
2470 c->buffer_ptr = c->pb_buffer;
2471 c->buffer_end = c->pb_buffer + len;
2472 c->state = RTSPSTATE_SEND_REPLY;
2473 return 0;
2474}
2475
2476static int prepare_sdp_description(HTTPContext *c,
2477 FFStream *stream, UINT8 **pbuffer)
2478{
2479 ByteIOContext pb1, *pb = &pb1;
2480 struct sockaddr_in my_addr;
2481 int len, i, payload_type;
2482 const char *ipstr, *title, *mediatype;
2483 AVStream *st;
2484
2485 len = sizeof(my_addr);
2486 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2487 ipstr = inet_ntoa(my_addr.sin_addr);
2488
2489 if (url_open_dyn_buf(pb) < 0)
2490 return -1;
2491
2492 /* general media info */
2493
2494 url_fprintf(pb, "v=0\n");
2495 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2496 title = stream->title;
2497 if (title[0] == '\0')
2498 title = "No Title";
2499 url_fprintf(pb, "s=%s\n", title);
2500 if (stream->comment[0] != '\0')
2501 url_fprintf(pb, "i=%s\n", stream->comment);
2502
2503 /* for each stream, we output the necessary info */
2504 for(i = 0; i < stream->nb_streams; i++) {
2505 st = stream->streams[i];
2506 switch(st->codec.codec_type) {
2507 case CODEC_TYPE_AUDIO:
2508 mediatype = "audio";
2509 break;
2510 case CODEC_TYPE_VIDEO:
2511 mediatype = "video";
2512 break;
2513 default:
2514 mediatype = "application";
2515 break;
2516 }
2517 /* XXX: the port indication is not correct (but should be correct
2518 for broadcast) */
2519 payload_type = rtp_get_payload_type(&st->codec);
2520
2521 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2522 mediatype, 0, payload_type);
2523 url_fprintf(pb, "a=control:streamid=%d\n", i);
2524 }
2525 return url_close_dyn_buf(pb, pbuffer);
2526}
2527
2528static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2529{
2530 FFStream *stream;
2531 char path1[1024];
2532 const char *path;
2533 UINT8 *content;
2534 int content_length;
2535
2536 /* find which url is asked */
2537 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2538 path = path1;
2539 if (*path == '/')
2540 path++;
2541
2542 for(stream = first_stream; stream != NULL; stream = stream->next) {
2543 if (!stream->is_feed && stream->fmt == &rtp_mux &&
2544 !strcmp(path, stream->filename)) {
2545 goto found;
2546 }
2547 }
2548 /* no stream found */
2549 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2550 return;
2551
2552 found:
2553 /* prepare the media description in sdp format */
2554 content_length = prepare_sdp_description(c, stream, &content);
2555 if (content_length < 0) {
2556 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2557 return;
2558 }
2559 rtsp_reply_header(c, RTSP_STATUS_OK);
2560 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2561 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2562 url_fprintf(c->pb, "\r\n");
2563 put_buffer(c->pb, content, content_length);
2564}
2565
2566static HTTPContext *find_rtp_session(const char *session_id)
2567{
2568 HTTPContext *c;
2569
2570 if (session_id[0] == '\0')
2571 return NULL;
2572
2573 for(c = first_http_ctx; c != NULL; c = c->next) {
2574 if (!strcmp(c->session_id, session_id))
2575 return c;
2576 }
2577 return NULL;
2578}
2579
2580RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2581{
2582 RTSPTransportField *th;
2583 int i;
2584
2585 for(i=0;i<h->nb_transports;i++) {
2586 th = &h->transports[i];
2587 if (th->protocol == protocol)
2588 return th;
2589 }
2590 return NULL;
2591}
2592
2593static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2594 RTSPHeader *h)
2595{
2596 FFStream *stream;
2597 int stream_index, port;
2598 char buf[1024];
2599 char path1[1024];
2600 const char *path;
2601 HTTPContext *rtp_c;
2602 RTSPTransportField *th;
2603 struct sockaddr_in dest_addr;
2604 RTSPActionServerSetup setup;
2605
2606 /* find which url is asked */
2607 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2608 path = path1;
2609 if (*path == '/')
2610 path++;
2611
2612 /* now check each stream */
2613 for(stream = first_stream; stream != NULL; stream = stream->next) {
2614 if (!stream->is_feed && stream->fmt == &rtp_mux) {
2615 /* accept aggregate filenames only if single stream */
2616 if (!strcmp(path, stream->filename)) {
2617 if (stream->nb_streams != 1) {
2618 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2619 return;
2620 }
2621 stream_index = 0;
2622 goto found;
2623 }
2624
2625 for(stream_index = 0; stream_index < stream->nb_streams;
2626 stream_index++) {
2627 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2628 stream->filename, stream_index);
2629 if (!strcmp(path, buf))
2630 goto found;
2631 }
2632 }
2633 }
2634 /* no stream found */
2635 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2636 return;
2637 found:
2638
2639 /* generate session id if needed */
2640 if (h->session_id[0] == '\0') {
2641 snprintf(h->session_id, sizeof(h->session_id),
2642 "%08x%08x", (int)random(), (int)random());
2643 }
2644
2645 /* find rtp session, and create it if none found */
2646 rtp_c = find_rtp_session(h->session_id);
2647 if (!rtp_c) {
2648 rtp_c = rtp_new_connection(c, stream, h->session_id);
2649 if (!rtp_c) {
2650 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2651 return;
2652 }
2653
2654 /* open input stream */
2655 if (open_input_stream(rtp_c, "") < 0) {
2656 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2657 return;
2658 }
2659
2660 /* always prefer UDP */
2661 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2662 if (!th) {
2663 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2664 if (!th) {
2665 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2666 return;
2667 }
2668 }
2669 rtp_c->rtp_protocol = th->protocol;
2670 }
2671
2672 /* test if stream is OK (test needed because several SETUP needs
2673 to be done for a given file) */
2674 if (rtp_c->stream != stream) {
2675 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2676 return;
2677 }
2678
2679 /* test if stream is already set up */
2680 if (rtp_c->rtp_ctx[stream_index]) {
2681 rtsp_reply_error(c, RTSP_STATUS_STATE);
2682 return;
2683 }
2684
2685 /* check transport */
2686 th = find_transport(h, rtp_c->rtp_protocol);
2687 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2688 th->client_port_min <= 0)) {
2689 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2690 return;
2691 }
2692
2693 /* setup default options */
2694 setup.transport_option[0] = '\0';
2695 dest_addr = rtp_c->from_addr;
2696 dest_addr.sin_port = htons(th->client_port_min);
2697
2698 /* add transport option if needed */
2699 if (ff_rtsp_callback) {
2700 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2701 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2702 (char *)&setup, sizeof(setup),
2703 stream->rtsp_option) < 0) {
2704 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2705 return;
2706 }
2707 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2708 }
2709
2710 /* setup stream */
2711 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
2712 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2713 return;
2714 }
2715
2716 /* now everything is OK, so we can send the connection parameters */
2717 rtsp_reply_header(c, RTSP_STATUS_OK);
2718 /* session ID */
2719 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2720
2721 switch(rtp_c->rtp_protocol) {
2722 case RTSP_PROTOCOL_RTP_UDP:
2723 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2724 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2725 "client_port=%d-%d;server_port=%d-%d",
2726 th->client_port_min, th->client_port_min + 1,
2727 port, port + 1);
2728 break;
2729 case RTSP_PROTOCOL_RTP_TCP:
2730 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2731 stream_index * 2, stream_index * 2 + 1);
2732 break;
2733 default:
2734 break;
2735 }
2736 if (setup.transport_option[0] != '\0') {
2737 url_fprintf(c->pb, ";%s", setup.transport_option);
2738 }
2739 url_fprintf(c->pb, "\r\n");
2740
2741
2742 url_fprintf(c->pb, "\r\n");
2743}
2744
2745
2746/* find an rtp connection by using the session ID. Check consistency
2747 with filename */
2748static HTTPContext *find_rtp_session_with_url(const char *url,
2749 const char *session_id)
2750{
2751 HTTPContext *rtp_c;
2752 char path1[1024];
2753 const char *path;
2754
2755 rtp_c = find_rtp_session(session_id);
2756 if (!rtp_c)
2757 return NULL;
2758
2759 /* find which url is asked */
2760 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2761 path = path1;
2762 if (*path == '/')
2763 path++;
2764 if (strcmp(path, rtp_c->stream->filename) != 0)
2765 return NULL;
2766 return rtp_c;
2767}
2768
2769static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2770{
2771 HTTPContext *rtp_c;
2772
2773 rtp_c = find_rtp_session_with_url(url, h->session_id);
2774 if (!rtp_c) {
2775 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2776 return;
2777 }
2778
2779 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2780 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2781 rtp_c->state != HTTPSTATE_READY) {
2782 rtsp_reply_error(c, RTSP_STATUS_STATE);
2783 return;
2784 }
2785
2786 rtp_c->state = HTTPSTATE_SEND_DATA;
2787
2788 /* now everything is OK, so we can send the connection parameters */
2789 rtsp_reply_header(c, RTSP_STATUS_OK);
2790 /* session ID */
2791 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2792 url_fprintf(c->pb, "\r\n");
2793}
2794
2795static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
2796{
2797 HTTPContext *rtp_c;
2798
2799 rtp_c = find_rtp_session_with_url(url, h->session_id);
2800 if (!rtp_c) {
2801 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2802 return;
2803 }
2804
2805 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2806 rtp_c->state != HTTPSTATE_WAIT_FEED) {
2807 rtsp_reply_error(c, RTSP_STATUS_STATE);
2808 return;
2809 }
2810
2811 rtp_c->state = HTTPSTATE_READY;
2812
2813 /* now everything is OK, so we can send the connection parameters */
2814 rtsp_reply_header(c, RTSP_STATUS_OK);
2815 /* session ID */
2816 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2817 url_fprintf(c->pb, "\r\n");
2818}
2819
2820static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
2821{
2822 HTTPContext *rtp_c;
2823
2824 rtp_c = find_rtp_session_with_url(url, h->session_id);
2825 if (!rtp_c) {
2826 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2827 return;
2828 }
2829
2830 /* abort the session */
2831 close_connection(rtp_c);
2832
2833 if (ff_rtsp_callback) {
2834 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
2835 NULL, 0,
2836 rtp_c->stream->rtsp_option);
2837 }
2838
2839 /* now everything is OK, so we can send the connection parameters */
2840 rtsp_reply_header(c, RTSP_STATUS_OK);
2841 /* session ID */
2842 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2843 url_fprintf(c->pb, "\r\n");
2844}
2845
2846
2847/********************************************************************/
2848/* RTP handling */
2849
2850static HTTPContext *rtp_new_connection(HTTPContext *rtsp_c,
2851 FFStream *stream, const char *session_id)
2852{
2853 HTTPContext *c = NULL;
2854
2855 /* XXX: should output a warning page when coming
2856 close to the connection limit */
2857 if (nb_connections >= nb_max_connections)
2858 goto fail;
2859
2860 /* add a new connection */
2861 c = av_mallocz(sizeof(HTTPContext));
2862 if (!c)
2863 goto fail;
2864
2865 c->fd = -1;
2866 c->poll_entry = NULL;
2867 c->from_addr = rtsp_c->from_addr;
2868 c->buffer_size = IOBUFFER_INIT_SIZE;
2869 c->buffer = av_malloc(c->buffer_size);
2870 if (!c->buffer)
2871 goto fail;
2872 nb_connections++;
2873 c->stream = stream;
2874 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
2875 c->state = HTTPSTATE_READY;
2876 c->is_packetized = 1;
2877 /* protocol is shown in statistics */
2878 pstrcpy(c->protocol, sizeof(c->protocol), "RTP");
2879
2880 c->next = first_http_ctx;
2881 first_http_ctx = c;
2882 return c;
2883
2884 fail:
2885 if (c) {
2886 av_free(c->buffer);
2887 av_free(c);
2888 }
2889 return NULL;
2890}
2891
2892/* add a new RTP stream in an RTP connection (used in RTSP SETUP
2893 command). if dest_addr is NULL, then TCP tunneling in RTSP is
2894 used. */
2895static int rtp_new_av_stream(HTTPContext *c,
2896 int stream_index, struct sockaddr_in *dest_addr)
2897{
2898 AVFormatContext *ctx;
2899 AVStream *st;
2900 char *ipaddr;
2901 URLContext *h;
2902 UINT8 *dummy_buf;
2903
2904 /* now we can open the relevant output stream */
2905 ctx = av_mallocz(sizeof(AVFormatContext));
2906 if (!ctx)
2907 return -1;
2908 ctx->oformat = &rtp_mux;
2909
2910 st = av_mallocz(sizeof(AVStream));
2911 if (!st)
2912 goto fail;
2913 ctx->nb_streams = 1;
2914 ctx->streams[0] = st;
2915
2916 if (!c->stream->feed ||
2917 c->stream->feed == c->stream) {
2918 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
2919 } else {
2920 memcpy(st,
2921 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
2922 sizeof(AVStream));
2923 }
2924
2925 if (dest_addr) {
2926 /* build destination RTP address */
2927 ipaddr = inet_ntoa(dest_addr->sin_addr);
2928
2929 snprintf(ctx->filename, sizeof(ctx->filename),
2930 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
2931
2932 printf("open %s\n", ctx->filename);
2933
2934 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
2935 goto fail;
2936 c->rtp_handles[stream_index] = h;
2937 } else {
2938 goto fail;
2939 }
2940
2941 /* normally, no packets should be output here, but the packet size may be checked */
2942 if (url_open_dyn_packet_buf(&ctx->pb,
2943 url_get_max_packet_size(h)) < 0) {
2944 /* XXX: close stream */
2945 goto fail;
2946 }
2947 if (av_write_header(ctx) < 0) {
2948 fail:
2949 if (h)
2950 url_close(h);
2951 av_free(ctx);
2952 return -1;
2953 }
2954 url_close_dyn_buf(&ctx->pb, &dummy_buf);
2955 av_free(dummy_buf);
2956
2957 c->rtp_ctx[stream_index] = ctx;
2958 return 0;
2959}
2960
2961/********************************************************************/
2962/* ffserver initialization */
2963
2964AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
2965{
2966 AVStream *fst;
2967
2968 fst = av_mallocz(sizeof(AVStream));
2969 if (!fst)
2970 return NULL;
2971 fst->priv_data = av_mallocz(sizeof(FeedData));
2972 memcpy(&fst->codec, codec, sizeof(AVCodecContext));
2973 stream->streams[stream->nb_streams++] = fst;
2974 return fst;
2975}
2976
85f07f22
FB
2977/* return the stream number in the feed */
2978int add_av_stream(FFStream *feed,
2979 AVStream *st)
2980{
2981 AVStream *fst;
2982 AVCodecContext *av, *av1;
2983 int i;
2984
2985 av = &st->codec;
2986 for(i=0;i<feed->nb_streams;i++) {
2987 st = feed->streams[i];
2988 av1 = &st->codec;
f747e6d3
PG
2989 if (av1->codec_id == av->codec_id &&
2990 av1->codec_type == av->codec_type &&
85f07f22
FB
2991 av1->bit_rate == av->bit_rate) {
2992
2993 switch(av->codec_type) {
2994 case CODEC_TYPE_AUDIO:
2995 if (av1->channels == av->channels &&
2996 av1->sample_rate == av->sample_rate)
2997 goto found;
2998 break;
2999 case CODEC_TYPE_VIDEO:
3000 if (av1->width == av->width &&
3001 av1->height == av->height &&
3002 av1->frame_rate == av->frame_rate &&
3003 av1->gop_size == av->gop_size)
3004 goto found;
3005 break;
f747e6d3 3006 default:
ec3b2232 3007 av_abort();
85f07f22
FB
3008 }
3009 }
3010 }
3011
2effd274 3012 fst = add_av_stream1(feed, av);
85f07f22
FB
3013 if (!fst)
3014 return -1;
85f07f22
FB
3015 return feed->nb_streams - 1;
3016 found:
3017 return i;
3018}
3019
2effd274
FB
3020void remove_stream(FFStream *stream)
3021{
3022 FFStream **ps;
3023 ps = &first_stream;
3024 while (*ps != NULL) {
3025 if (*ps == stream) {
3026 *ps = (*ps)->next;
3027 } else {
3028 ps = &(*ps)->next;
3029 }
3030 }
3031}
3032
3033/* compute the needed AVStream for each file */
3034void build_file_streams(void)
3035{
3036 FFStream *stream, *stream_next;
3037 AVFormatContext *infile;
3038 int i;
3039
3040 /* gather all streams */
3041 for(stream = first_stream; stream != NULL; stream = stream_next) {
3042 stream_next = stream->next;
3043 if (stream->stream_type == STREAM_TYPE_LIVE &&
3044 !stream->feed) {
3045 /* the stream comes from a file */
3046 /* try to open the file */
3047 /* open stream */
3048 if (av_open_input_file(&infile, stream->feed_filename,
3049 NULL, 0, NULL) < 0) {
3050 http_log("%s not found", stream->feed_filename);
3051 /* remove stream (no need to spend more time on it) */
3052 fail:
3053 remove_stream(stream);
3054 } else {
3055 /* find all the AVStreams inside and reference them in
3056 'stream' */
3057 if (av_find_stream_info(infile) < 0) {
3058 http_log("Could not find codec parameters from '%s'",
3059 stream->feed_filename);
3060 av_close_input_file(infile);
3061 goto fail;
3062 }
3063 for(i=0;i<infile->nb_streams;i++) {
3064 add_av_stream1(stream, &infile->streams[i]->codec);
3065 }
3066 av_close_input_file(infile);
3067 }
3068 }
3069 }
3070}
3071
85f07f22
FB
3072/* compute the needed AVStream for each feed */
3073void build_feed_streams(void)
3074{
3075 FFStream *stream, *feed;
3076 int i;
3077
3078 /* gather all streams */
3079 for(stream = first_stream; stream != NULL; stream = stream->next) {
3080 feed = stream->feed;
3081 if (feed) {
3082 if (!stream->is_feed) {
2effd274 3083 /* we handle a stream coming from a feed */
85f07f22
FB
3084 for(i=0;i<stream->nb_streams;i++) {
3085 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3086 }
cde25790
PG
3087 }
3088 }
3089 }
3090
3091 /* gather all streams */
3092 for(stream = first_stream; stream != NULL; stream = stream->next) {
3093 feed = stream->feed;
3094 if (feed) {
3095 if (stream->is_feed) {
85f07f22
FB
3096 for(i=0;i<stream->nb_streams;i++) {
3097 stream->feed_streams[i] = i;
3098 }
3099 }
3100 }
3101 }
3102
3103 /* create feed files if needed */
3104 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3105 int fd;
3106
3107 if (!url_exist(feed->feed_filename)) {
3108 AVFormatContext s1, *s = &s1;
3109
3110 /* only write the header of the ffm file */
3111 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3112 fprintf(stderr, "Could not open output feed file '%s'\n",
3113 feed->feed_filename);
3114 exit(1);
3115 }
bd7cf6ad 3116 s->oformat = feed->fmt;
85f07f22
FB
3117 s->nb_streams = feed->nb_streams;
3118 for(i=0;i<s->nb_streams;i++) {
3119 AVStream *st;
3120 st = feed->streams[i];
3121 s->streams[i] = st;
3122 }
bd7cf6ad
FB
3123 av_write_header(s);
3124 /* XXX: need better api */
3125 av_freep(&s->priv_data);
85f07f22
FB
3126 url_fclose(&s->pb);
3127 }
3128 /* get feed size and write index */
3129 fd = open(feed->feed_filename, O_RDONLY);
3130 if (fd < 0) {
3131 fprintf(stderr, "Could not open output feed file '%s'\n",
3132 feed->feed_filename);
3133 exit(1);
3134 }
3135
3136 feed->feed_write_index = ffm_read_write_index(fd);
3137 feed->feed_size = lseek(fd, 0, SEEK_END);
3138 /* ensure that we do not wrap before the end of file */
3139 if (feed->feed_max_size < feed->feed_size)
3140 feed->feed_max_size = feed->feed_size;
3141
3142 close(fd);
3143 }
3144}
3145
3146static void get_arg(char *buf, int buf_size, const char **pp)
3147{
3148 const char *p;
3149 char *q;
3150 int quote;
3151
3152 p = *pp;
3153 while (isspace(*p)) p++;
3154 q = buf;
3155 quote = 0;
3156 if (*p == '\"' || *p == '\'')
3157 quote = *p++;
3158 for(;;) {
3159 if (quote) {
3160 if (*p == quote)
3161 break;
3162 } else {
3163 if (isspace(*p))
3164 break;
3165 }
3166 if (*p == '\0')
3167 break;
3168 if ((q - buf) < buf_size - 1)
3169 *q++ = *p;
3170 p++;
3171 }
3172 *q = '\0';
3173 if (quote && *p == quote)
3174 p++;
3175 *pp = p;
3176}
3177
3178/* add a codec and set the default parameters */
3179void add_codec(FFStream *stream, AVCodecContext *av)
3180{
3181 AVStream *st;
3182
3183 /* compute default parameters */
3184 switch(av->codec_type) {
3185 case CODEC_TYPE_AUDIO:
3186 if (av->bit_rate == 0)
3187 av->bit_rate = 64000;
3188 if (av->sample_rate == 0)
3189 av->sample_rate = 22050;
3190 if (av->channels == 0)
3191 av->channels = 1;
3192 break;
3193 case CODEC_TYPE_VIDEO:
3194 if (av->bit_rate == 0)
3195 av->bit_rate = 64000;
3196 if (av->frame_rate == 0)
3197 av->frame_rate = 5 * FRAME_RATE_BASE;
3198 if (av->width == 0 || av->height == 0) {
3199 av->width = 160;
3200 av->height = 128;
3201 }
ba9b374f 3202 /* Bitrate tolerance is less for streaming */
42a63c6a
PG
3203 if (av->bit_rate_tolerance == 0)
3204 av->bit_rate_tolerance = av->bit_rate / 4;
3205 if (av->qmin == 0)
3206 av->qmin = 3;
3207 if (av->qmax == 0)
3208 av->qmax = 31;
3209 if (av->max_qdiff == 0)
3210 av->max_qdiff = 3;
ba9b374f
J
3211 av->qcompress = 0.5;
3212 av->qblur = 0.5;
68d7eef9 3213
85f07f22 3214 break;
f747e6d3 3215 default:
ec3b2232 3216 av_abort();
85f07f22
FB
3217 }
3218
3219 st = av_mallocz(sizeof(AVStream));
3220 if (!st)
3221 return;
3222 stream->streams[stream->nb_streams++] = st;
3223 memcpy(&st->codec, av, sizeof(AVCodecContext));
3224}
3225
f747e6d3
PG
3226int opt_audio_codec(const char *arg)
3227{
3228 AVCodec *p;
3229
3230 p = first_avcodec;
3231 while (p) {
3232 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3233 break;
3234 p = p->next;
3235 }
3236 if (p == NULL) {
3237 return CODEC_ID_NONE;
3238 }
3239
3240 return p->id;
3241}
3242
3243int opt_video_codec(const char *arg)
3244{
3245 AVCodec *p;
3246
3247 p = first_avcodec;
3248 while (p) {
3249 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3250 break;
3251 p = p->next;
3252 }
3253 if (p == NULL) {
3254 return CODEC_ID_NONE;
3255 }
3256
3257 return p->id;
3258}
3259
2effd274
FB
3260/* simplistic plugin support */
3261
3262void load_module(const char *filename)
3263{
3264 void *dll;
3265 void (*init_func)(void);
3266 dll = dlopen(filename, RTLD_NOW);
3267 if (!dll) {
3268 fprintf(stderr, "Could not load module '%s' - %s\n",
3269 filename, dlerror());
3270 return;
3271 }
3272
3273 init_func = dlsym(dll, "ffserver_module_init");
3274 if (!init_func) {
3275 fprintf(stderr,
3276 "%s: init function 'ffserver_module_init()' not found\n",
3277 filename);
3278 dlclose(dll);
3279 }
3280
3281 init_func();
3282}
3283
85f07f22
FB
3284int parse_ffconfig(const char *filename)
3285{
3286 FILE *f;
3287 char line[1024];
3288 char cmd[64];
3289 char arg[1024];
3290 const char *p;
3291 int val, errors, line_num;
cde25790 3292 FFStream **last_stream, *stream, *redirect;
85f07f22
FB
3293 FFStream **last_feed, *feed;
3294 AVCodecContext audio_enc, video_enc;
3295 int audio_id, video_id;
3296
3297 f = fopen(filename, "r");
3298 if (!f) {
3299 perror(filename);
3300 return -1;
3301 }
3302
3303 errors = 0;
3304 line_num = 0;
3305 first_stream = NULL;
3306 last_stream = &first_stream;
3307 first_feed = NULL;
3308 last_feed = &first_feed;
3309 stream = NULL;
3310 feed = NULL;
cde25790 3311 redirect = NULL;
85f07f22
FB
3312 audio_id = CODEC_ID_NONE;
3313 video_id = CODEC_ID_NONE;
3314 for(;;) {
3315 if (fgets(line, sizeof(line), f) == NULL)
3316 break;
3317 line_num++;
3318 p = line;
3319 while (isspace(*p))
3320 p++;
3321 if (*p == '\0' || *p == '#')
3322 continue;
3323
3324 get_arg(cmd, sizeof(cmd), &p);
3325
3326 if (!strcasecmp(cmd, "Port")) {
3327 get_arg(arg, sizeof(arg), &p);
2effd274 3328 my_http_addr.sin_port = htons (atoi(arg));
85f07f22
FB
3329 } else if (!strcasecmp(cmd, "BindAddress")) {
3330 get_arg(arg, sizeof(arg), &p);
2effd274
FB
3331 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3332 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3333 filename, line_num, arg);
3334 errors++;
3335 }
3336 } else if (!strcasecmp(cmd, "NoDaemon")) {
3337 ffserver_daemon = 0;
3338 } else if (!strcasecmp(cmd, "RTSPPort")) {
3339 get_arg(arg, sizeof(arg), &p);
3340 my_rtsp_addr.sin_port = htons (atoi(arg));
3341 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3342 get_arg(arg, sizeof(arg), &p);
3343 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
85f07f22
FB
3344 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3345 filename, line_num, arg);
3346 errors++;
3347 }
3348 } else if (!strcasecmp(cmd, "MaxClients")) {
3349 get_arg(arg, sizeof(arg), &p);
3350 val = atoi(arg);
3351 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3352 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3353 filename, line_num, arg);
3354 errors++;
3355 } else {
3356 nb_max_connections = val;
3357 }
42a63c6a
PG
3358 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3359 get_arg(arg, sizeof(arg), &p);
3360 val = atoi(arg);
3361 if (val < 10 || val > 100000) {
3362 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3363 filename, line_num, arg);
3364 errors++;
3365 } else {
3366 nb_max_bandwidth = val;
3367 }
85f07f22
FB
3368 } else if (!strcasecmp(cmd, "CustomLog")) {
3369 get_arg(logfilename, sizeof(logfilename), &p);
3370 } else if (!strcasecmp(cmd, "<Feed")) {
3371 /*********************************************/
3372 /* Feed related options */
3373 char *q;
3374 if (stream || feed) {
3375 fprintf(stderr, "%s:%d: Already in a tag\n",
3376 filename, line_num);
3377 } else {
3378 feed = av_mallocz(sizeof(FFStream));
3379 /* add in stream list */
3380 *last_stream = feed;
3381 last_stream = &feed->next;
3382 /* add in feed list */
3383 *last_feed = feed;
3384 last_feed = &feed->next_feed;
3385
3386 get_arg(feed->filename, sizeof(feed->filename), &p);
3387 q = strrchr(feed->filename, '>');
3388 if (*q)
3389 *q = '\0';
3390 feed->fmt = guess_format("ffm", NULL, NULL);
3391 /* defaut feed file */
3392 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3393 "/tmp/%s.ffm", feed->filename);
3394 feed->feed_max_size = 5 * 1024 * 1024;
3395 feed->is_feed = 1;
3396 feed->feed = feed; /* self feeding :-) */
3397 }
cde25790
PG
3398 } else if (!strcasecmp(cmd, "Launch")) {
3399 if (feed) {
3400 int i;
3401
3402 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3403
3404 feed->child_argv[0] = av_malloc(7);
3405 strcpy(feed->child_argv[0], "ffmpeg");
3406
3407 for (i = 1; i < 62; i++) {
3408 char argbuf[256];
3409
3410 get_arg(argbuf, sizeof(argbuf), &p);
3411 if (!argbuf[0])
3412 break;
3413
3414 feed->child_argv[i] = av_malloc(strlen(argbuf + 1));
3415 strcpy(feed->child_argv[i], argbuf);
3416 }
3417
3418 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3419
3420 snprintf(feed->child_argv[i], 256, "http://127.0.0.1:%d/%s",
2effd274 3421 ntohs(my_http_addr.sin_port), feed->filename);
cde25790 3422 }
85f07f22
FB
3423 } else if (!strcasecmp(cmd, "File")) {
3424 if (feed) {
3425 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3426 } else if (stream) {
3427 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3428 }
3429 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3430 if (feed) {
3431 const char *p1;
3432 double fsize;
3433
3434 get_arg(arg, sizeof(arg), &p);
3435 p1 = arg;
3436 fsize = strtod(p1, (char **)&p1);
3437 switch(toupper(*p1)) {
3438 case 'K':
3439 fsize *= 1024;
3440 break;
3441 case 'M':
3442 fsize *= 1024 * 1024;
3443 break;
3444 case 'G':
3445 fsize *= 1024 * 1024 * 1024;
3446 break;
3447 }
3448 feed->feed_max_size = (INT64)fsize;
3449 }
3450 } else if (!strcasecmp(cmd, "</Feed>")) {
3451 if (!feed) {
3452 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3453 filename, line_num);
3454 errors++;
f747e6d3
PG
3455 } else {
3456 /* Make sure that we start out clean */
42a63c6a
PG
3457 if (unlink(feed->feed_filename) < 0
3458 && errno != ENOENT) {
3459 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3460 filename, line_num, feed->feed_filename, strerror(errno));
3461 errors++;
3462 }
85f07f22
FB
3463 }
3464 feed = NULL;
3465 } else if (!strcasecmp(cmd, "<Stream")) {
3466 /*********************************************/
3467 /* Stream related options */
3468 char *q;
3469 if (stream || feed) {
3470 fprintf(stderr, "%s:%d: Already in a tag\n",
3471 filename, line_num);
3472 } else {
3473 stream = av_mallocz(sizeof(FFStream));
3474 *last_stream = stream;
3475 last_stream = &stream->next;
3476
3477 get_arg(stream->filename, sizeof(stream->filename), &p);
3478 q = strrchr(stream->filename, '>');
3479 if (*q)
3480 *q = '\0';
8256c0a3 3481 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
85f07f22
FB
3482 memset(&audio_enc, 0, sizeof(AVCodecContext));
3483 memset(&video_enc, 0, sizeof(AVCodecContext));
3484 audio_id = CODEC_ID_NONE;
3485 video_id = CODEC_ID_NONE;
3486 if (stream->fmt) {
3487 audio_id = stream->fmt->audio_codec;
3488 video_id = stream->fmt->video_codec;
3489 }
3490 }
3491 } else if (!strcasecmp(cmd, "Feed")) {
3492 get_arg(arg, sizeof(arg), &p);
3493 if (stream) {
3494 FFStream *sfeed;
3495
3496 sfeed = first_feed;
3497 while (sfeed != NULL) {
3498 if (!strcmp(sfeed->filename, arg))
3499 break;
3500 sfeed = sfeed->next_feed;
3501 }
3502 if (!sfeed) {
3503 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3504 filename, line_num, arg);
3505 } else {
3506 stream->feed = sfeed;
3507 }
3508 }
3509 } else if (!strcasecmp(cmd, "Format")) {
3510 get_arg(arg, sizeof(arg), &p);
3511 if (!strcmp(arg, "status")) {
3512 stream->stream_type = STREAM_TYPE_STATUS;
3513 stream->fmt = NULL;
3514 } else {
3515 stream->stream_type = STREAM_TYPE_LIVE;
dd2af5aa
FB
3516 /* jpeg cannot be used here, so use single frame jpeg */
3517 if (!strcmp(arg, "jpeg"))
3518 strcpy(arg, "singlejpeg");
8256c0a3 3519 stream->fmt = guess_stream_format(arg, NULL, NULL);
85f07f22
FB
3520 if (!stream->fmt) {
3521 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3522 filename, line_num, arg);
3523 errors++;
3524 }
3525 }
3526 if (stream->fmt) {
3527 audio_id = stream->fmt->audio_codec;
3528 video_id = stream->fmt->video_codec;
3529 }
cde25790
PG
3530 } else if (!strcasecmp(cmd, "FaviconURL")) {
3531 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3532 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3533 } else {
3534 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3535 filename, line_num);
3536 errors++;
3537 }
2ac887ba
PG
3538 } else if (!strcasecmp(cmd, "Author")) {
3539 if (stream) {
3540 get_arg(stream->author, sizeof(stream->author), &p);
3541 }
3542 } else if (!strcasecmp(cmd, "Comment")) {
3543 if (stream) {
3544 get_arg(stream->comment, sizeof(stream->comment), &p);
3545 }
3546 } else if (!strcasecmp(cmd, "Copyright")) {
3547 if (stream) {
3548 get_arg(stream->copyright, sizeof(stream->copyright), &p);
3549 }
3550 } else if (!strcasecmp(cmd, "Title")) {
3551 if (stream) {
3552 get_arg(stream->title, sizeof(stream->title), &p);
3553 }
42a63c6a
PG
3554 } else if (!strcasecmp(cmd, "Preroll")) {
3555 get_arg(arg, sizeof(arg), &p);
3556 if (stream) {
8256c0a3 3557 stream->prebuffer = atof(arg) * 1000;
42a63c6a 3558 }
79c4ea3c
PG
3559 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
3560 if (stream) {
3561 stream->send_on_key = 1;
3562 }
f747e6d3
PG
3563 } else if (!strcasecmp(cmd, "AudioCodec")) {
3564 get_arg(arg, sizeof(arg), &p);
3565 audio_id = opt_audio_codec(arg);
3566 if (audio_id == CODEC_ID_NONE) {
3567 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
3568 filename, line_num, arg);
3569 errors++;
3570 }
3571 } else if (!strcasecmp(cmd, "VideoCodec")) {
3572 get_arg(arg, sizeof(arg), &p);
3573 video_id = opt_video_codec(arg);
3574 if (video_id == CODEC_ID_NONE) {
3575 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
3576 filename, line_num, arg);
3577 errors++;
3578 }
ec3b2232
PG
3579 } else if (!strcasecmp(cmd, "MaxTime")) {
3580 get_arg(arg, sizeof(arg), &p);
3581 if (stream) {
8256c0a3 3582 stream->max_time = atof(arg) * 1000;
ec3b2232 3583 }
85f07f22
FB
3584 } else if (!strcasecmp(cmd, "AudioBitRate")) {
3585 get_arg(arg, sizeof(arg), &p);
3586 if (stream) {
3587 audio_enc.bit_rate = atoi(arg) * 1000;
3588 }
3589 } else if (!strcasecmp(cmd, "AudioChannels")) {
3590 get_arg(arg, sizeof(arg), &p);
3591 if (stream) {
3592 audio_enc.channels = atoi(arg);
3593 }
3594 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
3595 get_arg(arg, sizeof(arg), &p);
3596 if (stream) {
3597 audio_enc.sample_rate = atoi(arg);
3598 }
3599 } else if (!strcasecmp(cmd, "VideoBitRate")) {
3600 get_arg(arg, sizeof(arg), &p);
3601 if (stream) {
3602 video_enc.bit_rate = atoi(arg) * 1000;
3603 }
3604 } else if (!strcasecmp(cmd, "VideoSize")) {
3605 get_arg(arg, sizeof(arg), &p);
3606 if (stream) {
3607 parse_image_size(&video_enc.width, &video_enc.height, arg);
3608 if ((video_enc.width % 16) != 0 ||
3609 (video_enc.height % 16) != 0) {
3610 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
3611 filename, line_num);
3612 errors++;
3613 }
3614 }
3615 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
3616 get_arg(arg, sizeof(arg), &p);
3617 if (stream) {
3618 video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
3619 }
3620 } else if (!strcasecmp(cmd, "VideoGopSize")) {
3621 get_arg(arg, sizeof(arg), &p);
3622 if (stream) {
3623 video_enc.gop_size = atoi(arg);
3624 }
3625 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
3626 if (stream) {
3627 video_enc.gop_size = 1;
3628 }
e7f9c674
J
3629 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
3630 if (stream) {
3631 video_enc.flags |= CODEC_FLAG_HQ;
3632 }
42a63c6a
PG
3633 } else if (!strcasecmp(cmd, "VideoQDiff")) {
3634 if (stream) {
3635 video_enc.max_qdiff = atoi(arg);
3636 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
3637 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
3638 filename, line_num);
3639 errors++;
3640 }
3641 }
3642 } else if (!strcasecmp(cmd, "VideoQMax")) {
3643 if (stream) {
3644 video_enc.qmax = atoi(arg);
3645 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
3646 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
3647 filename, line_num);
3648 errors++;
3649 }
3650 }
3651 } else if (!strcasecmp(cmd, "VideoQMin")) {
3652 if (stream) {
3653 video_enc.qmin = atoi(arg);
3654 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
3655 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
3656 filename, line_num);
3657 errors++;
3658 }
3659 }
85f07f22
FB
3660 } else if (!strcasecmp(cmd, "NoVideo")) {
3661 video_id = CODEC_ID_NONE;
3662 } else if (!strcasecmp(cmd, "NoAudio")) {
3663 audio_id = CODEC_ID_NONE;
8256c0a3
PG
3664 } else if (!strcasecmp(cmd, "ACL")) {
3665 IPAddressACL acl;
3666 struct hostent *he;
3667
3668 get_arg(arg, sizeof(arg), &p);
3669 if (strcasecmp(arg, "allow") == 0) {
3670 acl.action = IP_ALLOW;
3671 } else if (strcasecmp(arg, "deny") == 0) {
3672 acl.action = IP_DENY;
3673 } else {
3674 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
3675 filename, line_num, arg);
3676 errors++;
3677 }
3678
3679 get_arg(arg, sizeof(arg), &p);
3680
3681 he = gethostbyname(arg);
3682 if (!he) {
3683 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
3684 filename, line_num, arg);
3685 errors++;
3686 } else {
3687 /* Only take the first */
3688 acl.first = *(struct in_addr *) he->h_addr_list[0];
3689 acl.last = acl.first;
3690 }
3691
3692 get_arg(arg, sizeof(arg), &p);
3693
3694 if (arg[0]) {
3695 he = gethostbyname(arg);
3696 if (!he) {
3697 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
3698 filename, line_num, arg);
3699 errors++;
3700 } else {
3701 /* Only take the first */
3702 acl.last = *(struct in_addr *) he->h_addr_list[0];
3703 }
3704 }
3705
3706 if (!errors) {
3707 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
3708 IPAddressACL **naclp = 0;
3709
3710 *nacl = acl;
3711 nacl->next = 0;
3712
3713 if (stream) {
3714 naclp = &stream->acl;
3715 } else if (feed) {
3716 naclp = &feed->acl;
3717 } else {
3718 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
3719 filename, line_num);
3720 errors++;
3721 }
3722
3723 if (naclp) {
3724 while (*naclp)
3725 naclp = &(*naclp)->next;
3726
3727 *naclp = nacl;
3728 }
3729 }
2effd274
FB
3730 } else if (!strcasecmp(cmd, "RTSPOption")) {
3731 get_arg(arg, sizeof(arg), &p);
3732 if (stream) {
3733 av_freep(&stream->rtsp_option);
3734 /* XXX: av_strdup ? */
3735 stream->rtsp_option = av_malloc(strlen(arg) + 1);
3736 if (stream->rtsp_option) {
3737 strcpy(stream->rtsp_option, arg);
3738 }
3739 }
85f07f22
FB
3740 } else if (!strcasecmp(cmd, "</Stream>")) {
3741 if (!stream) {
3742 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
3743 filename, line_num);
3744 errors++;
3745 }
3746 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
3747 if (audio_id != CODEC_ID_NONE) {
3748 audio_enc.codec_type = CODEC_TYPE_AUDIO;
3749 audio_enc.codec_id = audio_id;
3750 add_codec(stream, &audio_enc);
3751 }
3752 if (video_id != CODEC_ID_NONE) {
3753 video_enc.codec_type = CODEC_TYPE_VIDEO;
3754 video_enc.codec_id = video_id;
3755 add_codec(stream, &video_enc);
3756 }
3757 }
3758 stream = NULL;
cde25790
PG
3759 } else if (!strcasecmp(cmd, "<Redirect")) {
3760 /*********************************************/
3761 char *q;
3762 if (stream || feed || redirect) {
3763 fprintf(stderr, "%s:%d: Already in a tag\n",
3764 filename, line_num);
3765 errors++;
3766 } else {
3767 redirect = av_mallocz(sizeof(FFStream));
3768 *last_stream = redirect;
3769 last_stream = &redirect->next;
3770
3771 get_arg(redirect->filename, sizeof(redirect->filename), &p);
3772 q = strrchr(redirect->filename, '>');
3773 if (*q)
3774 *q = '\0';
3775 redirect->stream_type = STREAM_TYPE_REDIRECT;
3776 }
3777 } else if (!strcasecmp(cmd, "URL")) {
3778 if (redirect) {
3779 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
3780 }
3781 } else if (!strcasecmp(cmd, "</Redirect>")) {
3782 if (!redirect) {
3783 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
3784 filename, line_num);
3785 errors++;
3786 }
3787 if (!redirect->feed_filename[0]) {
3788 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
3789 filename, line_num);
3790 errors++;
3791 }
3792 redirect = NULL;
2effd274
FB
3793 } else if (!strcasecmp(cmd, "LoadModule")) {
3794 get_arg(arg, sizeof(arg), &p);
3795 load_module(arg);
85f07f22
FB
3796 } else {
3797 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
3798 filename, line_num, cmd);
3799 errors++;
3800 }
3801 }
3802
3803 fclose(f);
3804 if (errors)
3805 return -1;
3806 else
3807 return 0;
3808}
3809
3810
85f07f22
FB
3811#if 0
3812static void write_packet(FFCodec *ffenc,
3813 UINT8 *buf, int size)
3814{
3815 PacketHeader hdr;
3816 AVCodecContext *enc = &ffenc->enc;
3817 UINT8 *wptr;
3818 mk_header(&hdr, enc, size);
3819 wptr = http_fifo.wptr;
3820 fifo_write(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &wptr);
3821 fifo_write(&http_fifo, buf, size, &wptr);
3822 /* atomic modification of wptr */
3823 http_fifo.wptr = wptr;
3824 ffenc->data_count += size;
3825 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
3826}
3827#endif
3828
3829void help(void)
3830{
773a21b8 3831 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
85f07f22
FB
3832 "usage: ffserver [-L] [-h] [-f configfile]\n"
3833 "Hyper fast multi format Audio/Video streaming server\n"
3834 "\n"
3835 "-L : print the LICENCE\n"
3836 "-h : this help\n"
3837 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
3838 );
3839}
3840
3841void licence(void)
3842{
3843 printf(
3844 "ffserver version " FFMPEG_VERSION "\n"
773a21b8
FB
3845 "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
3846 "This library is free software; you can redistribute it and/or\n"
3847 "modify it under the terms of the GNU Lesser General Public\n"
3848 "License as published by the Free Software Foundation; either\n"
3849 "version 2 of the License, or (at your option) any later version.\n"
85f07f22 3850 "\n"
773a21b8 3851 "This library is distributed in the hope that it will be useful,\n"
85f07f22 3852 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
773a21b8
FB
3853 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
3854 "Lesser General Public License for more details.\n"
85f07f22 3855 "\n"
773a21b8
FB
3856 "You should have received a copy of the GNU Lesser General Public\n"
3857 "License along with this library; if not, write to the Free Software\n"
3858 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
85f07f22
FB
3859 );
3860}
3861
5eb765ef
PG
3862static void handle_child_exit(int sig)
3863{
3864 pid_t pid;
3865 int status;
3866
3867 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
3868 FFStream *feed;
3869
3870 for (feed = first_feed; feed; feed = feed->next) {
3871 if (feed->pid == pid) {
3872 int uptime = time(0) - feed->pid_start;
3873
3874 feed->pid = 0;
3875 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
3876
3877 if (uptime < 30) {
3878 /* Turn off any more restarts */
3879 feed->child_argv = 0;
3880 }
3881 }
3882 }
3883 }
3884
3885 need_to_start_children = 1;
3886}
3887
85f07f22
FB
3888int main(int argc, char **argv)
3889{
3890 const char *config_filename;
3891 int c;
5eb765ef 3892 struct sigaction sigact;
85f07f22 3893
2c4ae653 3894 av_register_all();
85f07f22
FB
3895
3896 config_filename = "/etc/ffserver.conf";
3897
cde25790 3898 my_program_name = argv[0];
2effd274
FB
3899 ffserver_daemon = 1;
3900
85f07f22 3901 for(;;) {
2effd274 3902 c = getopt(argc, argv, "ndLh?f:");
85f07f22
FB
3903 if (c == -1)
3904 break;
3905 switch(c) {
3906 case 'L':
3907 licence();
3908 exit(1);
3909 case '?':
3910 case 'h':
3911 help();
3912 exit(1);
2ac887ba
PG
3913 case 'n':
3914 no_launch = 1;
3915 break;
3916 case 'd':
3917 ffserver_debug = 1;
2effd274 3918 ffserver_daemon = 0;
2ac887ba 3919 break;
85f07f22
FB
3920 case 'f':
3921 config_filename = optarg;
3922 break;
3923 default:
3924 exit(2);
3925 }
3926 }
3927
cde25790
PG
3928 putenv("http_proxy"); /* Kill the http_proxy */
3929
8256c0a3
PG
3930 srandom(gettime_ms() + (getpid() << 16));
3931
2effd274
FB
3932 /* address on which the server will handle HTTP connections */
3933 my_http_addr.sin_family = AF_INET;
3934 my_http_addr.sin_port = htons (8080);
3935 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
3936
3937 /* address on which the server will handle RTSP connections */
3938 my_rtsp_addr.sin_family = AF_INET;
3939 my_rtsp_addr.sin_port = htons (5454);
3940 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
3941
85f07f22 3942 nb_max_connections = 5;
42a63c6a 3943 nb_max_bandwidth = 1000;
85f07f22
FB
3944 first_stream = NULL;
3945