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