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