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