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