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