cosmetics: use braces when it makes the code easier to read.
[ffmpeg-lucabe.git] / ffserver.c
blob8525f7be79db2cc1fd7afaf14dc784f5460e0522
1 /*
2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include "config.h"
23 #if HAVE_CLOSESOCKET != 1
24 #define closesocket close
25 #endif
26 #include <string.h>
27 #include <stdlib.h>
28 #include "avformat.h"
30 #include <stdarg.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <sys/ioctl.h>
34 #ifdef HAVE_SYS_POLL_H
35 #include <sys/poll.h>
36 #endif
37 #include <errno.h>
38 #include <sys/time.h>
39 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
40 #include <time.h>
41 #include <sys/wait.h>
42 #include <signal.h>
43 #ifdef HAVE_DLFCN_H
44 #include <dlfcn.h>
45 #endif
47 #include "network.h"
48 #include "version.h"
49 #include "ffserver.h"
50 #include "random.h"
51 #include "avstring.h"
53 #undef exit
55 /* maximum number of simultaneous HTTP connections */
56 #define HTTP_MAX_CONNECTIONS 2000
58 enum HTTPState {
59 HTTPSTATE_WAIT_REQUEST,
60 HTTPSTATE_SEND_HEADER,
61 HTTPSTATE_SEND_DATA_HEADER,
62 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
63 HTTPSTATE_SEND_DATA_TRAILER,
64 HTTPSTATE_RECEIVE_DATA,
65 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
66 HTTPSTATE_READY,
68 RTSPSTATE_WAIT_REQUEST,
69 RTSPSTATE_SEND_REPLY,
70 RTSPSTATE_SEND_PACKET,
73 const char *http_state[] = {
74 "HTTP_WAIT_REQUEST",
75 "HTTP_SEND_HEADER",
77 "SEND_DATA_HEADER",
78 "SEND_DATA",
79 "SEND_DATA_TRAILER",
80 "RECEIVE_DATA",
81 "WAIT_FEED",
82 "READY",
84 "RTSP_WAIT_REQUEST",
85 "RTSP_SEND_REPLY",
86 "RTSP_SEND_PACKET",
89 #define IOBUFFER_INIT_SIZE 8192
91 /* timeouts are in ms */
92 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
93 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
95 #define SYNC_TIMEOUT (10 * 1000)
97 typedef struct {
98 int64_t count1, count2;
99 int64_t time1, time2;
100 } DataRateData;
102 /* context associated with one connection */
103 typedef struct HTTPContext {
104 enum HTTPState state;
105 int fd; /* socket file descriptor */
106 struct sockaddr_in from_addr; /* origin */
107 struct pollfd *poll_entry; /* used when polling */
108 int64_t timeout;
109 uint8_t *buffer_ptr, *buffer_end;
110 int http_error;
111 int post;
112 struct HTTPContext *next;
113 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
114 int64_t data_count;
115 /* feed input */
116 int feed_fd;
117 /* input format handling */
118 AVFormatContext *fmt_in;
119 int64_t start_time; /* In milliseconds - this wraps fairly often */
120 int64_t first_pts; /* initial pts value */
121 int64_t cur_pts; /* current pts value from the stream in us */
122 int64_t cur_frame_duration; /* duration of the current frame in us */
123 int cur_frame_bytes; /* output frame size, needed to compute
124 the time at which we send each
125 packet */
126 int pts_stream_index; /* stream we choose as clock reference */
127 int64_t cur_clock; /* current clock reference value in us */
128 /* output format handling */
129 struct FFStream *stream;
130 /* -1 is invalid stream */
131 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
132 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
133 int switch_pending;
134 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
135 int last_packet_sent; /* true if last data packet was sent */
136 int suppress_log;
137 DataRateData datarate;
138 int wmp_client_id;
139 char protocol[16];
140 char method[16];
141 char url[128];
142 int buffer_size;
143 uint8_t *buffer;
144 int is_packetized; /* if true, the stream is packetized */
145 int packet_stream_index; /* current stream for output in state machine */
147 /* RTSP state specific */
148 uint8_t *pb_buffer; /* XXX: use that in all the code */
149 ByteIOContext *pb;
150 int seq; /* RTSP sequence number */
152 /* RTP state specific */
153 enum RTSPProtocol rtp_protocol;
154 char session_id[32]; /* session id */
155 AVFormatContext *rtp_ctx[MAX_STREAMS];
157 /* RTP/UDP specific */
158 URLContext *rtp_handles[MAX_STREAMS];
160 /* RTP/TCP specific */
161 struct HTTPContext *rtsp_c;
162 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
163 } HTTPContext;
165 static AVFrame dummy_frame;
167 /* each generated stream is described here */
168 enum StreamType {
169 STREAM_TYPE_LIVE,
170 STREAM_TYPE_STATUS,
171 STREAM_TYPE_REDIRECT,
174 enum IPAddressAction {
175 IP_ALLOW = 1,
176 IP_DENY,
179 typedef struct IPAddressACL {
180 struct IPAddressACL *next;
181 enum IPAddressAction action;
182 /* These are in host order */
183 struct in_addr first;
184 struct in_addr last;
185 } IPAddressACL;
187 /* description of each stream of the ffserver.conf file */
188 typedef struct FFStream {
189 enum StreamType stream_type;
190 char filename[1024]; /* stream filename */
191 struct FFStream *feed; /* feed we are using (can be null if
192 coming from file) */
193 AVFormatParameters *ap_in; /* input parameters */
194 AVInputFormat *ifmt; /* if non NULL, force input format */
195 AVOutputFormat *fmt;
196 IPAddressACL *acl;
197 int nb_streams;
198 int prebuffer; /* Number of millseconds early to start */
199 int64_t max_time; /* Number of milliseconds to run */
200 int send_on_key;
201 AVStream *streams[MAX_STREAMS];
202 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
203 char feed_filename[1024]; /* file name of the feed storage, or
204 input file name for a stream */
205 char author[512];
206 char title[512];
207 char copyright[512];
208 char comment[512];
209 pid_t pid; /* Of ffmpeg process */
210 time_t pid_start; /* Of ffmpeg process */
211 char **child_argv;
212 struct FFStream *next;
213 int bandwidth; /* bandwidth, in kbits/s */
214 /* RTSP options */
215 char *rtsp_option;
216 /* multicast specific */
217 int is_multicast;
218 struct in_addr multicast_ip;
219 int multicast_port; /* first port used for multicast */
220 int multicast_ttl;
221 int loop; /* if true, send the stream in loops (only meaningful if file) */
223 /* feed specific */
224 int feed_opened; /* true if someone is writing to the feed */
225 int is_feed; /* true if it is a feed */
226 int readonly; /* True if writing is prohibited to the file */
227 int conns_served;
228 int64_t bytes_served;
229 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
230 int64_t feed_write_index; /* current write position in feed (it wraps round) */
231 int64_t feed_size; /* current size of feed */
232 struct FFStream *next_feed;
233 } FFStream;
235 typedef struct FeedData {
236 long long data_count;
237 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
238 } FeedData;
240 static struct sockaddr_in my_http_addr;
241 static struct sockaddr_in my_rtsp_addr;
243 static char logfilename[1024];
244 static HTTPContext *first_http_ctx;
245 static FFStream *first_feed; /* contains only feeds */
246 static FFStream *first_stream; /* contains all streams, including feeds */
248 static void new_connection(int server_fd, int is_rtsp);
249 static void close_connection(HTTPContext *c);
251 /* HTTP handling */
252 static int handle_connection(HTTPContext *c);
253 static int http_parse_request(HTTPContext *c);
254 static int http_send_data(HTTPContext *c);
255 static void compute_stats(HTTPContext *c);
256 static int open_input_stream(HTTPContext *c, const char *info);
257 static int http_start_receive_data(HTTPContext *c);
258 static int http_receive_data(HTTPContext *c);
260 /* RTSP handling */
261 static int rtsp_parse_request(HTTPContext *c);
262 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
263 static void rtsp_cmd_options(HTTPContext *c, const char *url);
264 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
265 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
266 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
267 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
269 /* SDP handling */
270 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
271 struct in_addr my_ip);
273 /* RTP handling */
274 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
275 FFStream *stream, const char *session_id,
276 enum RTSPProtocol rtp_protocol);
277 static int rtp_new_av_stream(HTTPContext *c,
278 int stream_index, struct sockaddr_in *dest_addr,
279 HTTPContext *rtsp_c);
281 static const char *my_program_name;
282 static const char *my_program_dir;
284 static int ffserver_debug;
285 static int ffserver_daemon;
286 static int no_launch;
287 static int need_to_start_children;
289 static int nb_max_connections;
290 static int nb_connections;
292 static int max_bandwidth;
293 static int current_bandwidth;
295 static int64_t cur_time; // Making this global saves on passing it around everywhere
297 static AVRandomState random_state;
299 static FILE *logfile = NULL;
301 static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
303 va_list ap;
304 va_start(ap, fmt);
306 if (logfile) {
307 vfprintf(logfile, fmt, ap);
308 fflush(logfile);
310 va_end(ap);
313 static char *ctime1(char *buf2)
315 time_t ti;
316 char *p;
318 ti = time(NULL);
319 p = ctime(&ti);
320 strcpy(buf2, p);
321 p = buf2 + strlen(p) - 1;
322 if (*p == '\n')
323 *p = '\0';
324 return buf2;
327 static void log_connection(HTTPContext *c)
329 char buf2[32];
331 if (c->suppress_log)
332 return;
334 http_log("%s - - [%s] \"%s %s %s\" %d %"PRId64"\n",
335 inet_ntoa(c->from_addr.sin_addr),
336 ctime1(buf2), c->method, c->url,
337 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
340 static void update_datarate(DataRateData *drd, int64_t count)
342 if (!drd->time1 && !drd->count1) {
343 drd->time1 = drd->time2 = cur_time;
344 drd->count1 = drd->count2 = count;
345 } else {
346 if (cur_time - drd->time2 > 5000) {
347 drd->time1 = drd->time2;
348 drd->count1 = drd->count2;
349 drd->time2 = cur_time;
350 drd->count2 = count;
355 /* In bytes per second */
356 static int compute_datarate(DataRateData *drd, int64_t count)
358 if (cur_time == drd->time1)
359 return 0;
361 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
365 static void start_children(FFStream *feed)
367 if (no_launch)
368 return;
370 for (; feed; feed = feed->next) {
371 if (feed->child_argv && !feed->pid) {
372 feed->pid_start = time(0);
374 feed->pid = fork();
376 if (feed->pid < 0) {
377 fprintf(stderr, "Unable to create children\n");
378 exit(1);
380 if (!feed->pid) {
381 /* In child */
382 char pathname[1024];
383 char *slash;
384 int i;
386 for (i = 3; i < 256; i++) {
387 close(i);
390 if (!ffserver_debug) {
391 i = open("/dev/null", O_RDWR);
392 if (i)
393 dup2(i, 0);
394 dup2(i, 1);
395 dup2(i, 2);
396 if (i)
397 close(i);
400 av_strlcpy(pathname, my_program_name, sizeof(pathname));
402 slash = strrchr(pathname, '/');
403 if (!slash) {
404 slash = pathname;
405 } else {
406 slash++;
408 strcpy(slash, "ffmpeg");
410 /* This is needed to make relative pathnames work */
411 chdir(my_program_dir);
413 signal(SIGPIPE, SIG_DFL);
415 execvp(pathname, feed->child_argv);
417 _exit(1);
423 /* open a listening socket */
424 static int socket_open_listen(struct sockaddr_in *my_addr)
426 int server_fd, tmp;
428 server_fd = socket(AF_INET,SOCK_STREAM,0);
429 if (server_fd < 0) {
430 perror ("socket");
431 return -1;
434 tmp = 1;
435 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
437 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
438 char bindmsg[32];
439 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
440 perror (bindmsg);
441 closesocket(server_fd);
442 return -1;
445 if (listen (server_fd, 5) < 0) {
446 perror ("listen");
447 closesocket(server_fd);
448 return -1;
450 ff_socket_nonblock(server_fd, 1);
452 return server_fd;
455 /* start all multicast streams */
456 static void start_multicast(void)
458 FFStream *stream;
459 char session_id[32];
460 HTTPContext *rtp_c;
461 struct sockaddr_in dest_addr;
462 int default_port, stream_index;
464 default_port = 6000;
465 for(stream = first_stream; stream != NULL; stream = stream->next) {
466 if (stream->is_multicast) {
467 /* open the RTP connection */
468 snprintf(session_id, sizeof(session_id), "%08x%08x",
469 av_random(&random_state), av_random(&random_state));
471 /* choose a port if none given */
472 if (stream->multicast_port == 0) {
473 stream->multicast_port = default_port;
474 default_port += 100;
477 dest_addr.sin_family = AF_INET;
478 dest_addr.sin_addr = stream->multicast_ip;
479 dest_addr.sin_port = htons(stream->multicast_port);
481 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
482 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
483 if (!rtp_c) {
484 continue;
486 if (open_input_stream(rtp_c, "") < 0) {
487 fprintf(stderr, "Could not open input stream for stream '%s'\n",
488 stream->filename);
489 continue;
492 /* open each RTP stream */
493 for(stream_index = 0; stream_index < stream->nb_streams;
494 stream_index++) {
495 dest_addr.sin_port = htons(stream->multicast_port +
496 2 * stream_index);
497 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
498 fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
499 stream->filename, stream_index);
500 exit(1);
504 /* change state to send data */
505 rtp_c->state = HTTPSTATE_SEND_DATA;
510 /* main loop of the http server */
511 static int http_server(void)
513 int server_fd, ret, rtsp_server_fd, delay, delay1;
514 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
515 HTTPContext *c, *c_next;
517 server_fd = socket_open_listen(&my_http_addr);
518 if (server_fd < 0)
519 return -1;
521 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
522 if (rtsp_server_fd < 0)
523 return -1;
525 http_log("ffserver started.\n");
527 start_children(first_feed);
529 first_http_ctx = NULL;
530 nb_connections = 0;
532 start_multicast();
534 for(;;) {
535 poll_entry = poll_table;
536 poll_entry->fd = server_fd;
537 poll_entry->events = POLLIN;
538 poll_entry++;
540 poll_entry->fd = rtsp_server_fd;
541 poll_entry->events = POLLIN;
542 poll_entry++;
544 /* wait for events on each HTTP handle */
545 c = first_http_ctx;
546 delay = 1000;
547 while (c != NULL) {
548 int fd;
549 fd = c->fd;
550 switch(c->state) {
551 case HTTPSTATE_SEND_HEADER:
552 case RTSPSTATE_SEND_REPLY:
553 case RTSPSTATE_SEND_PACKET:
554 c->poll_entry = poll_entry;
555 poll_entry->fd = fd;
556 poll_entry->events = POLLOUT;
557 poll_entry++;
558 break;
559 case HTTPSTATE_SEND_DATA_HEADER:
560 case HTTPSTATE_SEND_DATA:
561 case HTTPSTATE_SEND_DATA_TRAILER:
562 if (!c->is_packetized) {
563 /* for TCP, we output as much as we can (may need to put a limit) */
564 c->poll_entry = poll_entry;
565 poll_entry->fd = fd;
566 poll_entry->events = POLLOUT;
567 poll_entry++;
568 } else {
569 /* when ffserver is doing the timing, we work by
570 looking at which packet need to be sent every
571 10 ms */
572 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
573 if (delay1 < delay)
574 delay = delay1;
576 break;
577 case HTTPSTATE_WAIT_REQUEST:
578 case HTTPSTATE_RECEIVE_DATA:
579 case HTTPSTATE_WAIT_FEED:
580 case RTSPSTATE_WAIT_REQUEST:
581 /* need to catch errors */
582 c->poll_entry = poll_entry;
583 poll_entry->fd = fd;
584 poll_entry->events = POLLIN;/* Maybe this will work */
585 poll_entry++;
586 break;
587 default:
588 c->poll_entry = NULL;
589 break;
591 c = c->next;
594 /* wait for an event on one connection. We poll at least every
595 second to handle timeouts */
596 do {
597 ret = poll(poll_table, poll_entry - poll_table, delay);
598 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
599 ff_neterrno() != FF_NETERROR(EINTR))
600 return -1;
601 } while (ret < 0);
603 cur_time = av_gettime() / 1000;
605 if (need_to_start_children) {
606 need_to_start_children = 0;
607 start_children(first_feed);
610 /* now handle the events */
611 for(c = first_http_ctx; c != NULL; c = c_next) {
612 c_next = c->next;
613 if (handle_connection(c) < 0) {
614 /* close and free the connection */
615 log_connection(c);
616 close_connection(c);
620 poll_entry = poll_table;
621 /* new HTTP connection request ? */
622 if (poll_entry->revents & POLLIN) {
623 new_connection(server_fd, 0);
625 poll_entry++;
626 /* new RTSP connection request ? */
627 if (poll_entry->revents & POLLIN) {
628 new_connection(rtsp_server_fd, 1);
633 /* start waiting for a new HTTP/RTSP request */
634 static void start_wait_request(HTTPContext *c, int is_rtsp)
636 c->buffer_ptr = c->buffer;
637 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
639 if (is_rtsp) {
640 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
641 c->state = RTSPSTATE_WAIT_REQUEST;
642 } else {
643 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
644 c->state = HTTPSTATE_WAIT_REQUEST;
648 static void new_connection(int server_fd, int is_rtsp)
650 struct sockaddr_in from_addr;
651 int fd, len;
652 HTTPContext *c = NULL;
654 len = sizeof(from_addr);
655 fd = accept(server_fd, (struct sockaddr *)&from_addr,
656 &len);
657 if (fd < 0)
658 return;
659 ff_socket_nonblock(fd, 1);
661 /* XXX: should output a warning page when coming
662 close to the connection limit */
663 if (nb_connections >= nb_max_connections)
664 goto fail;
666 /* add a new connection */
667 c = av_mallocz(sizeof(HTTPContext));
668 if (!c)
669 goto fail;
671 c->fd = fd;
672 c->poll_entry = NULL;
673 c->from_addr = from_addr;
674 c->buffer_size = IOBUFFER_INIT_SIZE;
675 c->buffer = av_malloc(c->buffer_size);
676 if (!c->buffer)
677 goto fail;
679 c->next = first_http_ctx;
680 first_http_ctx = c;
681 nb_connections++;
683 start_wait_request(c, is_rtsp);
685 return;
687 fail:
688 if (c) {
689 av_free(c->buffer);
690 av_free(c);
692 closesocket(fd);
695 static void close_connection(HTTPContext *c)
697 HTTPContext **cp, *c1;
698 int i, nb_streams;
699 AVFormatContext *ctx;
700 URLContext *h;
701 AVStream *st;
703 /* remove connection from list */
704 cp = &first_http_ctx;
705 while ((*cp) != NULL) {
706 c1 = *cp;
707 if (c1 == c) {
708 *cp = c->next;
709 } else {
710 cp = &c1->next;
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;
720 /* remove connection associated resources */
721 if (c->fd >= 0)
722 closesocket(c->fd);
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];
727 if (st->codec->codec) {
728 avcodec_close(st->codec);
731 av_close_input_file(c->fmt_in);
734 /* free RTP output streams if any */
735 nb_streams = 0;
736 if (c->stream)
737 nb_streams = c->stream->nb_streams;
739 for(i=0;i<nb_streams;i++) {
740 ctx = c->rtp_ctx[i];
741 if (ctx) {
742 av_write_trailer(ctx);
743 av_free(ctx);
745 h = c->rtp_handles[i];
746 if (h) {
747 url_close(h);
751 ctx = &c->fmt_ctx;
753 if (!c->last_packet_sent) {
754 if (ctx->oformat) {
755 /* prepare header */
756 if (url_open_dyn_buf(&ctx->pb) >= 0) {
757 av_write_trailer(ctx);
758 url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
763 for(i=0; i<ctx->nb_streams; i++)
764 av_free(ctx->streams[i]) ;
766 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
767 current_bandwidth -= c->stream->bandwidth;
769 /* signal that there is no feed if we are the feeder socket */
770 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
771 c->stream->feed_opened = 0;
772 close(c->feed_fd);
775 av_freep(&c->pb_buffer);
776 av_freep(&c->packet_buffer);
777 av_free(c->buffer);
778 av_free(c);
779 nb_connections--;
782 static int handle_connection(HTTPContext *c)
784 int len, ret;
786 switch(c->state) {
787 case HTTPSTATE_WAIT_REQUEST:
788 case RTSPSTATE_WAIT_REQUEST:
789 /* timeout ? */
790 if ((c->timeout - cur_time) < 0)
791 return -1;
792 if (c->poll_entry->revents & (POLLERR | POLLHUP))
793 return -1;
795 /* no need to read if no events */
796 if (!(c->poll_entry->revents & POLLIN))
797 return 0;
798 /* read the data */
799 read_loop:
800 len = recv(c->fd, c->buffer_ptr, 1, 0);
801 if (len < 0) {
802 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
803 ff_neterrno() != FF_NETERROR(EINTR))
804 return -1;
805 } else if (len == 0) {
806 return -1;
807 } else {
808 /* search for end of request. */
809 uint8_t *ptr;
810 c->buffer_ptr += len;
811 ptr = c->buffer_ptr;
812 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
813 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
814 /* request found : parse it and reply */
815 if (c->state == HTTPSTATE_WAIT_REQUEST) {
816 ret = http_parse_request(c);
817 } else {
818 ret = rtsp_parse_request(c);
820 if (ret < 0)
821 return -1;
822 } else if (ptr >= c->buffer_end) {
823 /* request too long: cannot do anything */
824 return -1;
825 } else goto read_loop;
827 break;
829 case HTTPSTATE_SEND_HEADER:
830 if (c->poll_entry->revents & (POLLERR | POLLHUP))
831 return -1;
833 /* no need to write if no events */
834 if (!(c->poll_entry->revents & POLLOUT))
835 return 0;
836 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
837 if (len < 0) {
838 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
839 ff_neterrno() != FF_NETERROR(EINTR)) {
840 /* error : close connection */
841 av_freep(&c->pb_buffer);
842 return -1;
844 } else {
845 c->buffer_ptr += len;
846 if (c->stream)
847 c->stream->bytes_served += len;
848 c->data_count += len;
849 if (c->buffer_ptr >= c->buffer_end) {
850 av_freep(&c->pb_buffer);
851 /* if error, exit */
852 if (c->http_error) {
853 return -1;
855 /* all the buffer was sent : synchronize to the incoming stream */
856 c->state = HTTPSTATE_SEND_DATA_HEADER;
857 c->buffer_ptr = c->buffer_end = c->buffer;
860 break;
862 case HTTPSTATE_SEND_DATA:
863 case HTTPSTATE_SEND_DATA_HEADER:
864 case HTTPSTATE_SEND_DATA_TRAILER:
865 /* for packetized output, we consider we can always write (the
866 input streams sets the speed). It may be better to verify
867 that we do not rely too much on the kernel queues */
868 if (!c->is_packetized) {
869 if (c->poll_entry->revents & (POLLERR | POLLHUP))
870 return -1;
872 /* no need to read if no events */
873 if (!(c->poll_entry->revents & POLLOUT))
874 return 0;
876 if (http_send_data(c) < 0)
877 return -1;
878 /* close connection if trailer sent */
879 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
880 return -1;
881 break;
882 case HTTPSTATE_RECEIVE_DATA:
883 /* no need to read if no events */
884 if (c->poll_entry->revents & (POLLERR | POLLHUP))
885 return -1;
886 if (!(c->poll_entry->revents & POLLIN))
887 return 0;
888 if (http_receive_data(c) < 0)
889 return -1;
890 break;
891 case HTTPSTATE_WAIT_FEED:
892 /* no need to read if no events */
893 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
894 return -1;
896 /* nothing to do, we'll be waken up by incoming feed packets */
897 break;
899 case RTSPSTATE_SEND_REPLY:
900 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
901 av_freep(&c->pb_buffer);
902 return -1;
904 /* no need to write if no events */
905 if (!(c->poll_entry->revents & POLLOUT))
906 return 0;
907 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
908 if (len < 0) {
909 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
910 ff_neterrno() != FF_NETERROR(EINTR)) {
911 /* error : close connection */
912 av_freep(&c->pb_buffer);
913 return -1;
915 } else {
916 c->buffer_ptr += len;
917 c->data_count += len;
918 if (c->buffer_ptr >= c->buffer_end) {
919 /* all the buffer was sent : wait for a new request */
920 av_freep(&c->pb_buffer);
921 start_wait_request(c, 1);
924 break;
925 case RTSPSTATE_SEND_PACKET:
926 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
927 av_freep(&c->packet_buffer);
928 return -1;
930 /* no need to write if no events */
931 if (!(c->poll_entry->revents & POLLOUT))
932 return 0;
933 len = send(c->fd, c->packet_buffer_ptr,
934 c->packet_buffer_end - c->packet_buffer_ptr, 0);
935 if (len < 0) {
936 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
937 ff_neterrno() != FF_NETERROR(EINTR)) {
938 /* error : close connection */
939 av_freep(&c->packet_buffer);
940 return -1;
942 } else {
943 c->packet_buffer_ptr += len;
944 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
945 /* all the buffer was sent : wait for a new request */
946 av_freep(&c->packet_buffer);
947 c->state = RTSPSTATE_WAIT_REQUEST;
950 break;
951 case HTTPSTATE_READY:
952 /* nothing to do */
953 break;
954 default:
955 return -1;
957 return 0;
960 static int extract_rates(char *rates, int ratelen, const char *request)
962 const char *p;
964 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
965 if (strncasecmp(p, "Pragma:", 7) == 0) {
966 const char *q = p + 7;
968 while (*q && *q != '\n' && isspace(*q))
969 q++;
971 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
972 int stream_no;
973 int rate_no;
975 q += 20;
977 memset(rates, 0xff, ratelen);
979 while (1) {
980 while (*q && *q != '\n' && *q != ':')
981 q++;
983 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
984 break;
986 stream_no--;
987 if (stream_no < ratelen && stream_no >= 0) {
988 rates[stream_no] = rate_no;
991 while (*q && *q != '\n' && !isspace(*q))
992 q++;
995 return 1;
998 p = strchr(p, '\n');
999 if (!p)
1000 break;
1002 p++;
1005 return 0;
1008 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1010 int i;
1011 int best_bitrate = 100000000;
1012 int best = -1;
1014 for (i = 0; i < feed->nb_streams; i++) {
1015 AVCodecContext *feed_codec = feed->streams[i]->codec;
1017 if (feed_codec->codec_id != codec->codec_id ||
1018 feed_codec->sample_rate != codec->sample_rate ||
1019 feed_codec->width != codec->width ||
1020 feed_codec->height != codec->height) {
1021 continue;
1024 /* Potential stream */
1026 /* We want the fastest stream less than bit_rate, or the slowest
1027 * faster than bit_rate
1030 if (feed_codec->bit_rate <= bit_rate) {
1031 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1032 best_bitrate = feed_codec->bit_rate;
1033 best = i;
1035 } else {
1036 if (feed_codec->bit_rate < best_bitrate) {
1037 best_bitrate = feed_codec->bit_rate;
1038 best = i;
1043 return best;
1046 static int modify_current_stream(HTTPContext *c, char *rates)
1048 int i;
1049 FFStream *req = c->stream;
1050 int action_required = 0;
1052 /* Not much we can do for a feed */
1053 if (!req->feed)
1054 return 0;
1056 for (i = 0; i < req->nb_streams; i++) {
1057 AVCodecContext *codec = req->streams[i]->codec;
1059 switch(rates[i]) {
1060 case 0:
1061 c->switch_feed_streams[i] = req->feed_streams[i];
1062 break;
1063 case 1:
1064 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1065 break;
1066 case 2:
1067 /* Wants off or slow */
1068 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1069 #ifdef WANTS_OFF
1070 /* This doesn't work well when it turns off the only stream! */
1071 c->switch_feed_streams[i] = -2;
1072 c->feed_streams[i] = -2;
1073 #endif
1074 break;
1077 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1078 action_required = 1;
1081 return action_required;
1085 static void do_switch_stream(HTTPContext *c, int i)
1087 if (c->switch_feed_streams[i] >= 0) {
1088 #ifdef PHILIP
1089 c->feed_streams[i] = c->switch_feed_streams[i];
1090 #endif
1092 /* Now update the stream */
1094 c->switch_feed_streams[i] = -1;
1097 /* XXX: factorize in utils.c ? */
1098 /* XXX: take care with different space meaning */
1099 static void skip_spaces(const char **pp)
1101 const char *p;
1102 p = *pp;
1103 while (*p == ' ' || *p == '\t')
1104 p++;
1105 *pp = p;
1108 static void get_word(char *buf, int buf_size, const char **pp)
1110 const char *p;
1111 char *q;
1113 p = *pp;
1114 skip_spaces(&p);
1115 q = buf;
1116 while (!isspace(*p) && *p != '\0') {
1117 if ((q - buf) < buf_size - 1)
1118 *q++ = *p;
1119 p++;
1121 if (buf_size > 0)
1122 *q = '\0';
1123 *pp = p;
1126 static int validate_acl(FFStream *stream, HTTPContext *c)
1128 enum IPAddressAction last_action = IP_DENY;
1129 IPAddressACL *acl;
1130 struct in_addr *src = &c->from_addr.sin_addr;
1131 unsigned long src_addr = src->s_addr;
1133 for (acl = stream->acl; acl; acl = acl->next) {
1134 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr) {
1135 return (acl->action == IP_ALLOW) ? 1 : 0;
1137 last_action = acl->action;
1140 /* Nothing matched, so return not the last action */
1141 return (last_action == IP_DENY) ? 1 : 0;
1144 /* compute the real filename of a file by matching it without its
1145 extensions to all the stream filenames */
1146 static void compute_real_filename(char *filename, int max_size)
1148 char file1[1024];
1149 char file2[1024];
1150 char *p;
1151 FFStream *stream;
1153 /* compute filename by matching without the file extensions */
1154 av_strlcpy(file1, filename, sizeof(file1));
1155 p = strrchr(file1, '.');
1156 if (p)
1157 *p = '\0';
1158 for(stream = first_stream; stream != NULL; stream = stream->next) {
1159 av_strlcpy(file2, stream->filename, sizeof(file2));
1160 p = strrchr(file2, '.');
1161 if (p)
1162 *p = '\0';
1163 if (!strcmp(file1, file2)) {
1164 av_strlcpy(filename, stream->filename, max_size);
1165 break;
1170 enum RedirType {
1171 REDIR_NONE,
1172 REDIR_ASX,
1173 REDIR_RAM,
1174 REDIR_ASF,
1175 REDIR_RTSP,
1176 REDIR_SDP,
1179 /* parse http request and prepare header */
1180 static int http_parse_request(HTTPContext *c)
1182 char *p;
1183 enum RedirType redir_type;
1184 char cmd[32];
1185 char info[1024], filename[1024];
1186 char url[1024], *q;
1187 char protocol[32];
1188 char msg[1024];
1189 const char *mime_type;
1190 FFStream *stream;
1191 int i;
1192 char ratebuf[32];
1193 char *useragent = 0;
1195 p = c->buffer;
1196 get_word(cmd, sizeof(cmd), (const char **)&p);
1197 av_strlcpy(c->method, cmd, sizeof(c->method));
1199 if (!strcmp(cmd, "GET"))
1200 c->post = 0;
1201 else if (!strcmp(cmd, "POST"))
1202 c->post = 1;
1203 else
1204 return -1;
1206 get_word(url, sizeof(url), (const char **)&p);
1207 av_strlcpy(c->url, url, sizeof(c->url));
1209 get_word(protocol, sizeof(protocol), (const char **)&p);
1210 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1211 return -1;
1213 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1215 if (ffserver_debug)
1216 http_log("New connection: %s %s\n", cmd, url);
1218 /* find the filename and the optional info string in the request */
1219 p = strchr(url, '?');
1220 if (p) {
1221 av_strlcpy(info, p, sizeof(info));
1222 *p = '\0';
1223 } else {
1224 info[0] = '\0';
1227 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1229 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1230 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1231 useragent = p + 11;
1232 if (*useragent && *useragent != '\n' && isspace(*useragent))
1233 useragent++;
1234 break;
1236 p = strchr(p, '\n');
1237 if (!p)
1238 break;
1240 p++;
1243 redir_type = REDIR_NONE;
1244 if (match_ext(filename, "asx")) {
1245 redir_type = REDIR_ASX;
1246 filename[strlen(filename)-1] = 'f';
1247 } else if (match_ext(filename, "asf") &&
1248 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1249 /* if this isn't WMP or lookalike, return the redirector file */
1250 redir_type = REDIR_ASF;
1251 } else if (match_ext(filename, "rpm,ram")) {
1252 redir_type = REDIR_RAM;
1253 strcpy(filename + strlen(filename)-2, "m");
1254 } else if (match_ext(filename, "rtsp")) {
1255 redir_type = REDIR_RTSP;
1256 compute_real_filename(filename, sizeof(filename) - 1);
1257 } else if (match_ext(filename, "sdp")) {
1258 redir_type = REDIR_SDP;
1259 compute_real_filename(filename, sizeof(filename) - 1);
1262 // "redirect" / request to index.html
1263 if (!strlen(filename))
1264 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1266 stream = first_stream;
1267 while (stream != NULL) {
1268 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1269 break;
1270 stream = stream->next;
1272 if (stream == NULL) {
1273 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1274 goto send_error;
1277 c->stream = stream;
1278 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1279 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1281 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1282 c->http_error = 301;
1283 q = c->buffer;
1284 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1285 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1286 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1287 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1288 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1289 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1290 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1292 /* prepare output buffer */
1293 c->buffer_ptr = c->buffer;
1294 c->buffer_end = q;
1295 c->state = HTTPSTATE_SEND_HEADER;
1296 return 0;
1299 /* If this is WMP, get the rate information */
1300 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1301 if (modify_current_stream(c, ratebuf)) {
1302 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1303 if (c->switch_feed_streams[i] >= 0)
1304 do_switch_stream(c, i);
1309 /* If already streaming this feed, do not let start another feeder. */
1310 if (stream->feed_opened) {
1311 snprintf(msg, sizeof(msg), "This feed is already being received.");
1312 goto send_error;
1315 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1316 current_bandwidth += stream->bandwidth;
1319 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1320 c->http_error = 200;
1321 q = c->buffer;
1322 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1323 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1324 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1325 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1326 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");
1327 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",
1328 current_bandwidth, max_bandwidth);
1329 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1331 /* prepare output buffer */
1332 c->buffer_ptr = c->buffer;
1333 c->buffer_end = q;
1334 c->state = HTTPSTATE_SEND_HEADER;
1335 return 0;
1338 if (redir_type != REDIR_NONE) {
1339 char *hostinfo = 0;
1341 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1342 if (strncasecmp(p, "Host:", 5) == 0) {
1343 hostinfo = p + 5;
1344 break;
1346 p = strchr(p, '\n');
1347 if (!p)
1348 break;
1350 p++;
1353 if (hostinfo) {
1354 char *eoh;
1355 char hostbuf[260];
1357 while (isspace(*hostinfo))
1358 hostinfo++;
1360 eoh = strchr(hostinfo, '\n');
1361 if (eoh) {
1362 if (eoh[-1] == '\r')
1363 eoh--;
1365 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1366 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1367 hostbuf[eoh - hostinfo] = 0;
1369 c->http_error = 200;
1370 q = c->buffer;
1371 switch(redir_type) {
1372 case REDIR_ASX:
1373 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1374 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1375 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1376 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1377 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1378 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1379 hostbuf, filename, info);
1380 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1381 break;
1382 case REDIR_RAM:
1383 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1384 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\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, "# Autogenerated by ffserver\r\n");
1387 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1388 hostbuf, filename, info);
1389 break;
1390 case REDIR_ASF:
1391 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1392 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1393 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1394 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1395 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1396 hostbuf, filename, info);
1397 break;
1398 case REDIR_RTSP:
1400 char hostname[256], *p;
1401 /* extract only hostname */
1402 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1403 p = strrchr(hostname, ':');
1404 if (p)
1405 *p = '\0';
1406 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1407 /* XXX: incorrect mime type ? */
1408 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1409 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1410 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1411 hostname, ntohs(my_rtsp_addr.sin_port),
1412 filename);
1414 break;
1415 case REDIR_SDP:
1417 uint8_t *sdp_data;
1418 int sdp_data_size, len;
1419 struct sockaddr_in my_addr;
1421 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1422 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1423 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1425 len = sizeof(my_addr);
1426 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1428 /* XXX: should use a dynamic buffer */
1429 sdp_data_size = prepare_sdp_description(stream,
1430 &sdp_data,
1431 my_addr.sin_addr);
1432 if (sdp_data_size > 0) {
1433 memcpy(q, sdp_data, sdp_data_size);
1434 q += sdp_data_size;
1435 *q = '\0';
1436 av_free(sdp_data);
1439 break;
1440 default:
1441 abort();
1442 break;
1445 /* prepare output buffer */
1446 c->buffer_ptr = c->buffer;
1447 c->buffer_end = q;
1448 c->state = HTTPSTATE_SEND_HEADER;
1449 return 0;
1454 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1455 goto send_error;
1458 stream->conns_served++;
1460 /* XXX: add there authenticate and IP match */
1462 if (c->post) {
1463 /* if post, it means a feed is being sent */
1464 if (!stream->is_feed) {
1465 /* However it might be a status report from WMP! Lets log the data
1466 * as it might come in handy one day
1468 char *logline = 0;
1469 int client_id = 0;
1471 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1472 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1473 logline = p;
1474 break;
1476 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1477 client_id = strtol(p + 18, 0, 10);
1479 p = strchr(p, '\n');
1480 if (!p)
1481 break;
1483 p++;
1486 if (logline) {
1487 char *eol = strchr(logline, '\n');
1489 logline += 17;
1491 if (eol) {
1492 if (eol[-1] == '\r')
1493 eol--;
1494 http_log("%.*s\n", (int) (eol - logline), logline);
1495 c->suppress_log = 1;
1499 #ifdef DEBUG_WMP
1500 http_log("\nGot request:\n%s\n", c->buffer);
1501 #endif
1503 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1504 HTTPContext *wmpc;
1506 /* Now we have to find the client_id */
1507 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1508 if (wmpc->wmp_client_id == client_id)
1509 break;
1512 if (wmpc) {
1513 if (modify_current_stream(wmpc, ratebuf)) {
1514 wmpc->switch_pending = 1;
1519 snprintf(msg, sizeof(msg), "POST command not handled");
1520 c->stream = 0;
1521 goto send_error;
1523 if (http_start_receive_data(c) < 0) {
1524 snprintf(msg, sizeof(msg), "could not open feed");
1525 goto send_error;
1527 c->http_error = 0;
1528 c->state = HTTPSTATE_RECEIVE_DATA;
1529 return 0;
1532 #ifdef DEBUG_WMP
1533 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1534 http_log("\nGot request:\n%s\n", c->buffer);
1536 #endif
1538 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1539 goto send_stats;
1541 /* open input stream */
1542 if (open_input_stream(c, info) < 0) {
1543 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1544 goto send_error;
1547 /* prepare http header */
1548 q = c->buffer;
1549 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1550 mime_type = c->stream->fmt->mime_type;
1551 if (!mime_type)
1552 mime_type = "application/x-octet-stream";
1553 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1555 /* for asf, we need extra headers */
1556 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1557 /* Need to allocate a client id */
1559 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1561 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);
1563 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1564 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1566 /* prepare output buffer */
1567 c->http_error = 0;
1568 c->buffer_ptr = c->buffer;
1569 c->buffer_end = q;
1570 c->state = HTTPSTATE_SEND_HEADER;
1571 return 0;
1572 send_error:
1573 c->http_error = 404;
1574 q = c->buffer;
1575 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1576 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1577 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1578 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1579 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1580 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1581 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1583 /* prepare output buffer */
1584 c->buffer_ptr = c->buffer;
1585 c->buffer_end = q;
1586 c->state = HTTPSTATE_SEND_HEADER;
1587 return 0;
1588 send_stats:
1589 compute_stats(c);
1590 c->http_error = 200; /* horrible : we use this value to avoid
1591 going to the send data state */
1592 c->state = HTTPSTATE_SEND_HEADER;
1593 return 0;
1596 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1598 static const char *suffix = " kMGTP";
1599 const char *s;
1601 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1604 url_fprintf(pb, "%"PRId64"%c", count, *s);
1607 static void compute_stats(HTTPContext *c)
1609 HTTPContext *c1;
1610 FFStream *stream;
1611 char *p;
1612 time_t ti;
1613 int i, len;
1614 ByteIOContext pb1, *pb = &pb1;
1616 if (url_open_dyn_buf(pb) < 0) {
1617 /* XXX: return an error ? */
1618 c->buffer_ptr = c->buffer;
1619 c->buffer_end = c->buffer;
1620 return;
1623 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1624 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1625 url_fprintf(pb, "Pragma: no-cache\r\n");
1626 url_fprintf(pb, "\r\n");
1628 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1629 if (c->stream->feed_filename) {
1630 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1632 url_fprintf(pb, "</HEAD>\n<BODY>");
1633 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1634 /* format status */
1635 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1636 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1637 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");
1638 stream = first_stream;
1639 while (stream != NULL) {
1640 char sfilename[1024];
1641 char *eosf;
1643 if (stream->feed != stream) {
1644 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1645 eosf = sfilename + strlen(sfilename);
1646 if (eosf - sfilename >= 4) {
1647 if (strcmp(eosf - 4, ".asf") == 0) {
1648 strcpy(eosf - 4, ".asx");
1649 } else if (strcmp(eosf - 3, ".rm") == 0) {
1650 strcpy(eosf - 3, ".ram");
1651 } else if (stream->fmt == &rtp_muxer) {
1652 /* generate a sample RTSP director if
1653 unicast. Generate an SDP redirector if
1654 multicast */
1655 eosf = strrchr(sfilename, '.');
1656 if (!eosf)
1657 eosf = sfilename + strlen(sfilename);
1658 if (stream->is_multicast)
1659 strcpy(eosf, ".sdp");
1660 else
1661 strcpy(eosf, ".rtsp");
1665 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1666 sfilename, stream->filename);
1667 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1668 stream->conns_served);
1669 fmt_bytecount(pb, stream->bytes_served);
1670 switch(stream->stream_type) {
1671 case STREAM_TYPE_LIVE:
1673 int audio_bit_rate = 0;
1674 int video_bit_rate = 0;
1675 const char *audio_codec_name = "";
1676 const char *video_codec_name = "";
1677 const char *audio_codec_name_extra = "";
1678 const char *video_codec_name_extra = "";
1680 for(i=0;i<stream->nb_streams;i++) {
1681 AVStream *st = stream->streams[i];
1682 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1683 switch(st->codec->codec_type) {
1684 case CODEC_TYPE_AUDIO:
1685 audio_bit_rate += st->codec->bit_rate;
1686 if (codec) {
1687 if (*audio_codec_name)
1688 audio_codec_name_extra = "...";
1689 audio_codec_name = codec->name;
1691 break;
1692 case CODEC_TYPE_VIDEO:
1693 video_bit_rate += st->codec->bit_rate;
1694 if (codec) {
1695 if (*video_codec_name)
1696 video_codec_name_extra = "...";
1697 video_codec_name = codec->name;
1699 break;
1700 case CODEC_TYPE_DATA:
1701 video_bit_rate += st->codec->bit_rate;
1702 break;
1703 default:
1704 abort();
1707 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",
1708 stream->fmt->name,
1709 stream->bandwidth,
1710 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1711 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1712 if (stream->feed) {
1713 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1714 } else {
1715 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1717 url_fprintf(pb, "\n");
1719 break;
1720 default:
1721 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1722 break;
1725 stream = stream->next;
1727 url_fprintf(pb, "</TABLE>\n");
1729 stream = first_stream;
1730 while (stream != NULL) {
1731 if (stream->feed == stream) {
1732 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1733 if (stream->pid) {
1734 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1736 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1738 FILE *pid_stat;
1739 char ps_cmd[64];
1741 /* This is somewhat linux specific I guess */
1742 snprintf(ps_cmd, sizeof(ps_cmd),
1743 "ps -o \"%%cpu,cputime\" --no-headers %d",
1744 stream->pid);
1746 pid_stat = popen(ps_cmd, "r");
1747 if (pid_stat) {
1748 char cpuperc[10];
1749 char cpuused[64];
1751 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1752 cpuused) == 2) {
1753 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1754 cpuperc, cpuused);
1756 fclose(pid_stat);
1759 #endif
1761 url_fprintf(pb, "<p>");
1763 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");
1765 for (i = 0; i < stream->nb_streams; i++) {
1766 AVStream *st = stream->streams[i];
1767 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1768 const char *type = "unknown";
1769 char parameters[64];
1771 parameters[0] = 0;
1773 switch(st->codec->codec_type) {
1774 case CODEC_TYPE_AUDIO:
1775 type = "audio";
1776 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1777 break;
1778 case CODEC_TYPE_VIDEO:
1779 type = "video";
1780 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1781 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1782 break;
1783 default:
1784 abort();
1786 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1787 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1789 url_fprintf(pb, "</table>\n");
1792 stream = stream->next;
1795 #if 0
1797 float avg;
1798 AVCodecContext *enc;
1799 char buf[1024];
1801 /* feed status */
1802 stream = first_feed;
1803 while (stream != NULL) {
1804 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1805 url_fprintf(pb, "<TABLE>\n");
1806 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1807 for(i=0;i<stream->nb_streams;i++) {
1808 AVStream *st = stream->streams[i];
1809 FeedData *fdata = st->priv_data;
1810 enc = st->codec;
1812 avcodec_string(buf, sizeof(buf), enc);
1813 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1814 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1815 avg /= enc->frame_size;
1816 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1817 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1819 url_fprintf(pb, "</TABLE>\n");
1820 stream = stream->next_feed;
1823 #endif
1825 /* connection status */
1826 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1828 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1829 nb_connections, nb_max_connections);
1831 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1832 current_bandwidth, max_bandwidth);
1834 url_fprintf(pb, "<TABLE>\n");
1835 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");
1836 c1 = first_http_ctx;
1837 i = 0;
1838 while (c1 != NULL) {
1839 int bitrate;
1840 int j;
1842 bitrate = 0;
1843 if (c1->stream) {
1844 for (j = 0; j < c1->stream->nb_streams; j++) {
1845 if (!c1->stream->feed) {
1846 bitrate += c1->stream->streams[j]->codec->bit_rate;
1847 } else {
1848 if (c1->feed_streams[j] >= 0) {
1849 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1855 i++;
1856 p = inet_ntoa(c1->from_addr.sin_addr);
1857 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1859 c1->stream ? c1->stream->filename : "",
1860 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1862 c1->protocol,
1863 http_state[c1->state]);
1864 fmt_bytecount(pb, bitrate);
1865 url_fprintf(pb, "<td align=right>");
1866 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1867 url_fprintf(pb, "<td align=right>");
1868 fmt_bytecount(pb, c1->data_count);
1869 url_fprintf(pb, "\n");
1870 c1 = c1->next;
1872 url_fprintf(pb, "</TABLE>\n");
1874 /* date */
1875 ti = time(NULL);
1876 p = ctime(&ti);
1877 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1878 url_fprintf(pb, "</BODY>\n</HTML>\n");
1880 len = url_close_dyn_buf(pb, &c->pb_buffer);
1881 c->buffer_ptr = c->pb_buffer;
1882 c->buffer_end = c->pb_buffer + len;
1885 /* check if the parser needs to be opened for stream i */
1886 static void open_parser(AVFormatContext *s, int i)
1888 AVStream *st = s->streams[i];
1889 AVCodec *codec;
1891 if (!st->codec->codec) {
1892 codec = avcodec_find_decoder(st->codec->codec_id);
1893 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1894 st->codec->parse_only = 1;
1895 if (avcodec_open(st->codec, codec) < 0) {
1896 st->codec->parse_only = 0;
1902 static int open_input_stream(HTTPContext *c, const char *info)
1904 char buf[128];
1905 char input_filename[1024];
1906 AVFormatContext *s;
1907 int buf_size, i;
1908 int64_t stream_pos;
1910 /* find file name */
1911 if (c->stream->feed) {
1912 strcpy(input_filename, c->stream->feed->feed_filename);
1913 buf_size = FFM_PACKET_SIZE;
1914 /* compute position (absolute time) */
1915 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1916 stream_pos = parse_date(buf, 0);
1917 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1918 int prebuffer = strtol(buf, 0, 10);
1919 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1920 } else {
1921 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1923 } else {
1924 strcpy(input_filename, c->stream->feed_filename);
1925 buf_size = 0;
1926 /* compute position (relative time) */
1927 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1928 stream_pos = parse_date(buf, 1);
1929 } else {
1930 stream_pos = 0;
1933 if (input_filename[0] == '\0')
1934 return -1;
1936 #if 0
1937 { time_t when = stream_pos / 1000000;
1938 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1940 #endif
1942 /* open stream */
1943 if (av_open_input_file(&s, input_filename, c->stream->ifmt,
1944 buf_size, c->stream->ap_in) < 0) {
1945 http_log("%s not found", input_filename);
1946 return -1;
1948 c->fmt_in = s;
1950 /* open each parser */
1951 for(i=0;i<s->nb_streams;i++)
1952 open_parser(s, i);
1954 /* choose stream as clock source (we favorize video stream if
1955 present) for packet sending */
1956 c->pts_stream_index = 0;
1957 for(i=0;i<c->stream->nb_streams;i++) {
1958 if (c->pts_stream_index == 0 &&
1959 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1960 c->pts_stream_index = i;
1964 #if 1
1965 if (c->fmt_in->iformat->read_seek) {
1966 c->fmt_in->iformat->read_seek(c->fmt_in, 0, stream_pos, 0);
1968 #endif
1969 /* set the start time (needed for maxtime and RTP packet timing) */
1970 c->start_time = cur_time;
1971 c->first_pts = AV_NOPTS_VALUE;
1972 return 0;
1975 /* return the server clock (in us) */
1976 static int64_t get_server_clock(HTTPContext *c)
1978 /* compute current pts value from system time */
1979 return (cur_time - c->start_time) * 1000;
1982 /* return the estimated time at which the current packet must be sent
1983 (in us) */
1984 static int64_t get_packet_send_clock(HTTPContext *c)
1986 int bytes_left, bytes_sent, frame_bytes;
1988 frame_bytes = c->cur_frame_bytes;
1989 if (frame_bytes <= 0) {
1990 return c->cur_pts;
1991 } else {
1992 bytes_left = c->buffer_end - c->buffer_ptr;
1993 bytes_sent = frame_bytes - bytes_left;
1994 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
1999 static int http_prepare_data(HTTPContext *c)
2001 int i, len, ret;
2002 AVFormatContext *ctx;
2004 av_freep(&c->pb_buffer);
2005 switch(c->state) {
2006 case HTTPSTATE_SEND_DATA_HEADER:
2007 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2008 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2009 sizeof(c->fmt_ctx.author));
2010 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2011 sizeof(c->fmt_ctx.comment));
2012 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2013 sizeof(c->fmt_ctx.copyright));
2014 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2015 sizeof(c->fmt_ctx.title));
2017 /* open output stream by using specified codecs */
2018 c->fmt_ctx.oformat = c->stream->fmt;
2019 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2020 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2021 AVStream *st;
2022 AVStream *src;
2023 st = av_mallocz(sizeof(AVStream));
2024 st->codec= avcodec_alloc_context();
2025 c->fmt_ctx.streams[i] = st;
2026 /* if file or feed, then just take streams from FFStream struct */
2027 if (!c->stream->feed ||
2028 c->stream->feed == c->stream)
2029 src = c->stream->streams[i];
2030 else
2031 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2033 *st = *src;
2034 st->priv_data = 0;
2035 st->codec->frame_number = 0; /* XXX: should be done in
2036 AVStream, not in codec */
2037 /* I'm pretty sure that this is not correct...
2038 * However, without it, we crash
2040 st->codec->coded_frame = &dummy_frame;
2042 c->got_key_frame = 0;
2044 /* prepare header and save header data in a stream */
2045 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2046 /* XXX: potential leak */
2047 return -1;
2049 c->fmt_ctx.pb.is_streamed = 1;
2051 av_set_parameters(&c->fmt_ctx, NULL);
2052 if (av_write_header(&c->fmt_ctx) < 0)
2053 return -1;
2055 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2056 c->buffer_ptr = c->pb_buffer;
2057 c->buffer_end = c->pb_buffer + len;
2059 c->state = HTTPSTATE_SEND_DATA;
2060 c->last_packet_sent = 0;
2061 break;
2062 case HTTPSTATE_SEND_DATA:
2063 /* find a new packet */
2065 AVPacket pkt;
2067 /* read a packet from the input stream */
2068 if (c->stream->feed) {
2069 ffm_set_write_index(c->fmt_in,
2070 c->stream->feed->feed_write_index,
2071 c->stream->feed->feed_size);
2074 if (c->stream->max_time &&
2075 c->stream->max_time + c->start_time - cur_time < 0) {
2076 /* We have timed out */
2077 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2078 } else {
2079 redo:
2080 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2081 if (c->stream->feed && c->stream->feed->feed_opened) {
2082 /* if coming from feed, it means we reached the end of the
2083 ffm file, so must wait for more data */
2084 c->state = HTTPSTATE_WAIT_FEED;
2085 return 1; /* state changed */
2086 } else {
2087 if (c->stream->loop) {
2088 av_close_input_file(c->fmt_in);
2089 c->fmt_in = NULL;
2090 if (open_input_stream(c, "") < 0)
2091 goto no_loop;
2092 goto redo;
2093 } else {
2094 no_loop:
2095 /* must send trailer now because eof or error */
2096 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2099 } else {
2100 /* update first pts if needed */
2101 if (c->first_pts == AV_NOPTS_VALUE) {
2102 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2103 c->start_time = cur_time;
2105 /* send it to the appropriate stream */
2106 if (c->stream->feed) {
2107 /* if coming from a feed, select the right stream */
2108 if (c->switch_pending) {
2109 c->switch_pending = 0;
2110 for(i=0;i<c->stream->nb_streams;i++) {
2111 if (c->switch_feed_streams[i] == pkt.stream_index) {
2112 if (pkt.flags & PKT_FLAG_KEY) {
2113 do_switch_stream(c, i);
2116 if (c->switch_feed_streams[i] >= 0) {
2117 c->switch_pending = 1;
2121 for(i=0;i<c->stream->nb_streams;i++) {
2122 if (c->feed_streams[i] == pkt.stream_index) {
2123 pkt.stream_index = i;
2124 if (pkt.flags & PKT_FLAG_KEY) {
2125 c->got_key_frame |= 1 << i;
2127 /* See if we have all the key frames, then
2128 * we start to send. This logic is not quite
2129 * right, but it works for the case of a
2130 * single video stream with one or more
2131 * audio streams (for which every frame is
2132 * typically a key frame).
2134 if (!c->stream->send_on_key ||
2135 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2136 goto send_it;
2140 } else {
2141 AVCodecContext *codec;
2143 send_it:
2144 /* specific handling for RTP: we use several
2145 output stream (one for each RTP
2146 connection). XXX: need more abstract handling */
2147 if (c->is_packetized) {
2148 AVStream *st;
2149 /* compute send time and duration */
2150 st = c->fmt_in->streams[pkt.stream_index];
2151 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2152 if (st->start_time != AV_NOPTS_VALUE)
2153 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2154 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2155 #if 0
2156 printf("index=%d pts=%0.3f duration=%0.6f\n",
2157 pkt.stream_index,
2158 (double)c->cur_pts /
2159 AV_TIME_BASE,
2160 (double)c->cur_frame_duration /
2161 AV_TIME_BASE);
2162 #endif
2163 /* find RTP context */
2164 c->packet_stream_index = pkt.stream_index;
2165 ctx = c->rtp_ctx[c->packet_stream_index];
2166 if(!ctx) {
2167 av_free_packet(&pkt);
2168 break;
2170 codec = ctx->streams[0]->codec;
2171 /* only one stream per RTP connection */
2172 pkt.stream_index = 0;
2173 } else {
2174 ctx = &c->fmt_ctx;
2175 /* Fudge here */
2176 codec = ctx->streams[pkt.stream_index]->codec;
2179 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2180 if (c->is_packetized) {
2181 int max_packet_size;
2182 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2183 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2184 else
2185 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2186 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2187 } else {
2188 ret = url_open_dyn_buf(&ctx->pb);
2190 if (ret < 0) {
2191 /* XXX: potential leak */
2192 return -1;
2194 if (pkt.dts != AV_NOPTS_VALUE)
2195 pkt.dts = av_rescale_q(pkt.dts,
2196 c->fmt_in->streams[pkt.stream_index]->time_base,
2197 ctx->streams[pkt.stream_index]->time_base);
2198 if (pkt.pts != AV_NOPTS_VALUE)
2199 pkt.pts = av_rescale_q(pkt.pts,
2200 c->fmt_in->streams[pkt.stream_index]->time_base,
2201 ctx->streams[pkt.stream_index]->time_base);
2202 if (av_write_frame(ctx, &pkt)) {
2203 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2206 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2207 c->cur_frame_bytes = len;
2208 c->buffer_ptr = c->pb_buffer;
2209 c->buffer_end = c->pb_buffer + len;
2211 codec->frame_number++;
2212 if (len == 0)
2213 goto redo;
2215 av_free_packet(&pkt);
2219 break;
2220 default:
2221 case HTTPSTATE_SEND_DATA_TRAILER:
2222 /* last packet test ? */
2223 if (c->last_packet_sent || c->is_packetized)
2224 return -1;
2225 ctx = &c->fmt_ctx;
2226 /* prepare header */
2227 if (url_open_dyn_buf(&ctx->pb) < 0) {
2228 /* XXX: potential leak */
2229 return -1;
2231 av_write_trailer(ctx);
2232 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2233 c->buffer_ptr = c->pb_buffer;
2234 c->buffer_end = c->pb_buffer + len;
2236 c->last_packet_sent = 1;
2237 break;
2239 return 0;
2242 /* should convert the format at the same time */
2243 /* send data starting at c->buffer_ptr to the output connection
2244 (either UDP or TCP connection) */
2245 static int http_send_data(HTTPContext *c)
2247 int len, ret;
2249 for(;;) {
2250 if (c->buffer_ptr >= c->buffer_end) {
2251 ret = http_prepare_data(c);
2252 if (ret < 0)
2253 return -1;
2254 else if (ret != 0) {
2255 /* state change requested */
2256 break;
2258 } else {
2259 if (c->is_packetized) {
2260 /* RTP data output */
2261 len = c->buffer_end - c->buffer_ptr;
2262 if (len < 4) {
2263 /* fail safe - should never happen */
2264 fail1:
2265 c->buffer_ptr = c->buffer_end;
2266 return 0;
2268 len = (c->buffer_ptr[0] << 24) |
2269 (c->buffer_ptr[1] << 16) |
2270 (c->buffer_ptr[2] << 8) |
2271 (c->buffer_ptr[3]);
2272 if (len > (c->buffer_end - c->buffer_ptr))
2273 goto fail1;
2274 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2275 /* nothing to send yet: we can wait */
2276 return 0;
2279 c->data_count += len;
2280 update_datarate(&c->datarate, c->data_count);
2281 if (c->stream)
2282 c->stream->bytes_served += len;
2284 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2285 /* RTP packets are sent inside the RTSP TCP connection */
2286 ByteIOContext pb1, *pb = &pb1;
2287 int interleaved_index, size;
2288 uint8_t header[4];
2289 HTTPContext *rtsp_c;
2291 rtsp_c = c->rtsp_c;
2292 /* if no RTSP connection left, error */
2293 if (!rtsp_c)
2294 return -1;
2295 /* if already sending something, then wait. */
2296 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST) {
2297 break;
2299 if (url_open_dyn_buf(pb) < 0)
2300 goto fail1;
2301 interleaved_index = c->packet_stream_index * 2;
2302 /* RTCP packets are sent at odd indexes */
2303 if (c->buffer_ptr[1] == 200)
2304 interleaved_index++;
2305 /* write RTSP TCP header */
2306 header[0] = '$';
2307 header[1] = interleaved_index;
2308 header[2] = len >> 8;
2309 header[3] = len;
2310 put_buffer(pb, header, 4);
2311 /* write RTP packet data */
2312 c->buffer_ptr += 4;
2313 put_buffer(pb, c->buffer_ptr, len);
2314 size = url_close_dyn_buf(pb, &c->packet_buffer);
2315 /* prepare asynchronous TCP sending */
2316 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2317 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2318 c->buffer_ptr += len;
2320 /* send everything we can NOW */
2321 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2322 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2323 if (len > 0) {
2324 rtsp_c->packet_buffer_ptr += len;
2326 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2327 /* if we could not send all the data, we will
2328 send it later, so a new state is needed to
2329 "lock" the RTSP TCP connection */
2330 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2331 break;
2332 } else {
2333 /* all data has been sent */
2334 av_freep(&c->packet_buffer);
2336 } else {
2337 /* send RTP packet directly in UDP */
2338 c->buffer_ptr += 4;
2339 url_write(c->rtp_handles[c->packet_stream_index],
2340 c->buffer_ptr, len);
2341 c->buffer_ptr += len;
2342 /* here we continue as we can send several packets per 10 ms slot */
2344 } else {
2345 /* TCP data output */
2346 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2347 if (len < 0) {
2348 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2349 ff_neterrno() != FF_NETERROR(EINTR)) {
2350 /* error : close connection */
2351 return -1;
2352 } else {
2353 return 0;
2355 } else {
2356 c->buffer_ptr += len;
2358 c->data_count += len;
2359 update_datarate(&c->datarate, c->data_count);
2360 if (c->stream)
2361 c->stream->bytes_served += len;
2362 break;
2365 } /* for(;;) */
2366 return 0;
2369 static int http_start_receive_data(HTTPContext *c)
2371 int fd;
2373 if (c->stream->feed_opened)
2374 return -1;
2376 /* Don't permit writing to this one */
2377 if (c->stream->readonly)
2378 return -1;
2380 /* open feed */
2381 fd = open(c->stream->feed_filename, O_RDWR);
2382 if (fd < 0)
2383 return -1;
2384 c->feed_fd = fd;
2386 c->stream->feed_write_index = ffm_read_write_index(fd);
2387 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2388 lseek(fd, 0, SEEK_SET);
2390 /* init buffer input */
2391 c->buffer_ptr = c->buffer;
2392 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2393 c->stream->feed_opened = 1;
2394 return 0;
2397 static int http_receive_data(HTTPContext *c)
2399 HTTPContext *c1;
2401 if (c->buffer_end > c->buffer_ptr) {
2402 int len;
2404 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2405 if (len < 0) {
2406 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2407 ff_neterrno() != FF_NETERROR(EINTR)) {
2408 /* error : close connection */
2409 goto fail;
2411 } else if (len == 0) {
2412 /* end of connection : close it */
2413 goto fail;
2414 } else {
2415 c->buffer_ptr += len;
2416 c->data_count += len;
2417 update_datarate(&c->datarate, c->data_count);
2421 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2422 if (c->buffer[0] != 'f' ||
2423 c->buffer[1] != 'm') {
2424 http_log("Feed stream has become desynchronized -- disconnecting\n");
2425 goto fail;
2429 if (c->buffer_ptr >= c->buffer_end) {
2430 FFStream *feed = c->stream;
2431 /* a packet has been received : write it in the store, except
2432 if header */
2433 if (c->data_count > FFM_PACKET_SIZE) {
2435 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2436 /* XXX: use llseek or url_seek */
2437 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2438 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2440 feed->feed_write_index += FFM_PACKET_SIZE;
2441 /* update file size */
2442 if (feed->feed_write_index > c->stream->feed_size)
2443 feed->feed_size = feed->feed_write_index;
2445 /* handle wrap around if max file size reached */
2446 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2447 feed->feed_write_index = FFM_PACKET_SIZE;
2449 /* write index */
2450 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2452 /* wake up any waiting connections */
2453 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2454 if (c1->state == HTTPSTATE_WAIT_FEED &&
2455 c1->stream->feed == c->stream->feed) {
2456 c1->state = HTTPSTATE_SEND_DATA;
2459 } else {
2460 /* We have a header in our hands that contains useful data */
2461 AVFormatContext s;
2462 AVInputFormat *fmt_in;
2463 ByteIOContext *pb = &s.pb;
2464 int i;
2466 memset(&s, 0, sizeof(s));
2468 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2469 pb->buf_end = c->buffer_end; /* ?? */
2470 pb->is_streamed = 1;
2472 /* use feed output format name to find corresponding input format */
2473 fmt_in = av_find_input_format(feed->fmt->name);
2474 if (!fmt_in)
2475 goto fail;
2477 if (fmt_in->priv_data_size > 0) {
2478 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2479 if (!s.priv_data)
2480 goto fail;
2481 } else
2482 s.priv_data = NULL;
2484 if (fmt_in->read_header(&s, 0) < 0) {
2485 av_freep(&s.priv_data);
2486 goto fail;
2489 /* Now we have the actual streams */
2490 if (s.nb_streams != feed->nb_streams) {
2491 av_freep(&s.priv_data);
2492 goto fail;
2494 for (i = 0; i < s.nb_streams; i++) {
2495 memcpy(feed->streams[i]->codec,
2496 s.streams[i]->codec, sizeof(AVCodecContext));
2498 av_freep(&s.priv_data);
2500 c->buffer_ptr = c->buffer;
2503 return 0;
2504 fail:
2505 c->stream->feed_opened = 0;
2506 close(c->feed_fd);
2507 return -1;
2510 /********************************************************************/
2511 /* RTSP handling */
2513 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2515 const char *str;
2516 time_t ti;
2517 char *p;
2518 char buf2[32];
2520 switch(error_number) {
2521 case RTSP_STATUS_OK:
2522 str = "OK";
2523 break;
2524 case RTSP_STATUS_METHOD:
2525 str = "Method Not Allowed";
2526 break;
2527 case RTSP_STATUS_BANDWIDTH:
2528 str = "Not Enough Bandwidth";
2529 break;
2530 case RTSP_STATUS_SESSION:
2531 str = "Session Not Found";
2532 break;
2533 case RTSP_STATUS_STATE:
2534 str = "Method Not Valid in This State";
2535 break;
2536 case RTSP_STATUS_AGGREGATE:
2537 str = "Aggregate operation not allowed";
2538 break;
2539 case RTSP_STATUS_ONLY_AGGREGATE:
2540 str = "Only aggregate operation allowed";
2541 break;
2542 case RTSP_STATUS_TRANSPORT:
2543 str = "Unsupported transport";
2544 break;
2545 case RTSP_STATUS_INTERNAL:
2546 str = "Internal Server Error";
2547 break;
2548 case RTSP_STATUS_SERVICE:
2549 str = "Service Unavailable";
2550 break;
2551 case RTSP_STATUS_VERSION:
2552 str = "RTSP Version not supported";
2553 break;
2554 default:
2555 str = "Unknown Error";
2556 break;
2559 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2560 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2562 /* output GMT time */
2563 ti = time(NULL);
2564 p = ctime(&ti);
2565 strcpy(buf2, p);
2566 p = buf2 + strlen(p) - 1;
2567 if (*p == '\n')
2568 *p = '\0';
2569 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2572 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2574 rtsp_reply_header(c, error_number);
2575 url_fprintf(c->pb, "\r\n");
2578 static int rtsp_parse_request(HTTPContext *c)
2580 const char *p, *p1, *p2;
2581 char cmd[32];
2582 char url[1024];
2583 char protocol[32];
2584 char line[1024];
2585 ByteIOContext pb1;
2586 int len;
2587 RTSPHeader header1, *header = &header1;
2589 c->buffer_ptr[0] = '\0';
2590 p = c->buffer;
2592 get_word(cmd, sizeof(cmd), &p);
2593 get_word(url, sizeof(url), &p);
2594 get_word(protocol, sizeof(protocol), &p);
2596 av_strlcpy(c->method, cmd, sizeof(c->method));
2597 av_strlcpy(c->url, url, sizeof(c->url));
2598 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2600 c->pb = &pb1;
2601 if (url_open_dyn_buf(c->pb) < 0) {
2602 /* XXX: cannot do more */
2603 c->pb = NULL; /* safety */
2604 return -1;
2607 /* check version name */
2608 if (strcmp(protocol, "RTSP/1.0") != 0) {
2609 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2610 goto the_end;
2613 /* parse each header line */
2614 memset(header, 0, sizeof(RTSPHeader));
2615 /* skip to next line */
2616 while (*p != '\n' && *p != '\0')
2617 p++;
2618 if (*p == '\n')
2619 p++;
2620 while (*p != '\0') {
2621 p1 = strchr(p, '\n');
2622 if (!p1)
2623 break;
2624 p2 = p1;
2625 if (p2 > p && p2[-1] == '\r')
2626 p2--;
2627 /* skip empty line */
2628 if (p2 == p)
2629 break;
2630 len = p2 - p;
2631 if (len > sizeof(line) - 1)
2632 len = sizeof(line) - 1;
2633 memcpy(line, p, len);
2634 line[len] = '\0';
2635 rtsp_parse_line(header, line);
2636 p = p1 + 1;
2639 /* handle sequence number */
2640 c->seq = header->seq;
2642 if (!strcmp(cmd, "DESCRIBE")) {
2643 rtsp_cmd_describe(c, url);
2644 } else if (!strcmp(cmd, "OPTIONS")) {
2645 rtsp_cmd_options(c, url);
2646 } else if (!strcmp(cmd, "SETUP")) {
2647 rtsp_cmd_setup(c, url, header);
2648 } else if (!strcmp(cmd, "PLAY")) {
2649 rtsp_cmd_play(c, url, header);
2650 } else if (!strcmp(cmd, "PAUSE")) {
2651 rtsp_cmd_pause(c, url, header);
2652 } else if (!strcmp(cmd, "TEARDOWN")) {
2653 rtsp_cmd_teardown(c, url, header);
2654 } else {
2655 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2657 the_end:
2658 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2659 c->pb = NULL; /* safety */
2660 if (len < 0) {
2661 /* XXX: cannot do more */
2662 return -1;
2664 c->buffer_ptr = c->pb_buffer;
2665 c->buffer_end = c->pb_buffer + len;
2666 c->state = RTSPSTATE_SEND_REPLY;
2667 return 0;
2670 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2671 AVFormatContext */
2672 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2673 struct in_addr my_ip)
2675 ByteIOContext pb1, *pb = &pb1;
2676 int i, payload_type, port, private_payload_type, j;
2677 const char *ipstr, *title, *mediatype;
2678 AVStream *st;
2680 if (url_open_dyn_buf(pb) < 0)
2681 return -1;
2683 /* general media info */
2685 url_fprintf(pb, "v=0\n");
2686 ipstr = inet_ntoa(my_ip);
2687 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2688 title = stream->title;
2689 if (title[0] == '\0')
2690 title = "No Title";
2691 url_fprintf(pb, "s=%s\n", title);
2692 if (stream->comment[0] != '\0')
2693 url_fprintf(pb, "i=%s\n", stream->comment);
2694 if (stream->is_multicast) {
2695 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2697 /* for each stream, we output the necessary info */
2698 private_payload_type = RTP_PT_PRIVATE;
2699 for(i = 0; i < stream->nb_streams; i++) {
2700 st = stream->streams[i];
2701 if (st->codec->codec_id == CODEC_ID_MPEG2TS) {
2702 mediatype = "video";
2703 } else {
2704 switch(st->codec->codec_type) {
2705 case CODEC_TYPE_AUDIO:
2706 mediatype = "audio";
2707 break;
2708 case CODEC_TYPE_VIDEO:
2709 mediatype = "video";
2710 break;
2711 default:
2712 mediatype = "application";
2713 break;
2716 /* NOTE: the port indication is not correct in case of
2717 unicast. It is not an issue because RTSP gives it */
2718 payload_type = rtp_get_payload_type(st->codec);
2719 if (payload_type < 0)
2720 payload_type = private_payload_type++;
2721 if (stream->is_multicast) {
2722 port = stream->multicast_port + 2 * i;
2723 } else {
2724 port = 0;
2726 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2727 mediatype, port, payload_type);
2728 if (payload_type >= RTP_PT_PRIVATE) {
2729 /* for private payload type, we need to give more info */
2730 switch(st->codec->codec_id) {
2731 case CODEC_ID_MPEG4:
2733 uint8_t *data;
2734 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2735 payload_type, 90000);
2736 /* we must also add the mpeg4 header */
2737 data = st->codec->extradata;
2738 if (data) {
2739 url_fprintf(pb, "a=fmtp:%d config=", payload_type);
2740 for(j=0;j<st->codec->extradata_size;j++) {
2741 url_fprintf(pb, "%02x", data[j]);
2743 url_fprintf(pb, "\n");
2746 break;
2747 default:
2748 /* XXX: add other codecs ? */
2749 goto fail;
2752 url_fprintf(pb, "a=control:streamid=%d\n", i);
2754 return url_close_dyn_buf(pb, pbuffer);
2755 fail:
2756 url_close_dyn_buf(pb, pbuffer);
2757 av_free(*pbuffer);
2758 return -1;
2761 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2763 // rtsp_reply_header(c, RTSP_STATUS_OK);
2764 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2765 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2766 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2767 url_fprintf(c->pb, "\r\n");
2770 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2772 FFStream *stream;
2773 char path1[1024];
2774 const char *path;
2775 uint8_t *content;
2776 int content_length, len;
2777 struct sockaddr_in my_addr;
2779 /* find which url is asked */
2780 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2781 path = path1;
2782 if (*path == '/')
2783 path++;
2785 for(stream = first_stream; stream != NULL; stream = stream->next) {
2786 if (!stream->is_feed && stream->fmt == &rtp_muxer &&
2787 !strcmp(path, stream->filename)) {
2788 goto found;
2791 /* no stream found */
2792 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2793 return;
2795 found:
2796 /* prepare the media description in sdp format */
2798 /* get the host IP */
2799 len = sizeof(my_addr);
2800 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2801 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2802 if (content_length < 0) {
2803 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2804 return;
2806 rtsp_reply_header(c, RTSP_STATUS_OK);
2807 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2808 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2809 url_fprintf(c->pb, "\r\n");
2810 put_buffer(c->pb, content, content_length);
2813 static HTTPContext *find_rtp_session(const char *session_id)
2815 HTTPContext *c;
2817 if (session_id[0] == '\0')
2818 return NULL;
2820 for(c = first_http_ctx; c != NULL; c = c->next) {
2821 if (!strcmp(c->session_id, session_id))
2822 return c;
2824 return NULL;
2827 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2829 RTSPTransportField *th;
2830 int i;
2832 for(i=0;i<h->nb_transports;i++) {
2833 th = &h->transports[i];
2834 if (th->protocol == protocol)
2835 return th;
2837 return NULL;
2840 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2841 RTSPHeader *h)
2843 FFStream *stream;
2844 int stream_index, port;
2845 char buf[1024];
2846 char path1[1024];
2847 const char *path;
2848 HTTPContext *rtp_c;
2849 RTSPTransportField *th;
2850 struct sockaddr_in dest_addr;
2851 RTSPActionServerSetup setup;
2853 /* find which url is asked */
2854 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2855 path = path1;
2856 if (*path == '/')
2857 path++;
2859 /* now check each stream */
2860 for(stream = first_stream; stream != NULL; stream = stream->next) {
2861 if (!stream->is_feed && stream->fmt == &rtp_muxer) {
2862 /* accept aggregate filenames only if single stream */
2863 if (!strcmp(path, stream->filename)) {
2864 if (stream->nb_streams != 1) {
2865 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2866 return;
2868 stream_index = 0;
2869 goto found;
2872 for(stream_index = 0; stream_index < stream->nb_streams;
2873 stream_index++) {
2874 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2875 stream->filename, stream_index);
2876 if (!strcmp(path, buf))
2877 goto found;
2881 /* no stream found */
2882 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2883 return;
2884 found:
2886 /* generate session id if needed */
2887 if (h->session_id[0] == '\0') {
2888 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2889 av_random(&random_state), av_random(&random_state));
2892 /* find rtp session, and create it if none found */
2893 rtp_c = find_rtp_session(h->session_id);
2894 if (!rtp_c) {
2895 /* always prefer UDP */
2896 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2897 if (!th) {
2898 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2899 if (!th) {
2900 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2901 return;
2905 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2906 th->protocol);
2907 if (!rtp_c) {
2908 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2909 return;
2912 /* open input stream */
2913 if (open_input_stream(rtp_c, "") < 0) {
2914 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2915 return;
2919 /* test if stream is OK (test needed because several SETUP needs
2920 to be done for a given file) */
2921 if (rtp_c->stream != stream) {
2922 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2923 return;
2926 /* test if stream is already set up */
2927 if (rtp_c->rtp_ctx[stream_index]) {
2928 rtsp_reply_error(c, RTSP_STATUS_STATE);
2929 return;
2932 /* check transport */
2933 th = find_transport(h, rtp_c->rtp_protocol);
2934 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2935 th->client_port_min <= 0)) {
2936 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2937 return;
2940 /* setup default options */
2941 setup.transport_option[0] = '\0';
2942 dest_addr = rtp_c->from_addr;
2943 dest_addr.sin_port = htons(th->client_port_min);
2945 /* setup stream */
2946 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2947 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2948 return;
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);
2956 switch(rtp_c->rtp_protocol) {
2957 case RTSP_PROTOCOL_RTP_UDP:
2958 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2959 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2960 "client_port=%d-%d;server_port=%d-%d",
2961 th->client_port_min, th->client_port_min + 1,
2962 port, port + 1);
2963 break;
2964 case RTSP_PROTOCOL_RTP_TCP:
2965 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2966 stream_index * 2, stream_index * 2 + 1);
2967 break;
2968 default:
2969 break;
2971 if (setup.transport_option[0] != '\0') {
2972 url_fprintf(c->pb, ";%s", setup.transport_option);
2974 url_fprintf(c->pb, "\r\n");
2977 url_fprintf(c->pb, "\r\n");
2981 /* find an rtp connection by using the session ID. Check consistency
2982 with filename */
2983 static HTTPContext *find_rtp_session_with_url(const char *url,
2984 const char *session_id)
2986 HTTPContext *rtp_c;
2987 char path1[1024];
2988 const char *path;
2989 char buf[1024];
2990 int s;
2992 rtp_c = find_rtp_session(session_id);
2993 if (!rtp_c)
2994 return NULL;
2996 /* find which url is asked */
2997 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2998 path = path1;
2999 if (*path == '/')
3000 path++;
3001 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3002 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3003 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3004 rtp_c->stream->filename, s);
3005 if(!strncmp(path, buf, sizeof(buf))) {
3006 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3007 return rtp_c;
3010 return NULL;
3013 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
3015 HTTPContext *rtp_c;
3017 rtp_c = find_rtp_session_with_url(url, h->session_id);
3018 if (!rtp_c) {
3019 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3020 return;
3023 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3024 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3025 rtp_c->state != HTTPSTATE_READY) {
3026 rtsp_reply_error(c, RTSP_STATUS_STATE);
3027 return;
3030 #if 0
3031 /* XXX: seek in stream */
3032 if (h->range_start != AV_NOPTS_VALUE) {
3033 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3034 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3036 #endif
3038 rtp_c->state = HTTPSTATE_SEND_DATA;
3040 /* now everything is OK, so we can send the connection parameters */
3041 rtsp_reply_header(c, RTSP_STATUS_OK);
3042 /* session ID */
3043 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3044 url_fprintf(c->pb, "\r\n");
3047 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3049 HTTPContext *rtp_c;
3051 rtp_c = find_rtp_session_with_url(url, h->session_id);
3052 if (!rtp_c) {
3053 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3054 return;
3057 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3058 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3059 rtsp_reply_error(c, RTSP_STATUS_STATE);
3060 return;
3063 rtp_c->state = HTTPSTATE_READY;
3064 rtp_c->first_pts = AV_NOPTS_VALUE;
3065 /* now everything is OK, so we can send the connection parameters */
3066 rtsp_reply_header(c, RTSP_STATUS_OK);
3067 /* session ID */
3068 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3069 url_fprintf(c->pb, "\r\n");
3072 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3074 HTTPContext *rtp_c;
3075 char session_id[32];
3077 rtp_c = find_rtp_session_with_url(url, h->session_id);
3078 if (!rtp_c) {
3079 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3080 return;
3083 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3085 /* abort the session */
3086 close_connection(rtp_c);
3088 /* now everything is OK, so we can send the connection parameters */
3089 rtsp_reply_header(c, RTSP_STATUS_OK);
3090 /* session ID */
3091 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3092 url_fprintf(c->pb, "\r\n");
3096 /********************************************************************/
3097 /* RTP handling */
3099 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3100 FFStream *stream, const char *session_id,
3101 enum RTSPProtocol rtp_protocol)
3103 HTTPContext *c = NULL;
3104 const char *proto_str;
3106 /* XXX: should output a warning page when coming
3107 close to the connection limit */
3108 if (nb_connections >= nb_max_connections)
3109 goto fail;
3111 /* add a new connection */
3112 c = av_mallocz(sizeof(HTTPContext));
3113 if (!c)
3114 goto fail;
3116 c->fd = -1;
3117 c->poll_entry = NULL;
3118 c->from_addr = *from_addr;
3119 c->buffer_size = IOBUFFER_INIT_SIZE;
3120 c->buffer = av_malloc(c->buffer_size);
3121 if (!c->buffer)
3122 goto fail;
3123 nb_connections++;
3124 c->stream = stream;
3125 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3126 c->state = HTTPSTATE_READY;
3127 c->is_packetized = 1;
3128 c->rtp_protocol = rtp_protocol;
3130 /* protocol is shown in statistics */
3131 switch(c->rtp_protocol) {
3132 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3133 proto_str = "MCAST";
3134 break;
3135 case RTSP_PROTOCOL_RTP_UDP:
3136 proto_str = "UDP";
3137 break;
3138 case RTSP_PROTOCOL_RTP_TCP:
3139 proto_str = "TCP";
3140 break;
3141 default:
3142 proto_str = "???";
3143 break;
3145 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3146 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3148 current_bandwidth += stream->bandwidth;
3150 c->next = first_http_ctx;
3151 first_http_ctx = c;
3152 return c;
3154 fail:
3155 if (c) {
3156 av_free(c->buffer);
3157 av_free(c);
3159 return NULL;
3162 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3163 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3164 used. */
3165 static int rtp_new_av_stream(HTTPContext *c,
3166 int stream_index, struct sockaddr_in *dest_addr,
3167 HTTPContext *rtsp_c)
3169 AVFormatContext *ctx;
3170 AVStream *st;
3171 char *ipaddr;
3172 URLContext *h;
3173 uint8_t *dummy_buf;
3174 char buf2[32];
3175 int max_packet_size;
3177 /* now we can open the relevant output stream */
3178 ctx = av_alloc_format_context();
3179 if (!ctx)
3180 return -1;
3181 ctx->oformat = &rtp_muxer;
3183 st = av_mallocz(sizeof(AVStream));
3184 if (!st)
3185 goto fail;
3186 st->codec= avcodec_alloc_context();
3187 ctx->nb_streams = 1;
3188 ctx->streams[0] = st;
3190 if (!c->stream->feed ||
3191 c->stream->feed == c->stream) {
3192 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3193 } else {
3194 memcpy(st,
3195 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3196 sizeof(AVStream));
3198 st->priv_data = NULL;
3200 /* build destination RTP address */
3201 ipaddr = inet_ntoa(dest_addr->sin_addr);
3203 switch(c->rtp_protocol) {
3204 case RTSP_PROTOCOL_RTP_UDP:
3205 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3206 /* RTP/UDP case */
3208 /* XXX: also pass as parameter to function ? */
3209 if (c->stream->is_multicast) {
3210 int ttl;
3211 ttl = c->stream->multicast_ttl;
3212 if (!ttl)
3213 ttl = 16;
3214 snprintf(ctx->filename, sizeof(ctx->filename),
3215 "rtp://%s:%d?multicast=1&ttl=%d",
3216 ipaddr, ntohs(dest_addr->sin_port), ttl);
3217 } else {
3218 snprintf(ctx->filename, sizeof(ctx->filename),
3219 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3222 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3223 goto fail;
3224 c->rtp_handles[stream_index] = h;
3225 max_packet_size = url_get_max_packet_size(h);
3226 break;
3227 case RTSP_PROTOCOL_RTP_TCP:
3228 /* RTP/TCP case */
3229 c->rtsp_c = rtsp_c;
3230 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3231 break;
3232 default:
3233 goto fail;
3236 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3237 ipaddr, ntohs(dest_addr->sin_port),
3238 ctime1(buf2),
3239 c->stream->filename, stream_index, c->protocol);
3241 /* normally, no packets should be output here, but the packet size may be checked */
3242 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3243 /* XXX: close stream */
3244 goto fail;
3246 av_set_parameters(ctx, NULL);
3247 if (av_write_header(ctx) < 0) {
3248 fail:
3249 if (h)
3250 url_close(h);
3251 av_free(ctx);
3252 return -1;
3254 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3255 av_free(dummy_buf);
3257 c->rtp_ctx[stream_index] = ctx;
3258 return 0;
3261 /********************************************************************/
3262 /* ffserver initialization */
3264 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3266 AVStream *fst;
3268 fst = av_mallocz(sizeof(AVStream));
3269 if (!fst)
3270 return NULL;
3271 fst->codec= avcodec_alloc_context();
3272 fst->priv_data = av_mallocz(sizeof(FeedData));
3273 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3274 fst->codec->coded_frame = &dummy_frame;
3275 fst->index = stream->nb_streams;
3276 av_set_pts_info(fst, 33, 1, 90000);
3277 stream->streams[stream->nb_streams++] = fst;
3278 return fst;
3281 /* return the stream number in the feed */
3282 static int add_av_stream(FFStream *feed, AVStream *st)
3284 AVStream *fst;
3285 AVCodecContext *av, *av1;
3286 int i;
3288 av = st->codec;
3289 for(i=0;i<feed->nb_streams;i++) {
3290 st = feed->streams[i];
3291 av1 = st->codec;
3292 if (av1->codec_id == av->codec_id &&
3293 av1->codec_type == av->codec_type &&
3294 av1->bit_rate == av->bit_rate) {
3296 switch(av->codec_type) {
3297 case CODEC_TYPE_AUDIO:
3298 if (av1->channels == av->channels &&
3299 av1->sample_rate == av->sample_rate)
3300 goto found;
3301 break;
3302 case CODEC_TYPE_VIDEO:
3303 if (av1->width == av->width &&
3304 av1->height == av->height &&
3305 av1->time_base.den == av->time_base.den &&
3306 av1->time_base.num == av->time_base.num &&
3307 av1->gop_size == av->gop_size)
3308 goto found;
3309 break;
3310 default:
3311 abort();
3316 fst = add_av_stream1(feed, av);
3317 if (!fst)
3318 return -1;
3319 return feed->nb_streams - 1;
3320 found:
3321 return i;
3324 static void remove_stream(FFStream *stream)
3326 FFStream **ps;
3327 ps = &first_stream;
3328 while (*ps != NULL) {
3329 if (*ps == stream) {
3330 *ps = (*ps)->next;
3331 } else {
3332 ps = &(*ps)->next;
3337 /* specific mpeg4 handling : we extract the raw parameters */
3338 static void extract_mpeg4_header(AVFormatContext *infile)
3340 int mpeg4_count, i, size;
3341 AVPacket pkt;
3342 AVStream *st;
3343 const uint8_t *p;
3345 mpeg4_count = 0;
3346 for(i=0;i<infile->nb_streams;i++) {
3347 st = infile->streams[i];
3348 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3349 st->codec->extradata_size == 0) {
3350 mpeg4_count++;
3353 if (!mpeg4_count)
3354 return;
3356 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3357 while (mpeg4_count > 0) {
3358 if (av_read_packet(infile, &pkt) < 0)
3359 break;
3360 st = infile->streams[pkt.stream_index];
3361 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3362 st->codec->extradata_size == 0) {
3363 av_freep(&st->codec->extradata);
3364 /* fill extradata with the header */
3365 /* XXX: we make hard suppositions here ! */
3366 p = pkt.data;
3367 while (p < pkt.data + pkt.size - 4) {
3368 /* stop when vop header is found */
3369 if (p[0] == 0x00 && p[1] == 0x00 &&
3370 p[2] == 0x01 && p[3] == 0xb6) {
3371 size = p - pkt.data;
3372 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3373 st->codec->extradata = av_malloc(size);
3374 st->codec->extradata_size = size;
3375 memcpy(st->codec->extradata, pkt.data, size);
3376 break;
3378 p++;
3380 mpeg4_count--;
3382 av_free_packet(&pkt);
3386 /* compute the needed AVStream for each file */
3387 static void build_file_streams(void)
3389 FFStream *stream, *stream_next;
3390 AVFormatContext *infile;
3391 int i;
3393 /* gather all streams */
3394 for(stream = first_stream; stream != NULL; stream = stream_next) {
3395 stream_next = stream->next;
3396 if (stream->stream_type == STREAM_TYPE_LIVE &&
3397 !stream->feed) {
3398 /* the stream comes from a file */
3399 /* try to open the file */
3400 /* open stream */
3401 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3402 if (stream->fmt == &rtp_muxer) {
3403 /* specific case : if transport stream output to RTP,
3404 we use a raw transport stream reader */
3405 stream->ap_in->mpeg2ts_raw = 1;
3406 stream->ap_in->mpeg2ts_compute_pcr = 1;
3409 if (av_open_input_file(&infile, stream->feed_filename,
3410 stream->ifmt, 0, stream->ap_in) < 0) {
3411 http_log("%s not found", stream->feed_filename);
3412 /* remove stream (no need to spend more time on it) */
3413 fail:
3414 remove_stream(stream);
3415 } else {
3416 /* find all the AVStreams inside and reference them in
3417 'stream' */
3418 if (av_find_stream_info(infile) < 0) {
3419 http_log("Could not find codec parameters from '%s'",
3420 stream->feed_filename);
3421 av_close_input_file(infile);
3422 goto fail;
3424 extract_mpeg4_header(infile);
3426 for(i=0;i<infile->nb_streams;i++) {
3427 add_av_stream1(stream, infile->streams[i]->codec);
3429 av_close_input_file(infile);
3435 /* compute the needed AVStream for each feed */
3436 static void build_feed_streams(void)
3438 FFStream *stream, *feed;
3439 int i;
3441 /* gather all streams */
3442 for(stream = first_stream; stream != NULL; stream = stream->next) {
3443 feed = stream->feed;
3444 if (feed) {
3445 if (!stream->is_feed) {
3446 /* we handle a stream coming from a feed */
3447 for(i=0;i<stream->nb_streams;i++) {
3448 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3454 /* gather all streams */
3455 for(stream = first_stream; stream != NULL; stream = stream->next) {
3456 feed = stream->feed;
3457 if (feed) {
3458 if (stream->is_feed) {
3459 for(i=0;i<stream->nb_streams;i++) {
3460 stream->feed_streams[i] = i;
3466 /* create feed files if needed */
3467 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3468 int fd;
3470 if (url_exist(feed->feed_filename)) {
3471 /* See if it matches */
3472 AVFormatContext *s;
3473 int matches = 0;
3475 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3476 /* Now see if it matches */
3477 if (s->nb_streams == feed->nb_streams) {
3478 matches = 1;
3479 for(i=0;i<s->nb_streams;i++) {
3480 AVStream *sf, *ss;
3481 sf = feed->streams[i];
3482 ss = s->streams[i];
3484 if (sf->index != ss->index ||
3485 sf->id != ss->id) {
3486 printf("Index & Id do not match for stream %d (%s)\n",
3487 i, feed->feed_filename);
3488 matches = 0;
3489 } else {
3490 AVCodecContext *ccf, *ccs;
3492 ccf = sf->codec;
3493 ccs = ss->codec;
3494 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3496 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3497 printf("Codecs do not match for stream %d\n", i);
3498 matches = 0;
3499 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3500 printf("Codec bitrates do not match for stream %d\n", i);
3501 matches = 0;
3502 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3503 if (CHECK_CODEC(time_base.den) ||
3504 CHECK_CODEC(time_base.num) ||
3505 CHECK_CODEC(width) ||
3506 CHECK_CODEC(height)) {
3507 printf("Codec width, height and framerate do not match for stream %d\n", i);
3508 matches = 0;
3510 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3511 if (CHECK_CODEC(sample_rate) ||
3512 CHECK_CODEC(channels) ||
3513 CHECK_CODEC(frame_size)) {
3514 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3515 matches = 0;
3517 } else {
3518 printf("Unknown codec type\n");
3519 matches = 0;
3522 if (!matches) {
3523 break;
3526 } else {
3527 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3528 feed->feed_filename, s->nb_streams, feed->nb_streams);
3531 av_close_input_file(s);
3532 } else {
3533 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3534 feed->feed_filename);
3536 if (!matches) {
3537 if (feed->readonly) {
3538 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3539 feed->feed_filename);
3540 exit(1);
3542 unlink(feed->feed_filename);
3545 if (!url_exist(feed->feed_filename)) {
3546 AVFormatContext s1, *s = &s1;
3548 if (feed->readonly) {
3549 printf("Unable to create feed file '%s' as it is marked readonly\n",
3550 feed->feed_filename);
3551 exit(1);
3554 /* only write the header of the ffm file */
3555 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3556 fprintf(stderr, "Could not open output feed file '%s'\n",
3557 feed->feed_filename);
3558 exit(1);
3560 s->oformat = feed->fmt;
3561 s->nb_streams = feed->nb_streams;
3562 for(i=0;i<s->nb_streams;i++) {
3563 AVStream *st;
3564 st = feed->streams[i];
3565 s->streams[i] = st;
3567 av_set_parameters(s, NULL);
3568 if (av_write_header(s) < 0) {
3569 fprintf(stderr, "Container doesn't supports the required parameters\n");
3570 exit(1);
3572 /* XXX: need better api */
3573 av_freep(&s->priv_data);
3574 url_fclose(&s->pb);
3576 /* get feed size and write index */
3577 fd = open(feed->feed_filename, O_RDONLY);
3578 if (fd < 0) {
3579 fprintf(stderr, "Could not open output feed file '%s'\n",
3580 feed->feed_filename);
3581 exit(1);
3584 feed->feed_write_index = ffm_read_write_index(fd);
3585 feed->feed_size = lseek(fd, 0, SEEK_END);
3586 /* ensure that we do not wrap before the end of file */
3587 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3588 feed->feed_max_size = feed->feed_size;
3590 close(fd);
3594 /* compute the bandwidth used by each stream */
3595 static void compute_bandwidth(void)
3597 int bandwidth, i;
3598 FFStream *stream;
3600 for(stream = first_stream; stream != NULL; stream = stream->next) {
3601 bandwidth = 0;
3602 for(i=0;i<stream->nb_streams;i++) {
3603 AVStream *st = stream->streams[i];
3604 switch(st->codec->codec_type) {
3605 case CODEC_TYPE_AUDIO:
3606 case CODEC_TYPE_VIDEO:
3607 bandwidth += st->codec->bit_rate;
3608 break;
3609 default:
3610 break;
3613 stream->bandwidth = (bandwidth + 999) / 1000;
3617 static void get_arg(char *buf, int buf_size, const char **pp)
3619 const char *p;
3620 char *q;
3621 int quote;
3623 p = *pp;
3624 while (isspace(*p)) p++;
3625 q = buf;
3626 quote = 0;
3627 if (*p == '\"' || *p == '\'')
3628 quote = *p++;
3629 for(;;) {
3630 if (quote) {
3631 if (*p == quote)
3632 break;
3633 } else {
3634 if (isspace(*p))
3635 break;
3637 if (*p == '\0')
3638 break;
3639 if ((q - buf) < buf_size - 1)
3640 *q++ = *p;
3641 p++;
3643 *q = '\0';
3644 if (quote && *p == quote)
3645 p++;
3646 *pp = p;
3649 /* add a codec and set the default parameters */
3650 static void add_codec(FFStream *stream, AVCodecContext *av)
3652 AVStream *st;
3654 /* compute default parameters */
3655 switch(av->codec_type) {
3656 case CODEC_TYPE_AUDIO:
3657 if (av->bit_rate == 0)
3658 av->bit_rate = 64000;
3659 if (av->sample_rate == 0)
3660 av->sample_rate = 22050;
3661 if (av->channels == 0)
3662 av->channels = 1;
3663 break;
3664 case CODEC_TYPE_VIDEO:
3665 if (av->bit_rate == 0)
3666 av->bit_rate = 64000;
3667 if (av->time_base.num == 0){
3668 av->time_base.den = 5;
3669 av->time_base.num = 1;
3671 if (av->width == 0 || av->height == 0) {
3672 av->width = 160;
3673 av->height = 128;
3675 /* Bitrate tolerance is less for streaming */
3676 if (av->bit_rate_tolerance == 0)
3677 av->bit_rate_tolerance = av->bit_rate / 4;
3678 if (av->qmin == 0)
3679 av->qmin = 3;
3680 if (av->qmax == 0)
3681 av->qmax = 31;
3682 if (av->max_qdiff == 0)
3683 av->max_qdiff = 3;
3684 av->qcompress = 0.5;
3685 av->qblur = 0.5;
3687 if (!av->nsse_weight)
3688 av->nsse_weight = 8;
3690 av->frame_skip_cmp = FF_CMP_DCTMAX;
3691 av->me_method = ME_EPZS;
3692 av->rc_buffer_aggressivity = 1.0;
3694 if (!av->rc_eq)
3695 av->rc_eq = "tex^qComp";
3696 if (!av->i_quant_factor)
3697 av->i_quant_factor = -0.8;
3698 if (!av->b_quant_factor)
3699 av->b_quant_factor = 1.25;
3700 if (!av->b_quant_offset)
3701 av->b_quant_offset = 1.25;
3702 if (!av->rc_max_rate)
3703 av->rc_max_rate = av->bit_rate * 2;
3705 if (av->rc_max_rate && !av->rc_buffer_size) {
3706 av->rc_buffer_size = av->rc_max_rate;
3710 break;
3711 default:
3712 abort();
3715 st = av_mallocz(sizeof(AVStream));
3716 if (!st)
3717 return;
3718 st->codec = avcodec_alloc_context();
3719 stream->streams[stream->nb_streams++] = st;
3720 memcpy(st->codec, av, sizeof(AVCodecContext));
3723 static int opt_audio_codec(const char *arg)
3725 AVCodec *p;
3727 p = first_avcodec;
3728 while (p) {
3729 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3730 break;
3731 p = p->next;
3733 if (p == NULL) {
3734 return CODEC_ID_NONE;
3737 return p->id;
3740 static int opt_video_codec(const char *arg)
3742 AVCodec *p;
3744 p = first_avcodec;
3745 while (p) {
3746 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3747 break;
3748 p = p->next;
3750 if (p == NULL) {
3751 return CODEC_ID_NONE;
3754 return p->id;
3757 /* simplistic plugin support */
3759 #ifdef HAVE_DLOPEN
3760 static void load_module(const char *filename)
3762 void *dll;
3763 void (*init_func)(void);
3764 dll = dlopen(filename, RTLD_NOW);
3765 if (!dll) {
3766 fprintf(stderr, "Could not load module '%s' - %s\n",
3767 filename, dlerror());
3768 return;
3771 init_func = dlsym(dll, "ffserver_module_init");
3772 if (!init_func) {
3773 fprintf(stderr,
3774 "%s: init function 'ffserver_module_init()' not found\n",
3775 filename);
3776 dlclose(dll);
3779 init_func();
3781 #endif
3783 static int parse_ffconfig(const char *filename)
3785 FILE *f;
3786 char line[1024];
3787 char cmd[64];
3788 char arg[1024];
3789 const char *p;
3790 int val, errors, line_num;
3791 FFStream **last_stream, *stream, *redirect;
3792 FFStream **last_feed, *feed;
3793 AVCodecContext audio_enc, video_enc;
3794 int audio_id, video_id;
3796 f = fopen(filename, "r");
3797 if (!f) {
3798 perror(filename);
3799 return -1;
3802 errors = 0;
3803 line_num = 0;
3804 first_stream = NULL;
3805 last_stream = &first_stream;
3806 first_feed = NULL;
3807 last_feed = &first_feed;
3808 stream = NULL;
3809 feed = NULL;
3810 redirect = NULL;
3811 audio_id = CODEC_ID_NONE;
3812 video_id = CODEC_ID_NONE;
3813 for(;;) {
3814 if (fgets(line, sizeof(line), f) == NULL)
3815 break;
3816 line_num++;
3817 p = line;
3818 while (isspace(*p))
3819 p++;
3820 if (*p == '\0' || *p == '#')
3821 continue;
3823 get_arg(cmd, sizeof(cmd), &p);
3825 if (!strcasecmp(cmd, "Port")) {
3826 get_arg(arg, sizeof(arg), &p);
3827 val = atoi(arg);
3828 if (val < 1 || val > 65536) {
3829 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3830 filename, line_num, arg);
3831 errors++;
3833 my_http_addr.sin_port = htons(val);
3834 } else if (!strcasecmp(cmd, "BindAddress")) {
3835 get_arg(arg, sizeof(arg), &p);
3836 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3837 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3838 filename, line_num, arg);
3839 errors++;
3841 } else if (!strcasecmp(cmd, "NoDaemon")) {
3842 ffserver_daemon = 0;
3843 } else if (!strcasecmp(cmd, "RTSPPort")) {
3844 get_arg(arg, sizeof(arg), &p);
3845 val = atoi(arg);
3846 if (val < 1 || val > 65536) {
3847 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3848 filename, line_num, arg);
3849 errors++;
3851 my_rtsp_addr.sin_port = htons(atoi(arg));
3852 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3853 get_arg(arg, sizeof(arg), &p);
3854 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3855 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3856 filename, line_num, arg);
3857 errors++;
3859 } else if (!strcasecmp(cmd, "MaxClients")) {
3860 get_arg(arg, sizeof(arg), &p);
3861 val = atoi(arg);
3862 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3863 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3864 filename, line_num, arg);
3865 errors++;
3866 } else {
3867 nb_max_connections = val;
3869 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3870 get_arg(arg, sizeof(arg), &p);
3871 val = atoi(arg);
3872 if (val < 10 || val > 100000) {
3873 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3874 filename, line_num, arg);
3875 errors++;
3876 } else {
3877 max_bandwidth = val;
3879 } else if (!strcasecmp(cmd, "CustomLog")) {
3880 get_arg(logfilename, sizeof(logfilename), &p);
3881 } else if (!strcasecmp(cmd, "<Feed")) {
3882 /*********************************************/
3883 /* Feed related options */
3884 char *q;
3885 if (stream || feed) {
3886 fprintf(stderr, "%s:%d: Already in a tag\n",
3887 filename, line_num);
3888 } else {
3889 feed = av_mallocz(sizeof(FFStream));
3890 /* add in stream list */
3891 *last_stream = feed;
3892 last_stream = &feed->next;
3893 /* add in feed list */
3894 *last_feed = feed;
3895 last_feed = &feed->next_feed;
3897 get_arg(feed->filename, sizeof(feed->filename), &p);
3898 q = strrchr(feed->filename, '>');
3899 if (*q)
3900 *q = '\0';
3901 feed->fmt = guess_format("ffm", NULL, NULL);
3902 /* defaut feed file */
3903 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3904 "/tmp/%s.ffm", feed->filename);
3905 feed->feed_max_size = 5 * 1024 * 1024;
3906 feed->is_feed = 1;
3907 feed->feed = feed; /* self feeding :-) */
3909 } else if (!strcasecmp(cmd, "Launch")) {
3910 if (feed) {
3911 int i;
3913 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3915 for (i = 0; i < 62; i++) {
3916 get_arg(arg, sizeof(arg), &p);
3917 if (!arg[0])
3918 break;
3920 feed->child_argv[i] = av_strdup(arg);
3923 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3925 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3926 "http://%s:%d/%s",
3927 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3928 inet_ntoa(my_http_addr.sin_addr),
3929 ntohs(my_http_addr.sin_port), feed->filename);
3931 if (ffserver_debug)
3933 int j;
3934 fprintf(stdout, "Launch commandline: ");
3935 for (j = 0; j <= i; j++)
3936 fprintf(stdout, "%s ", feed->child_argv[j]);
3937 fprintf(stdout, "\n");
3940 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3941 if (feed) {
3942 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3943 feed->readonly = 1;
3944 } else if (stream) {
3945 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3947 } else if (!strcasecmp(cmd, "File")) {
3948 if (feed) {
3949 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3950 } else if (stream) {
3951 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3953 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3954 if (feed) {
3955 const char *p1;
3956 double fsize;
3958 get_arg(arg, sizeof(arg), &p);
3959 p1 = arg;
3960 fsize = strtod(p1, (char **)&p1);
3961 switch(toupper(*p1)) {
3962 case 'K':
3963 fsize *= 1024;
3964 break;
3965 case 'M':
3966 fsize *= 1024 * 1024;
3967 break;
3968 case 'G':
3969 fsize *= 1024 * 1024 * 1024;
3970 break;
3972 feed->feed_max_size = (int64_t)fsize;
3974 } else if (!strcasecmp(cmd, "</Feed>")) {
3975 if (!feed) {
3976 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3977 filename, line_num);
3978 errors++;
3980 feed = NULL;
3981 } else if (!strcasecmp(cmd, "<Stream")) {
3982 /*********************************************/
3983 /* Stream related options */
3984 char *q;
3985 if (stream || feed) {
3986 fprintf(stderr, "%s:%d: Already in a tag\n",
3987 filename, line_num);
3988 } else {
3989 stream = av_mallocz(sizeof(FFStream));
3990 *last_stream = stream;
3991 last_stream = &stream->next;
3993 get_arg(stream->filename, sizeof(stream->filename), &p);
3994 q = strrchr(stream->filename, '>');
3995 if (*q)
3996 *q = '\0';
3997 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3998 memset(&audio_enc, 0, sizeof(AVCodecContext));
3999 memset(&video_enc, 0, sizeof(AVCodecContext));
4000 audio_id = CODEC_ID_NONE;
4001 video_id = CODEC_ID_NONE;
4002 if (stream->fmt) {
4003 audio_id = stream->fmt->audio_codec;
4004 video_id = stream->fmt->video_codec;
4007 } else if (!strcasecmp(cmd, "Feed")) {
4008 get_arg(arg, sizeof(arg), &p);
4009 if (stream) {
4010 FFStream *sfeed;
4012 sfeed = first_feed;
4013 while (sfeed != NULL) {
4014 if (!strcmp(sfeed->filename, arg))
4015 break;
4016 sfeed = sfeed->next_feed;
4018 if (!sfeed) {
4019 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
4020 filename, line_num, arg);
4021 } else {
4022 stream->feed = sfeed;
4025 } else if (!strcasecmp(cmd, "Format")) {
4026 get_arg(arg, sizeof(arg), &p);
4027 if (!strcmp(arg, "status")) {
4028 stream->stream_type = STREAM_TYPE_STATUS;
4029 stream->fmt = NULL;
4030 } else {
4031 stream->stream_type = STREAM_TYPE_LIVE;
4032 /* jpeg cannot be used here, so use single frame jpeg */
4033 if (!strcmp(arg, "jpeg"))
4034 strcpy(arg, "mjpeg");
4035 stream->fmt = guess_stream_format(arg, NULL, NULL);
4036 if (!stream->fmt) {
4037 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4038 filename, line_num, arg);
4039 errors++;
4042 if (stream->fmt) {
4043 audio_id = stream->fmt->audio_codec;
4044 video_id = stream->fmt->video_codec;
4046 } else if (!strcasecmp(cmd, "InputFormat")) {
4047 get_arg(arg, sizeof(arg), &p);
4048 stream->ifmt = av_find_input_format(arg);
4049 if (!stream->ifmt) {
4050 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4051 filename, line_num, arg);
4053 } else if (!strcasecmp(cmd, "FaviconURL")) {
4054 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4055 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4056 } else {
4057 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4058 filename, line_num);
4059 errors++;
4061 } else if (!strcasecmp(cmd, "Author")) {
4062 if (stream) {
4063 get_arg(stream->author, sizeof(stream->author), &p);
4065 } else if (!strcasecmp(cmd, "Comment")) {
4066 if (stream) {
4067 get_arg(stream->comment, sizeof(stream->comment), &p);
4069 } else if (!strcasecmp(cmd, "Copyright")) {
4070 if (stream) {
4071 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4073 } else if (!strcasecmp(cmd, "Title")) {
4074 if (stream) {
4075 get_arg(stream->title, sizeof(stream->title), &p);
4077 } else if (!strcasecmp(cmd, "Preroll")) {
4078 get_arg(arg, sizeof(arg), &p);
4079 if (stream) {
4080 stream->prebuffer = atof(arg) * 1000;
4082 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4083 if (stream) {
4084 stream->send_on_key = 1;
4086 } else if (!strcasecmp(cmd, "AudioCodec")) {
4087 get_arg(arg, sizeof(arg), &p);
4088 audio_id = opt_audio_codec(arg);
4089 if (audio_id == CODEC_ID_NONE) {
4090 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4091 filename, line_num, arg);
4092 errors++;
4094 } else if (!strcasecmp(cmd, "VideoCodec")) {
4095 get_arg(arg, sizeof(arg), &p);
4096 video_id = opt_video_codec(arg);
4097 if (video_id == CODEC_ID_NONE) {
4098 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4099 filename, line_num, arg);
4100 errors++;
4102 } else if (!strcasecmp(cmd, "MaxTime")) {
4103 get_arg(arg, sizeof(arg), &p);
4104 if (stream) {
4105 stream->max_time = atof(arg) * 1000;
4107 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4108 get_arg(arg, sizeof(arg), &p);
4109 if (stream) {
4110 audio_enc.bit_rate = atoi(arg) * 1000;
4112 } else if (!strcasecmp(cmd, "AudioChannels")) {
4113 get_arg(arg, sizeof(arg), &p);
4114 if (stream) {
4115 audio_enc.channels = atoi(arg);
4117 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4118 get_arg(arg, sizeof(arg), &p);
4119 if (stream) {
4120 audio_enc.sample_rate = atoi(arg);
4122 } else if (!strcasecmp(cmd, "AudioQuality")) {
4123 get_arg(arg, sizeof(arg), &p);
4124 if (stream) {
4125 // audio_enc.quality = atof(arg) * 1000;
4127 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4128 if (stream) {
4129 int minrate, maxrate;
4131 get_arg(arg, sizeof(arg), &p);
4133 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4134 video_enc.rc_min_rate = minrate * 1000;
4135 video_enc.rc_max_rate = maxrate * 1000;
4136 } else {
4137 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4138 filename, line_num, arg);
4139 errors++;
4142 } else if (!strcasecmp(cmd, "Debug")) {
4143 if (stream) {
4144 get_arg(arg, sizeof(arg), &p);
4145 video_enc.debug = strtol(arg,0,0);
4147 } else if (!strcasecmp(cmd, "Strict")) {
4148 if (stream) {
4149 get_arg(arg, sizeof(arg), &p);
4150 video_enc.strict_std_compliance = atoi(arg);
4152 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4153 if (stream) {
4154 get_arg(arg, sizeof(arg), &p);
4155 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4157 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4158 if (stream) {
4159 get_arg(arg, sizeof(arg), &p);
4160 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4162 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4163 get_arg(arg, sizeof(arg), &p);
4164 if (stream) {
4165 video_enc.bit_rate = atoi(arg) * 1000;
4167 } else if (!strcasecmp(cmd, "VideoSize")) {
4168 get_arg(arg, sizeof(arg), &p);
4169 if (stream) {
4170 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4171 if ((video_enc.width % 16) != 0 ||
4172 (video_enc.height % 16) != 0) {
4173 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4174 filename, line_num);
4175 errors++;
4178 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4179 get_arg(arg, sizeof(arg), &p);
4180 if (stream) {
4181 video_enc.time_base.num= DEFAULT_FRAME_RATE_BASE;
4182 video_enc.time_base.den = (int)(strtod(arg, NULL) * video_enc.time_base.num);
4184 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4185 get_arg(arg, sizeof(arg), &p);
4186 if (stream) {
4187 video_enc.gop_size = atoi(arg);
4189 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4190 if (stream) {
4191 video_enc.gop_size = 1;
4193 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4194 if (stream) {
4195 video_enc.mb_decision = FF_MB_DECISION_BITS;
4197 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4198 if (stream) {
4199 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4200 video_enc.flags |= CODEC_FLAG_4MV;
4202 } else if (!strcasecmp(cmd, "VideoTag")) {
4203 get_arg(arg, sizeof(arg), &p);
4204 if ((strlen(arg) == 4) && stream) {
4205 video_enc.codec_tag = ff_get_fourcc(arg);
4207 } else if (!strcasecmp(cmd, "BitExact")) {
4208 if (stream) {
4209 video_enc.flags |= CODEC_FLAG_BITEXACT;
4211 } else if (!strcasecmp(cmd, "DctFastint")) {
4212 if (stream) {
4213 video_enc.dct_algo = FF_DCT_FASTINT;
4215 } else if (!strcasecmp(cmd, "IdctSimple")) {
4216 if (stream) {
4217 video_enc.idct_algo = FF_IDCT_SIMPLE;
4219 } else if (!strcasecmp(cmd, "Qscale")) {
4220 get_arg(arg, sizeof(arg), &p);
4221 if (stream) {
4222 video_enc.flags |= CODEC_FLAG_QSCALE;
4223 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4225 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4226 get_arg(arg, sizeof(arg), &p);
4227 if (stream) {
4228 video_enc.max_qdiff = atoi(arg);
4229 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4230 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4231 filename, line_num);
4232 errors++;
4235 } else if (!strcasecmp(cmd, "VideoQMax")) {
4236 get_arg(arg, sizeof(arg), &p);
4237 if (stream) {
4238 video_enc.qmax = atoi(arg);
4239 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4240 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4241 filename, line_num);
4242 errors++;
4245 } else if (!strcasecmp(cmd, "VideoQMin")) {
4246 get_arg(arg, sizeof(arg), &p);
4247 if (stream) {
4248 video_enc.qmin = atoi(arg);
4249 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4250 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4251 filename, line_num);
4252 errors++;
4255 } else if (!strcasecmp(cmd, "LumaElim")) {
4256 get_arg(arg, sizeof(arg), &p);
4257 if (stream) {
4258 video_enc.luma_elim_threshold = atoi(arg);
4260 } else if (!strcasecmp(cmd, "ChromaElim")) {
4261 get_arg(arg, sizeof(arg), &p);
4262 if (stream) {
4263 video_enc.chroma_elim_threshold = atoi(arg);
4265 } else if (!strcasecmp(cmd, "LumiMask")) {
4266 get_arg(arg, sizeof(arg), &p);
4267 if (stream) {
4268 video_enc.lumi_masking = atof(arg);
4270 } else if (!strcasecmp(cmd, "DarkMask")) {
4271 get_arg(arg, sizeof(arg), &p);
4272 if (stream) {
4273 video_enc.dark_masking = atof(arg);
4275 } else if (!strcasecmp(cmd, "NoVideo")) {
4276 video_id = CODEC_ID_NONE;
4277 } else if (!strcasecmp(cmd, "NoAudio")) {
4278 audio_id = CODEC_ID_NONE;
4279 } else if (!strcasecmp(cmd, "ACL")) {
4280 IPAddressACL acl;
4282 get_arg(arg, sizeof(arg), &p);
4283 if (strcasecmp(arg, "allow") == 0) {
4284 acl.action = IP_ALLOW;
4285 } else if (strcasecmp(arg, "deny") == 0) {
4286 acl.action = IP_DENY;
4287 } else {
4288 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4289 filename, line_num, arg);
4290 errors++;
4293 get_arg(arg, sizeof(arg), &p);
4295 if (resolve_host(&acl.first, arg) != 0) {
4296 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4297 filename, line_num, arg);
4298 errors++;
4299 } else {
4300 acl.last = acl.first;
4303 get_arg(arg, sizeof(arg), &p);
4305 if (arg[0]) {
4306 if (resolve_host(&acl.last, arg) != 0) {
4307 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4308 filename, line_num, arg);
4309 errors++;
4313 if (!errors) {
4314 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4315 IPAddressACL **naclp = 0;
4317 acl.next = 0;
4318 *nacl = acl;
4320 if (stream) {
4321 naclp = &stream->acl;
4322 } else if (feed) {
4323 naclp = &feed->acl;
4324 } else {
4325 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4326 filename, line_num);
4327 errors++;
4330 if (naclp) {
4331 while (*naclp)
4332 naclp = &(*naclp)->next;
4334 *naclp = nacl;
4337 } else if (!strcasecmp(cmd, "RTSPOption")) {
4338 get_arg(arg, sizeof(arg), &p);
4339 if (stream) {
4340 av_freep(&stream->rtsp_option);
4341 stream->rtsp_option = av_strdup(arg);
4343 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4344 get_arg(arg, sizeof(arg), &p);
4345 if (stream) {
4346 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4347 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4348 filename, line_num, arg);
4349 errors++;
4351 stream->is_multicast = 1;
4352 stream->loop = 1; /* default is looping */
4354 } else if (!strcasecmp(cmd, "MulticastPort")) {
4355 get_arg(arg, sizeof(arg), &p);
4356 if (stream) {
4357 stream->multicast_port = atoi(arg);
4359 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4360 get_arg(arg, sizeof(arg), &p);
4361 if (stream) {
4362 stream->multicast_ttl = atoi(arg);
4364 } else if (!strcasecmp(cmd, "NoLoop")) {
4365 if (stream) {
4366 stream->loop = 0;
4368 } else if (!strcasecmp(cmd, "</Stream>")) {
4369 if (!stream) {
4370 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4371 filename, line_num);
4372 errors++;
4374 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4375 if (audio_id != CODEC_ID_NONE) {
4376 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4377 audio_enc.codec_id = audio_id;
4378 add_codec(stream, &audio_enc);
4380 if (video_id != CODEC_ID_NONE) {
4381 video_enc.codec_type = CODEC_TYPE_VIDEO;
4382 video_enc.codec_id = video_id;
4383 add_codec(stream, &video_enc);
4386 stream = NULL;
4387 } else if (!strcasecmp(cmd, "<Redirect")) {
4388 /*********************************************/
4389 char *q;
4390 if (stream || feed || redirect) {
4391 fprintf(stderr, "%s:%d: Already in a tag\n",
4392 filename, line_num);
4393 errors++;
4394 } else {
4395 redirect = av_mallocz(sizeof(FFStream));
4396 *last_stream = redirect;
4397 last_stream = &redirect->next;
4399 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4400 q = strrchr(redirect->filename, '>');
4401 if (*q)
4402 *q = '\0';
4403 redirect->stream_type = STREAM_TYPE_REDIRECT;
4405 } else if (!strcasecmp(cmd, "URL")) {
4406 if (redirect) {
4407 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4409 } else if (!strcasecmp(cmd, "</Redirect>")) {
4410 if (!redirect) {
4411 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4412 filename, line_num);
4413 errors++;
4415 if (!redirect->feed_filename[0]) {
4416 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4417 filename, line_num);
4418 errors++;
4420 redirect = NULL;
4421 } else if (!strcasecmp(cmd, "LoadModule")) {
4422 get_arg(arg, sizeof(arg), &p);
4423 #ifdef HAVE_DLOPEN
4424 load_module(arg);
4425 #else
4426 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4427 filename, line_num, arg);
4428 errors++;
4429 #endif
4430 } else {
4431 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4432 filename, line_num, cmd);
4433 errors++;
4437 fclose(f);
4438 if (errors)
4439 return -1;
4440 else
4441 return 0;
4444 static void show_banner(void)
4446 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000-2006 Fabrice Bellard, et al.\n");
4449 static void show_help(void)
4451 show_banner();
4452 printf("usage: ffserver [-L] [-h] [-f configfile]\n"
4453 "Hyper fast multi format Audio/Video streaming server\n"
4454 "\n"
4455 "-L : print the LICENSE\n"
4456 "-h : this help\n"
4457 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4461 static void show_license(void)
4463 show_banner();
4464 printf(
4465 "FFmpeg is free software; you can redistribute it and/or\n"
4466 "modify it under the terms of the GNU Lesser General Public\n"
4467 "License as published by the Free Software Foundation; either\n"
4468 "version 2.1 of the License, or (at your option) any later version.\n"
4469 "\n"
4470 "FFmpeg is distributed in the hope that it will be useful,\n"
4471 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4472 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4473 "Lesser General Public License for more details.\n"
4474 "\n"
4475 "You should have received a copy of the GNU Lesser General Public\n"
4476 "License along with FFmpeg; if not, write to the Free Software\n"
4477 "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
4481 static void handle_child_exit(int sig)
4483 pid_t pid;
4484 int status;
4486 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4487 FFStream *feed;
4489 for (feed = first_feed; feed; feed = feed->next) {
4490 if (feed->pid == pid) {
4491 int uptime = time(0) - feed->pid_start;
4493 feed->pid = 0;
4494 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4496 if (uptime < 30) {
4497 /* Turn off any more restarts */
4498 feed->child_argv = 0;
4504 need_to_start_children = 1;
4507 int main(int argc, char **argv)
4509 const char *config_filename;
4510 int c;
4511 struct sigaction sigact;
4513 av_register_all();
4515 config_filename = "/etc/ffserver.conf";
4517 my_program_name = argv[0];
4518 my_program_dir = getcwd(0, 0);
4519 ffserver_daemon = 1;
4521 for(;;) {
4522 c = getopt(argc, argv, "ndLh?f:");
4523 if (c == -1)
4524 break;
4525 switch(c) {
4526 case 'L':
4527 show_license();
4528 exit(1);
4529 case '?':
4530 case 'h':
4531 show_help();
4532 exit(1);
4533 case 'n':
4534 no_launch = 1;
4535 break;
4536 case 'd':
4537 ffserver_debug = 1;
4538 ffserver_daemon = 0;
4539 break;
4540 case 'f':
4541 config_filename = optarg;
4542 break;
4543 default:
4544 exit(2);
4548 putenv("http_proxy"); /* Kill the http_proxy */
4550 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4552 /* address on which the server will handle HTTP connections */
4553 my_http_addr.sin_family = AF_INET;
4554 my_http_addr.sin_port = htons (8080);
4555 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4557 /* address on which the server will handle RTSP connections */
4558 my_rtsp_addr.sin_family = AF_INET;
4559 my_rtsp_addr.sin_port = htons (5454);
4560 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4562 nb_max_connections = 5;
4563 max_bandwidth = 1000;
4564 first_stream = NULL;
4565 logfilename[0] = '\0';
4567 memset(&sigact, 0, sizeof(sigact));
4568 sigact.sa_handler = handle_child_exit;
4569 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4570 sigaction(SIGCHLD, &sigact, 0);
4572 if (parse_ffconfig(config_filename) < 0) {
4573 fprintf(stderr, "Incorrect config file - exiting.\n");
4574 exit(1);
4577 build_file_streams();
4579 build_feed_streams();
4581 compute_bandwidth();
4583 /* put the process in background and detach it from its TTY */
4584 if (ffserver_daemon) {
4585 int pid;
4587 pid = fork();
4588 if (pid < 0) {
4589 perror("fork");
4590 exit(1);
4591 } else if (pid > 0) {
4592 /* parent : exit */
4593 exit(0);
4594 } else {
4595 /* child */
4596 setsid();
4597 chdir("/");
4598 close(0);
4599 open("/dev/null", O_RDWR);
4600 if (strcmp(logfilename, "-") != 0) {
4601 close(1);
4602 dup(0);
4604 close(2);
4605 dup(0);
4609 /* signal init */
4610 signal(SIGPIPE, SIG_IGN);
4612 /* open log file if needed */
4613 if (logfilename[0] != '\0') {
4614 if (!strcmp(logfilename, "-"))
4615 logfile = stdout;
4616 else
4617 logfile = fopen(logfilename, "w");
4620 if (http_server() < 0) {
4621 fprintf(stderr, "Could not start server\n");
4622 exit(1);
4625 return 0;