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