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