Add my last name to copyright headers
[ffmpeg-lucabe.git] / ffserver.c
blob71118aebb6031f3725648d2d7866db622386a82d
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 <sys/poll.h>
29 #include "avformat.h"
31 #include <stdarg.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <sys/ioctl.h>
35 #ifdef HAVE_SYS_POLL_H
36 #include <sys/poll.h>
37 #endif
38 #include <errno.h>
39 #include <sys/time.h>
40 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
41 #include <time.h>
42 #include <sys/wait.h>
43 #include <signal.h>
44 #ifdef HAVE_DLFCN_H
45 #include <dlfcn.h>
46 #endif
48 #include "network.h"
49 #include "version.h"
50 #include "ffserver.h"
51 #include "random.h"
52 #include "avstring.h"
54 #undef exit
56 /* maximum number of simultaneous HTTP connections */
57 #define HTTP_MAX_CONNECTIONS 2000
59 enum HTTPState {
60 HTTPSTATE_WAIT_REQUEST,
61 HTTPSTATE_SEND_HEADER,
62 HTTPSTATE_SEND_DATA_HEADER,
63 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
64 HTTPSTATE_SEND_DATA_TRAILER,
65 HTTPSTATE_RECEIVE_DATA,
66 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
67 HTTPSTATE_READY,
69 RTSPSTATE_WAIT_REQUEST,
70 RTSPSTATE_SEND_REPLY,
71 RTSPSTATE_SEND_PACKET,
74 const char *http_state[] = {
75 "HTTP_WAIT_REQUEST",
76 "HTTP_SEND_HEADER",
78 "SEND_DATA_HEADER",
79 "SEND_DATA",
80 "SEND_DATA_TRAILER",
81 "RECEIVE_DATA",
82 "WAIT_FEED",
83 "READY",
85 "RTSP_WAIT_REQUEST",
86 "RTSP_SEND_REPLY",
87 "RTSP_SEND_PACKET",
90 #define IOBUFFER_INIT_SIZE 8192
92 /* timeouts are in ms */
93 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
94 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
96 #define SYNC_TIMEOUT (10 * 1000)
98 typedef struct {
99 int64_t count1, count2;
100 int64_t time1, time2;
101 } DataRateData;
103 /* context associated with one connection */
104 typedef struct HTTPContext {
105 enum HTTPState state;
106 int fd; /* socket file descriptor */
107 struct sockaddr_in from_addr; /* origin */
108 struct pollfd *poll_entry; /* used when polling */
109 int64_t timeout;
110 uint8_t *buffer_ptr, *buffer_end;
111 int http_error;
112 int post;
113 struct HTTPContext *next;
114 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
115 int64_t data_count;
116 /* feed input */
117 int feed_fd;
118 /* input format handling */
119 AVFormatContext *fmt_in;
120 int64_t start_time; /* In milliseconds - this wraps fairly often */
121 int64_t first_pts; /* initial pts value */
122 int64_t cur_pts; /* current pts value from the stream in us */
123 int64_t cur_frame_duration; /* duration of the current frame in us */
124 int cur_frame_bytes; /* output frame size, needed to compute
125 the time at which we send each
126 packet */
127 int pts_stream_index; /* stream we choose as clock reference */
128 int64_t cur_clock; /* current clock reference value in us */
129 /* output format handling */
130 struct FFStream *stream;
131 /* -1 is invalid stream */
132 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
133 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
134 int switch_pending;
135 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
136 int last_packet_sent; /* true if last data packet was sent */
137 int suppress_log;
138 DataRateData datarate;
139 int wmp_client_id;
140 char protocol[16];
141 char method[16];
142 char url[128];
143 int buffer_size;
144 uint8_t *buffer;
145 int is_packetized; /* if true, the stream is packetized */
146 int packet_stream_index; /* current stream for output in state machine */
148 /* RTSP state specific */
149 uint8_t *pb_buffer; /* XXX: use that in all the code */
150 ByteIOContext *pb;
151 int seq; /* RTSP sequence number */
153 /* RTP state specific */
154 enum RTSPProtocol rtp_protocol;
155 char session_id[32]; /* session id */
156 AVFormatContext *rtp_ctx[MAX_STREAMS];
158 /* RTP/UDP specific */
159 URLContext *rtp_handles[MAX_STREAMS];
161 /* RTP/TCP specific */
162 struct HTTPContext *rtsp_c;
163 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
164 } HTTPContext;
166 static AVFrame dummy_frame;
168 /* each generated stream is described here */
169 enum StreamType {
170 STREAM_TYPE_LIVE,
171 STREAM_TYPE_STATUS,
172 STREAM_TYPE_REDIRECT,
175 enum IPAddressAction {
176 IP_ALLOW = 1,
177 IP_DENY,
180 typedef struct IPAddressACL {
181 struct IPAddressACL *next;
182 enum IPAddressAction action;
183 /* These are in host order */
184 struct in_addr first;
185 struct in_addr last;
186 } IPAddressACL;
188 /* description of each stream of the ffserver.conf file */
189 typedef struct FFStream {
190 enum StreamType stream_type;
191 char filename[1024]; /* stream filename */
192 struct FFStream *feed; /* feed we are using (can be null if
193 coming from file) */
194 AVFormatParameters *ap_in; /* input parameters */
195 AVInputFormat *ifmt; /* if non NULL, force input format */
196 AVOutputFormat *fmt;
197 IPAddressACL *acl;
198 int nb_streams;
199 int prebuffer; /* Number of millseconds early to start */
200 int64_t max_time; /* Number of milliseconds to run */
201 int send_on_key;
202 AVStream *streams[MAX_STREAMS];
203 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
204 char feed_filename[1024]; /* file name of the feed storage, or
205 input file name for a stream */
206 char author[512];
207 char title[512];
208 char copyright[512];
209 char comment[512];
210 pid_t pid; /* Of ffmpeg process */
211 time_t pid_start; /* Of ffmpeg process */
212 char **child_argv;
213 struct FFStream *next;
214 int bandwidth; /* bandwidth, in kbits/s */
215 /* RTSP options */
216 char *rtsp_option;
217 /* multicast specific */
218 int is_multicast;
219 struct in_addr multicast_ip;
220 int multicast_port; /* first port used for multicast */
221 int multicast_ttl;
222 int loop; /* if true, send the stream in loops (only meaningful if file) */
224 /* feed specific */
225 int feed_opened; /* true if someone is writing to the feed */
226 int is_feed; /* true if it is a feed */
227 int readonly; /* True if writing is prohibited to the file */
228 int conns_served;
229 int64_t bytes_served;
230 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
231 int64_t feed_write_index; /* current write position in feed (it wraps round) */
232 int64_t feed_size; /* current size of feed */
233 struct FFStream *next_feed;
234 } FFStream;
236 typedef struct FeedData {
237 long long data_count;
238 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
239 } FeedData;
241 static struct sockaddr_in my_http_addr;
242 static struct sockaddr_in my_rtsp_addr;
244 static char logfilename[1024];
245 static HTTPContext *first_http_ctx;
246 static FFStream *first_feed; /* contains only feeds */
247 static FFStream *first_stream; /* contains all streams, including feeds */
249 static void new_connection(int server_fd, int is_rtsp);
250 static void close_connection(HTTPContext *c);
252 /* HTTP handling */
253 static int handle_connection(HTTPContext *c);
254 static int http_parse_request(HTTPContext *c);
255 static int http_send_data(HTTPContext *c);
256 static void compute_stats(HTTPContext *c);
257 static int open_input_stream(HTTPContext *c, const char *info);
258 static int http_start_receive_data(HTTPContext *c);
259 static int http_receive_data(HTTPContext *c);
261 /* RTSP handling */
262 static int rtsp_parse_request(HTTPContext *c);
263 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
264 static void rtsp_cmd_options(HTTPContext *c, const char *url);
265 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
266 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
267 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
268 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
270 /* SDP handling */
271 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
272 struct in_addr my_ip);
274 /* RTP handling */
275 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
276 FFStream *stream, const char *session_id,
277 enum RTSPProtocol rtp_protocol);
278 static int rtp_new_av_stream(HTTPContext *c,
279 int stream_index, struct sockaddr_in *dest_addr,
280 HTTPContext *rtsp_c);
282 static const char *my_program_name;
283 static const char *my_program_dir;
285 static int ffserver_debug;
286 static int ffserver_daemon;
287 static int no_launch;
288 static int need_to_start_children;
290 static int nb_max_connections;
291 static int nb_connections;
293 static int max_bandwidth;
294 static int current_bandwidth;
296 static int64_t cur_time; // Making this global saves on passing it around everywhere
298 static AVRandomState random_state;
300 static FILE *logfile = NULL;
302 static void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
304 va_list ap;
305 va_start(ap, fmt);
307 if (logfile) {
308 vfprintf(logfile, fmt, ap);
309 fflush(logfile);
311 va_end(ap);
314 static char *ctime1(char *buf2)
316 time_t ti;
317 char *p;
319 ti = time(NULL);
320 p = ctime(&ti);
321 strcpy(buf2, p);
322 p = buf2 + strlen(p) - 1;
323 if (*p == '\n')
324 *p = '\0';
325 return buf2;
328 static void log_connection(HTTPContext *c)
330 char buf2[32];
332 if (c->suppress_log)
333 return;
335 http_log("%s - - [%s] \"%s %s %s\" %d %"PRId64"\n",
336 inet_ntoa(c->from_addr.sin_addr),
337 ctime1(buf2), c->method, c->url,
338 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
341 static void update_datarate(DataRateData *drd, int64_t count)
343 if (!drd->time1 && !drd->count1) {
344 drd->time1 = drd->time2 = cur_time;
345 drd->count1 = drd->count2 = count;
346 } else {
347 if (cur_time - drd->time2 > 5000) {
348 drd->time1 = drd->time2;
349 drd->count1 = drd->count2;
350 drd->time2 = cur_time;
351 drd->count2 = count;
356 /* In bytes per second */
357 static int compute_datarate(DataRateData *drd, int64_t count)
359 if (cur_time == drd->time1)
360 return 0;
362 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
366 static void start_children(FFStream *feed)
368 if (no_launch)
369 return;
371 for (; feed; feed = feed->next) {
372 if (feed->child_argv && !feed->pid) {
373 feed->pid_start = time(0);
375 feed->pid = fork();
377 if (feed->pid < 0) {
378 fprintf(stderr, "Unable to create children\n");
379 exit(1);
381 if (!feed->pid) {
382 /* In child */
383 char pathname[1024];
384 char *slash;
385 int i;
387 for (i = 3; i < 256; i++) {
388 close(i);
391 if (!ffserver_debug) {
392 i = open("/dev/null", O_RDWR);
393 if (i)
394 dup2(i, 0);
395 dup2(i, 1);
396 dup2(i, 2);
397 if (i)
398 close(i);
401 av_strlcpy(pathname, my_program_name, sizeof(pathname));
403 slash = strrchr(pathname, '/');
404 if (!slash) {
405 slash = pathname;
406 } else {
407 slash++;
409 strcpy(slash, "ffmpeg");
411 /* This is needed to make relative pathnames work */
412 chdir(my_program_dir);
414 signal(SIGPIPE, SIG_DFL);
416 execvp(pathname, feed->child_argv);
418 _exit(1);
424 /* open a listening socket */
425 static int socket_open_listen(struct sockaddr_in *my_addr)
427 int server_fd, tmp;
429 server_fd = socket(AF_INET,SOCK_STREAM,0);
430 if (server_fd < 0) {
431 perror ("socket");
432 return -1;
435 tmp = 1;
436 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
438 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
439 char bindmsg[32];
440 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
441 perror (bindmsg);
442 closesocket(server_fd);
443 return -1;
446 if (listen (server_fd, 5) < 0) {
447 perror ("listen");
448 closesocket(server_fd);
449 return -1;
451 ff_socket_nonblock(server_fd, 1);
453 return server_fd;
456 /* start all multicast streams */
457 static void start_multicast(void)
459 FFStream *stream;
460 char session_id[32];
461 HTTPContext *rtp_c;
462 struct sockaddr_in dest_addr;
463 int default_port, stream_index;
465 default_port = 6000;
466 for(stream = first_stream; stream != NULL; stream = stream->next) {
467 if (stream->is_multicast) {
468 /* open the RTP connection */
469 snprintf(session_id, sizeof(session_id), "%08x%08x",
470 av_random(&random_state), av_random(&random_state));
472 /* choose a port if none given */
473 if (stream->multicast_port == 0) {
474 stream->multicast_port = default_port;
475 default_port += 100;
478 dest_addr.sin_family = AF_INET;
479 dest_addr.sin_addr = stream->multicast_ip;
480 dest_addr.sin_port = htons(stream->multicast_port);
482 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
483 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
484 if (!rtp_c) {
485 continue;
487 if (open_input_stream(rtp_c, "") < 0) {
488 fprintf(stderr, "Could not open input stream for stream '%s'\n",
489 stream->filename);
490 continue;
493 /* open each RTP stream */
494 for(stream_index = 0; stream_index < stream->nb_streams;
495 stream_index++) {
496 dest_addr.sin_port = htons(stream->multicast_port +
497 2 * stream_index);
498 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
499 fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
500 stream->filename, stream_index);
501 exit(1);
505 /* change state to send data */
506 rtp_c->state = HTTPSTATE_SEND_DATA;
511 /* main loop of the http server */
512 static int http_server(void)
514 int server_fd, ret, rtsp_server_fd, delay, delay1;
515 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
516 HTTPContext *c, *c_next;
518 server_fd = socket_open_listen(&my_http_addr);
519 if (server_fd < 0)
520 return -1;
522 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
523 if (rtsp_server_fd < 0)
524 return -1;
526 http_log("ffserver started.\n");
528 start_children(first_feed);
530 first_http_ctx = NULL;
531 nb_connections = 0;
533 start_multicast();
535 for(;;) {
536 poll_entry = poll_table;
537 poll_entry->fd = server_fd;
538 poll_entry->events = POLLIN;
539 poll_entry++;
541 poll_entry->fd = rtsp_server_fd;
542 poll_entry->events = POLLIN;
543 poll_entry++;
545 /* wait for events on each HTTP handle */
546 c = first_http_ctx;
547 delay = 1000;
548 while (c != NULL) {
549 int fd;
550 fd = c->fd;
551 switch(c->state) {
552 case HTTPSTATE_SEND_HEADER:
553 case RTSPSTATE_SEND_REPLY:
554 case RTSPSTATE_SEND_PACKET:
555 c->poll_entry = poll_entry;
556 poll_entry->fd = fd;
557 poll_entry->events = POLLOUT;
558 poll_entry++;
559 break;
560 case HTTPSTATE_SEND_DATA_HEADER:
561 case HTTPSTATE_SEND_DATA:
562 case HTTPSTATE_SEND_DATA_TRAILER:
563 if (!c->is_packetized) {
564 /* for TCP, we output as much as we can (may need to put a limit) */
565 c->poll_entry = poll_entry;
566 poll_entry->fd = fd;
567 poll_entry->events = POLLOUT;
568 poll_entry++;
569 } else {
570 /* when ffserver is doing the timing, we work by
571 looking at which packet need to be sent every
572 10 ms */
573 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
574 if (delay1 < delay)
575 delay = delay1;
577 break;
578 case HTTPSTATE_WAIT_REQUEST:
579 case HTTPSTATE_RECEIVE_DATA:
580 case HTTPSTATE_WAIT_FEED:
581 case RTSPSTATE_WAIT_REQUEST:
582 /* need to catch errors */
583 c->poll_entry = poll_entry;
584 poll_entry->fd = fd;
585 poll_entry->events = POLLIN;/* Maybe this will work */
586 poll_entry++;
587 break;
588 default:
589 c->poll_entry = NULL;
590 break;
592 c = c->next;
595 /* wait for an event on one connection. We poll at least every
596 second to handle timeouts */
597 do {
598 ret = poll(poll_table, poll_entry - poll_table, delay);
599 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
600 ff_neterrno() != FF_NETERROR(EINTR))
601 return -1;
602 } while (ret < 0);
604 cur_time = av_gettime() / 1000;
606 if (need_to_start_children) {
607 need_to_start_children = 0;
608 start_children(first_feed);
611 /* now handle the events */
612 for(c = first_http_ctx; c != NULL; c = c_next) {
613 c_next = c->next;
614 if (handle_connection(c) < 0) {
615 /* close and free the connection */
616 log_connection(c);
617 close_connection(c);
621 poll_entry = poll_table;
622 /* new HTTP connection request ? */
623 if (poll_entry->revents & POLLIN) {
624 new_connection(server_fd, 0);
626 poll_entry++;
627 /* new RTSP connection request ? */
628 if (poll_entry->revents & POLLIN) {
629 new_connection(rtsp_server_fd, 1);
634 /* start waiting for a new HTTP/RTSP request */
635 static void start_wait_request(HTTPContext *c, int is_rtsp)
637 c->buffer_ptr = c->buffer;
638 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
640 if (is_rtsp) {
641 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
642 c->state = RTSPSTATE_WAIT_REQUEST;
643 } else {
644 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
645 c->state = HTTPSTATE_WAIT_REQUEST;
649 static void new_connection(int server_fd, int is_rtsp)
651 struct sockaddr_in from_addr;
652 int fd, len;
653 HTTPContext *c = NULL;
655 len = sizeof(from_addr);
656 fd = accept(server_fd, (struct sockaddr *)&from_addr,
657 &len);
658 if (fd < 0)
659 return;
660 ff_socket_nonblock(fd, 1);
662 /* XXX: should output a warning page when coming
663 close to the connection limit */
664 if (nb_connections >= nb_max_connections)
665 goto fail;
667 /* add a new connection */
668 c = av_mallocz(sizeof(HTTPContext));
669 if (!c)
670 goto fail;
672 c->fd = fd;
673 c->poll_entry = NULL;
674 c->from_addr = from_addr;
675 c->buffer_size = IOBUFFER_INIT_SIZE;
676 c->buffer = av_malloc(c->buffer_size);
677 if (!c->buffer)
678 goto fail;
680 c->next = first_http_ctx;
681 first_http_ctx = c;
682 nb_connections++;
684 start_wait_request(c, is_rtsp);
686 return;
688 fail:
689 if (c) {
690 av_free(c->buffer);
691 av_free(c);
693 closesocket(fd);
696 static void close_connection(HTTPContext *c)
698 HTTPContext **cp, *c1;
699 int i, nb_streams;
700 AVFormatContext *ctx;
701 URLContext *h;
702 AVStream *st;
704 /* remove connection from list */
705 cp = &first_http_ctx;
706 while ((*cp) != NULL) {
707 c1 = *cp;
708 if (c1 == c) {
709 *cp = c->next;
710 } else {
711 cp = &c1->next;
715 /* remove references, if any (XXX: do it faster) */
716 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
717 if (c1->rtsp_c == c)
718 c1->rtsp_c = NULL;
721 /* remove connection associated resources */
722 if (c->fd >= 0)
723 closesocket(c->fd);
724 if (c->fmt_in) {
725 /* close each frame parser */
726 for(i=0;i<c->fmt_in->nb_streams;i++) {
727 st = c->fmt_in->streams[i];
728 if (st->codec->codec) {
729 avcodec_close(st->codec);
732 av_close_input_file(c->fmt_in);
735 /* free RTP output streams if any */
736 nb_streams = 0;
737 if (c->stream)
738 nb_streams = c->stream->nb_streams;
740 for(i=0;i<nb_streams;i++) {
741 ctx = c->rtp_ctx[i];
742 if (ctx) {
743 av_write_trailer(ctx);
744 av_free(ctx);
746 h = c->rtp_handles[i];
747 if (h) {
748 url_close(h);
752 ctx = &c->fmt_ctx;
754 if (!c->last_packet_sent) {
755 if (ctx->oformat) {
756 /* prepare header */
757 if (url_open_dyn_buf(&ctx->pb) >= 0) {
758 av_write_trailer(ctx);
759 url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
764 for(i=0; i<ctx->nb_streams; i++)
765 av_free(ctx->streams[i]) ;
767 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
768 current_bandwidth -= c->stream->bandwidth;
770 /* signal that there is no feed if we are the feeder socket */
771 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
772 c->stream->feed_opened = 0;
773 close(c->feed_fd);
776 av_freep(&c->pb_buffer);
777 av_freep(&c->packet_buffer);
778 av_free(c->buffer);
779 av_free(c);
780 nb_connections--;
783 static int handle_connection(HTTPContext *c)
785 int len, ret;
787 switch(c->state) {
788 case HTTPSTATE_WAIT_REQUEST:
789 case RTSPSTATE_WAIT_REQUEST:
790 /* timeout ? */
791 if ((c->timeout - cur_time) < 0)
792 return -1;
793 if (c->poll_entry->revents & (POLLERR | POLLHUP))
794 return -1;
796 /* no need to read if no events */
797 if (!(c->poll_entry->revents & POLLIN))
798 return 0;
799 /* read the data */
800 read_loop:
801 len = recv(c->fd, c->buffer_ptr, 1, 0);
802 if (len < 0) {
803 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
804 ff_neterrno() != FF_NETERROR(EINTR))
805 return -1;
806 } else if (len == 0) {
807 return -1;
808 } else {
809 /* search for end of request. */
810 uint8_t *ptr;
811 c->buffer_ptr += len;
812 ptr = c->buffer_ptr;
813 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
814 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
815 /* request found : parse it and reply */
816 if (c->state == HTTPSTATE_WAIT_REQUEST) {
817 ret = http_parse_request(c);
818 } else {
819 ret = rtsp_parse_request(c);
821 if (ret < 0)
822 return -1;
823 } else if (ptr >= c->buffer_end) {
824 /* request too long: cannot do anything */
825 return -1;
826 } else goto read_loop;
828 break;
830 case HTTPSTATE_SEND_HEADER:
831 if (c->poll_entry->revents & (POLLERR | POLLHUP))
832 return -1;
834 /* no need to write if no events */
835 if (!(c->poll_entry->revents & POLLOUT))
836 return 0;
837 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
838 if (len < 0) {
839 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
840 ff_neterrno() != FF_NETERROR(EINTR)) {
841 /* error : close connection */
842 av_freep(&c->pb_buffer);
843 return -1;
845 } else {
846 c->buffer_ptr += len;
847 if (c->stream)
848 c->stream->bytes_served += len;
849 c->data_count += len;
850 if (c->buffer_ptr >= c->buffer_end) {
851 av_freep(&c->pb_buffer);
852 /* if error, exit */
853 if (c->http_error) {
854 return -1;
856 /* all the buffer was sent : synchronize to the incoming stream */
857 c->state = HTTPSTATE_SEND_DATA_HEADER;
858 c->buffer_ptr = c->buffer_end = c->buffer;
861 break;
863 case HTTPSTATE_SEND_DATA:
864 case HTTPSTATE_SEND_DATA_HEADER:
865 case HTTPSTATE_SEND_DATA_TRAILER:
866 /* for packetized output, we consider we can always write (the
867 input streams sets the speed). It may be better to verify
868 that we do not rely too much on the kernel queues */
869 if (!c->is_packetized) {
870 if (c->poll_entry->revents & (POLLERR | POLLHUP))
871 return -1;
873 /* no need to read if no events */
874 if (!(c->poll_entry->revents & POLLOUT))
875 return 0;
877 if (http_send_data(c) < 0)
878 return -1;
879 /* close connection if trailer sent */
880 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
881 return -1;
882 break;
883 case HTTPSTATE_RECEIVE_DATA:
884 /* no need to read if no events */
885 if (c->poll_entry->revents & (POLLERR | POLLHUP))
886 return -1;
887 if (!(c->poll_entry->revents & POLLIN))
888 return 0;
889 if (http_receive_data(c) < 0)
890 return -1;
891 break;
892 case HTTPSTATE_WAIT_FEED:
893 /* no need to read if no events */
894 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
895 return -1;
897 /* nothing to do, we'll be waken up by incoming feed packets */
898 break;
900 case RTSPSTATE_SEND_REPLY:
901 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
902 av_freep(&c->pb_buffer);
903 return -1;
905 /* no need to write if no events */
906 if (!(c->poll_entry->revents & POLLOUT))
907 return 0;
908 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
909 if (len < 0) {
910 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
911 ff_neterrno() != FF_NETERROR(EINTR)) {
912 /* error : close connection */
913 av_freep(&c->pb_buffer);
914 return -1;
916 } else {
917 c->buffer_ptr += len;
918 c->data_count += len;
919 if (c->buffer_ptr >= c->buffer_end) {
920 /* all the buffer was sent : wait for a new request */
921 av_freep(&c->pb_buffer);
922 start_wait_request(c, 1);
925 break;
926 case RTSPSTATE_SEND_PACKET:
927 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
928 av_freep(&c->packet_buffer);
929 return -1;
931 /* no need to write if no events */
932 if (!(c->poll_entry->revents & POLLOUT))
933 return 0;
934 len = send(c->fd, c->packet_buffer_ptr,
935 c->packet_buffer_end - c->packet_buffer_ptr, 0);
936 if (len < 0) {
937 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
938 ff_neterrno() != FF_NETERROR(EINTR)) {
939 /* error : close connection */
940 av_freep(&c->packet_buffer);
941 return -1;
943 } else {
944 c->packet_buffer_ptr += len;
945 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
946 /* all the buffer was sent : wait for a new request */
947 av_freep(&c->packet_buffer);
948 c->state = RTSPSTATE_WAIT_REQUEST;
951 break;
952 case HTTPSTATE_READY:
953 /* nothing to do */
954 break;
955 default:
956 return -1;
958 return 0;
961 static int extract_rates(char *rates, int ratelen, const char *request)
963 const char *p;
965 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
966 if (strncasecmp(p, "Pragma:", 7) == 0) {
967 const char *q = p + 7;
969 while (*q && *q != '\n' && isspace(*q))
970 q++;
972 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
973 int stream_no;
974 int rate_no;
976 q += 20;
978 memset(rates, 0xff, ratelen);
980 while (1) {
981 while (*q && *q != '\n' && *q != ':')
982 q++;
984 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
985 break;
987 stream_no--;
988 if (stream_no < ratelen && stream_no >= 0) {
989 rates[stream_no] = rate_no;
992 while (*q && *q != '\n' && !isspace(*q))
993 q++;
996 return 1;
999 p = strchr(p, '\n');
1000 if (!p)
1001 break;
1003 p++;
1006 return 0;
1009 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1011 int i;
1012 int best_bitrate = 100000000;
1013 int best = -1;
1015 for (i = 0; i < feed->nb_streams; i++) {
1016 AVCodecContext *feed_codec = feed->streams[i]->codec;
1018 if (feed_codec->codec_id != codec->codec_id ||
1019 feed_codec->sample_rate != codec->sample_rate ||
1020 feed_codec->width != codec->width ||
1021 feed_codec->height != codec->height) {
1022 continue;
1025 /* Potential stream */
1027 /* We want the fastest stream less than bit_rate, or the slowest
1028 * faster than bit_rate
1031 if (feed_codec->bit_rate <= bit_rate) {
1032 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1033 best_bitrate = feed_codec->bit_rate;
1034 best = i;
1036 } else {
1037 if (feed_codec->bit_rate < best_bitrate) {
1038 best_bitrate = feed_codec->bit_rate;
1039 best = i;
1044 return best;
1047 static int modify_current_stream(HTTPContext *c, char *rates)
1049 int i;
1050 FFStream *req = c->stream;
1051 int action_required = 0;
1053 /* Not much we can do for a feed */
1054 if (!req->feed)
1055 return 0;
1057 for (i = 0; i < req->nb_streams; i++) {
1058 AVCodecContext *codec = req->streams[i]->codec;
1060 switch(rates[i]) {
1061 case 0:
1062 c->switch_feed_streams[i] = req->feed_streams[i];
1063 break;
1064 case 1:
1065 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1066 break;
1067 case 2:
1068 /* Wants off or slow */
1069 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1070 #ifdef WANTS_OFF
1071 /* This doesn't work well when it turns off the only stream! */
1072 c->switch_feed_streams[i] = -2;
1073 c->feed_streams[i] = -2;
1074 #endif
1075 break;
1078 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1079 action_required = 1;
1082 return action_required;
1086 static void do_switch_stream(HTTPContext *c, int i)
1088 if (c->switch_feed_streams[i] >= 0) {
1089 #ifdef PHILIP
1090 c->feed_streams[i] = c->switch_feed_streams[i];
1091 #endif
1093 /* Now update the stream */
1095 c->switch_feed_streams[i] = -1;
1098 /* XXX: factorize in utils.c ? */
1099 /* XXX: take care with different space meaning */
1100 static void skip_spaces(const char **pp)
1102 const char *p;
1103 p = *pp;
1104 while (*p == ' ' || *p == '\t')
1105 p++;
1106 *pp = p;
1109 static void get_word(char *buf, int buf_size, const char **pp)
1111 const char *p;
1112 char *q;
1114 p = *pp;
1115 skip_spaces(&p);
1116 q = buf;
1117 while (!isspace(*p) && *p != '\0') {
1118 if ((q - buf) < buf_size - 1)
1119 *q++ = *p;
1120 p++;
1122 if (buf_size > 0)
1123 *q = '\0';
1124 *pp = p;
1127 static int validate_acl(FFStream *stream, HTTPContext *c)
1129 enum IPAddressAction last_action = IP_DENY;
1130 IPAddressACL *acl;
1131 struct in_addr *src = &c->from_addr.sin_addr;
1132 unsigned long src_addr = src->s_addr;
1134 for (acl = stream->acl; acl; acl = acl->next) {
1135 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr) {
1136 return (acl->action == IP_ALLOW) ? 1 : 0;
1138 last_action = acl->action;
1141 /* Nothing matched, so return not the last action */
1142 return (last_action == IP_DENY) ? 1 : 0;
1145 /* compute the real filename of a file by matching it without its
1146 extensions to all the stream filenames */
1147 static void compute_real_filename(char *filename, int max_size)
1149 char file1[1024];
1150 char file2[1024];
1151 char *p;
1152 FFStream *stream;
1154 /* compute filename by matching without the file extensions */
1155 av_strlcpy(file1, filename, sizeof(file1));
1156 p = strrchr(file1, '.');
1157 if (p)
1158 *p = '\0';
1159 for(stream = first_stream; stream != NULL; stream = stream->next) {
1160 av_strlcpy(file2, stream->filename, sizeof(file2));
1161 p = strrchr(file2, '.');
1162 if (p)
1163 *p = '\0';
1164 if (!strcmp(file1, file2)) {
1165 av_strlcpy(filename, stream->filename, max_size);
1166 break;
1171 enum RedirType {
1172 REDIR_NONE,
1173 REDIR_ASX,
1174 REDIR_RAM,
1175 REDIR_ASF,
1176 REDIR_RTSP,
1177 REDIR_SDP,
1180 /* parse http request and prepare header */
1181 static int http_parse_request(HTTPContext *c)
1183 char *p;
1184 enum RedirType redir_type;
1185 char cmd[32];
1186 char info[1024], filename[1024];
1187 char url[1024], *q;
1188 char protocol[32];
1189 char msg[1024];
1190 const char *mime_type;
1191 FFStream *stream;
1192 int i;
1193 char ratebuf[32];
1194 char *useragent = 0;
1196 p = c->buffer;
1197 get_word(cmd, sizeof(cmd), (const char **)&p);
1198 av_strlcpy(c->method, cmd, sizeof(c->method));
1200 if (!strcmp(cmd, "GET"))
1201 c->post = 0;
1202 else if (!strcmp(cmd, "POST"))
1203 c->post = 1;
1204 else
1205 return -1;
1207 get_word(url, sizeof(url), (const char **)&p);
1208 av_strlcpy(c->url, url, sizeof(c->url));
1210 get_word(protocol, sizeof(protocol), (const char **)&p);
1211 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1212 return -1;
1214 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1216 if (ffserver_debug)
1217 http_log("New connection: %s %s\n", cmd, url);
1219 /* find the filename and the optional info string in the request */
1220 p = strchr(url, '?');
1221 if (p) {
1222 av_strlcpy(info, p, sizeof(info));
1223 *p = '\0';
1224 } else {
1225 info[0] = '\0';
1228 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1230 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1231 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1232 useragent = p + 11;
1233 if (*useragent && *useragent != '\n' && isspace(*useragent))
1234 useragent++;
1235 break;
1237 p = strchr(p, '\n');
1238 if (!p)
1239 break;
1241 p++;
1244 redir_type = REDIR_NONE;
1245 if (match_ext(filename, "asx")) {
1246 redir_type = REDIR_ASX;
1247 filename[strlen(filename)-1] = 'f';
1248 } else if (match_ext(filename, "asf") &&
1249 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1250 /* if this isn't WMP or lookalike, return the redirector file */
1251 redir_type = REDIR_ASF;
1252 } else if (match_ext(filename, "rpm,ram")) {
1253 redir_type = REDIR_RAM;
1254 strcpy(filename + strlen(filename)-2, "m");
1255 } else if (match_ext(filename, "rtsp")) {
1256 redir_type = REDIR_RTSP;
1257 compute_real_filename(filename, sizeof(filename) - 1);
1258 } else if (match_ext(filename, "sdp")) {
1259 redir_type = REDIR_SDP;
1260 compute_real_filename(filename, sizeof(filename) - 1);
1263 // "redirect" / request to index.html
1264 if (!strlen(filename))
1265 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1267 stream = first_stream;
1268 while (stream != NULL) {
1269 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1270 break;
1271 stream = stream->next;
1273 if (stream == NULL) {
1274 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1275 goto send_error;
1278 c->stream = stream;
1279 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1280 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1282 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1283 c->http_error = 301;
1284 q = c->buffer;
1285 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1286 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1287 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1288 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1289 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1290 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1291 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1293 /* prepare output buffer */
1294 c->buffer_ptr = c->buffer;
1295 c->buffer_end = q;
1296 c->state = HTTPSTATE_SEND_HEADER;
1297 return 0;
1300 /* If this is WMP, get the rate information */
1301 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1302 if (modify_current_stream(c, ratebuf)) {
1303 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1304 if (c->switch_feed_streams[i] >= 0)
1305 do_switch_stream(c, i);
1310 /* If already streaming this feed, do not let start another feeder. */
1311 if (stream->feed_opened) {
1312 snprintf(msg, sizeof(msg), "This feed is already being received.");
1313 goto send_error;
1316 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1317 current_bandwidth += stream->bandwidth;
1320 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1321 c->http_error = 200;
1322 q = c->buffer;
1323 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1324 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1325 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1326 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1327 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");
1328 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",
1329 current_bandwidth, max_bandwidth);
1330 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1332 /* prepare output buffer */
1333 c->buffer_ptr = c->buffer;
1334 c->buffer_end = q;
1335 c->state = HTTPSTATE_SEND_HEADER;
1336 return 0;
1339 if (redir_type != REDIR_NONE) {
1340 char *hostinfo = 0;
1342 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1343 if (strncasecmp(p, "Host:", 5) == 0) {
1344 hostinfo = p + 5;
1345 break;
1347 p = strchr(p, '\n');
1348 if (!p)
1349 break;
1351 p++;
1354 if (hostinfo) {
1355 char *eoh;
1356 char hostbuf[260];
1358 while (isspace(*hostinfo))
1359 hostinfo++;
1361 eoh = strchr(hostinfo, '\n');
1362 if (eoh) {
1363 if (eoh[-1] == '\r')
1364 eoh--;
1366 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1367 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1368 hostbuf[eoh - hostinfo] = 0;
1370 c->http_error = 200;
1371 q = c->buffer;
1372 switch(redir_type) {
1373 case REDIR_ASX:
1374 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1375 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1376 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1377 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1378 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1379 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1380 hostbuf, filename, info);
1381 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1382 break;
1383 case REDIR_RAM:
1384 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1385 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1386 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1387 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1388 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1389 hostbuf, filename, info);
1390 break;
1391 case REDIR_ASF:
1392 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1393 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1394 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1395 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1396 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1397 hostbuf, filename, info);
1398 break;
1399 case REDIR_RTSP:
1401 char hostname[256], *p;
1402 /* extract only hostname */
1403 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1404 p = strrchr(hostname, ':');
1405 if (p)
1406 *p = '\0';
1407 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1408 /* XXX: incorrect mime type ? */
1409 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1410 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1411 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1412 hostname, ntohs(my_rtsp_addr.sin_port),
1413 filename);
1415 break;
1416 case REDIR_SDP:
1418 uint8_t *sdp_data;
1419 int sdp_data_size, len;
1420 struct sockaddr_in my_addr;
1422 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1423 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1424 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1426 len = sizeof(my_addr);
1427 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1429 /* XXX: should use a dynamic buffer */
1430 sdp_data_size = prepare_sdp_description(stream,
1431 &sdp_data,
1432 my_addr.sin_addr);
1433 if (sdp_data_size > 0) {
1434 memcpy(q, sdp_data, sdp_data_size);
1435 q += sdp_data_size;
1436 *q = '\0';
1437 av_free(sdp_data);
1440 break;
1441 default:
1442 abort();
1443 break;
1446 /* prepare output buffer */
1447 c->buffer_ptr = c->buffer;
1448 c->buffer_end = q;
1449 c->state = HTTPSTATE_SEND_HEADER;
1450 return 0;
1455 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1456 goto send_error;
1459 stream->conns_served++;
1461 /* XXX: add there authenticate and IP match */
1463 if (c->post) {
1464 /* if post, it means a feed is being sent */
1465 if (!stream->is_feed) {
1466 /* However it might be a status report from WMP! Lets log the data
1467 * as it might come in handy one day
1469 char *logline = 0;
1470 int client_id = 0;
1472 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1473 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1474 logline = p;
1475 break;
1477 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1478 client_id = strtol(p + 18, 0, 10);
1480 p = strchr(p, '\n');
1481 if (!p)
1482 break;
1484 p++;
1487 if (logline) {
1488 char *eol = strchr(logline, '\n');
1490 logline += 17;
1492 if (eol) {
1493 if (eol[-1] == '\r')
1494 eol--;
1495 http_log("%.*s\n", (int) (eol - logline), logline);
1496 c->suppress_log = 1;
1500 #ifdef DEBUG_WMP
1501 http_log("\nGot request:\n%s\n", c->buffer);
1502 #endif
1504 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1505 HTTPContext *wmpc;
1507 /* Now we have to find the client_id */
1508 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1509 if (wmpc->wmp_client_id == client_id)
1510 break;
1513 if (wmpc) {
1514 if (modify_current_stream(wmpc, ratebuf)) {
1515 wmpc->switch_pending = 1;
1520 snprintf(msg, sizeof(msg), "POST command not handled");
1521 c->stream = 0;
1522 goto send_error;
1524 if (http_start_receive_data(c) < 0) {
1525 snprintf(msg, sizeof(msg), "could not open feed");
1526 goto send_error;
1528 c->http_error = 0;
1529 c->state = HTTPSTATE_RECEIVE_DATA;
1530 return 0;
1533 #ifdef DEBUG_WMP
1534 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1535 http_log("\nGot request:\n%s\n", c->buffer);
1537 #endif
1539 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1540 goto send_stats;
1542 /* open input stream */
1543 if (open_input_stream(c, info) < 0) {
1544 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1545 goto send_error;
1548 /* prepare http header */
1549 q = c->buffer;
1550 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1551 mime_type = c->stream->fmt->mime_type;
1552 if (!mime_type)
1553 mime_type = "application/x-octet-stream";
1554 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1556 /* for asf, we need extra headers */
1557 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1558 /* Need to allocate a client id */
1560 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1562 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);
1564 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1565 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1567 /* prepare output buffer */
1568 c->http_error = 0;
1569 c->buffer_ptr = c->buffer;
1570 c->buffer_end = q;
1571 c->state = HTTPSTATE_SEND_HEADER;
1572 return 0;
1573 send_error:
1574 c->http_error = 404;
1575 q = c->buffer;
1576 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1577 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1578 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1579 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1580 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1581 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1582 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1584 /* prepare output buffer */
1585 c->buffer_ptr = c->buffer;
1586 c->buffer_end = q;
1587 c->state = HTTPSTATE_SEND_HEADER;
1588 return 0;
1589 send_stats:
1590 compute_stats(c);
1591 c->http_error = 200; /* horrible : we use this value to avoid
1592 going to the send data state */
1593 c->state = HTTPSTATE_SEND_HEADER;
1594 return 0;
1597 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1599 static const char *suffix = " kMGTP";
1600 const char *s;
1602 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1605 url_fprintf(pb, "%"PRId64"%c", count, *s);
1608 static void compute_stats(HTTPContext *c)
1610 HTTPContext *c1;
1611 FFStream *stream;
1612 char *p;
1613 time_t ti;
1614 int i, len;
1615 ByteIOContext pb1, *pb = &pb1;
1617 if (url_open_dyn_buf(pb) < 0) {
1618 /* XXX: return an error ? */
1619 c->buffer_ptr = c->buffer;
1620 c->buffer_end = c->buffer;
1621 return;
1624 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1625 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1626 url_fprintf(pb, "Pragma: no-cache\r\n");
1627 url_fprintf(pb, "\r\n");
1629 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1630 if (c->stream->feed_filename) {
1631 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1633 url_fprintf(pb, "</HEAD>\n<BODY>");
1634 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1635 /* format status */
1636 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1637 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1638 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");
1639 stream = first_stream;
1640 while (stream != NULL) {
1641 char sfilename[1024];
1642 char *eosf;
1644 if (stream->feed != stream) {
1645 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1646 eosf = sfilename + strlen(sfilename);
1647 if (eosf - sfilename >= 4) {
1648 if (strcmp(eosf - 4, ".asf") == 0) {
1649 strcpy(eosf - 4, ".asx");
1650 } else if (strcmp(eosf - 3, ".rm") == 0) {
1651 strcpy(eosf - 3, ".ram");
1652 } else if (stream->fmt == &rtp_muxer) {
1653 /* generate a sample RTSP director if
1654 unicast. Generate an SDP redirector if
1655 multicast */
1656 eosf = strrchr(sfilename, '.');
1657 if (!eosf)
1658 eosf = sfilename + strlen(sfilename);
1659 if (stream->is_multicast)
1660 strcpy(eosf, ".sdp");
1661 else
1662 strcpy(eosf, ".rtsp");
1666 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1667 sfilename, stream->filename);
1668 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1669 stream->conns_served);
1670 fmt_bytecount(pb, stream->bytes_served);
1671 switch(stream->stream_type) {
1672 case STREAM_TYPE_LIVE:
1674 int audio_bit_rate = 0;
1675 int video_bit_rate = 0;
1676 const char *audio_codec_name = "";
1677 const char *video_codec_name = "";
1678 const char *audio_codec_name_extra = "";
1679 const char *video_codec_name_extra = "";
1681 for(i=0;i<stream->nb_streams;i++) {
1682 AVStream *st = stream->streams[i];
1683 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1684 switch(st->codec->codec_type) {
1685 case CODEC_TYPE_AUDIO:
1686 audio_bit_rate += st->codec->bit_rate;
1687 if (codec) {
1688 if (*audio_codec_name)
1689 audio_codec_name_extra = "...";
1690 audio_codec_name = codec->name;
1692 break;
1693 case CODEC_TYPE_VIDEO:
1694 video_bit_rate += st->codec->bit_rate;
1695 if (codec) {
1696 if (*video_codec_name)
1697 video_codec_name_extra = "...";
1698 video_codec_name = codec->name;
1700 break;
1701 case CODEC_TYPE_DATA:
1702 video_bit_rate += st->codec->bit_rate;
1703 break;
1704 default:
1705 abort();
1708 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",
1709 stream->fmt->name,
1710 stream->bandwidth,
1711 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1712 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1713 if (stream->feed) {
1714 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1715 } else {
1716 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1718 url_fprintf(pb, "\n");
1720 break;
1721 default:
1722 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1723 break;
1726 stream = stream->next;
1728 url_fprintf(pb, "</TABLE>\n");
1730 stream = first_stream;
1731 while (stream != NULL) {
1732 if (stream->feed == stream) {
1733 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1734 if (stream->pid) {
1735 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1737 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1739 FILE *pid_stat;
1740 char ps_cmd[64];
1742 /* This is somewhat linux specific I guess */
1743 snprintf(ps_cmd, sizeof(ps_cmd),
1744 "ps -o \"%%cpu,cputime\" --no-headers %d",
1745 stream->pid);
1747 pid_stat = popen(ps_cmd, "r");
1748 if (pid_stat) {
1749 char cpuperc[10];
1750 char cpuused[64];
1752 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1753 cpuused) == 2) {
1754 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1755 cpuperc, cpuused);
1757 fclose(pid_stat);
1760 #endif
1762 url_fprintf(pb, "<p>");
1764 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");
1766 for (i = 0; i < stream->nb_streams; i++) {
1767 AVStream *st = stream->streams[i];
1768 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1769 const char *type = "unknown";
1770 char parameters[64];
1772 parameters[0] = 0;
1774 switch(st->codec->codec_type) {
1775 case CODEC_TYPE_AUDIO:
1776 type = "audio";
1777 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1778 break;
1779 case CODEC_TYPE_VIDEO:
1780 type = "video";
1781 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1782 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1783 break;
1784 default:
1785 abort();
1787 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1788 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1790 url_fprintf(pb, "</table>\n");
1793 stream = stream->next;
1796 #if 0
1798 float avg;
1799 AVCodecContext *enc;
1800 char buf[1024];
1802 /* feed status */
1803 stream = first_feed;
1804 while (stream != NULL) {
1805 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1806 url_fprintf(pb, "<TABLE>\n");
1807 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1808 for(i=0;i<stream->nb_streams;i++) {
1809 AVStream *st = stream->streams[i];
1810 FeedData *fdata = st->priv_data;
1811 enc = st->codec;
1813 avcodec_string(buf, sizeof(buf), enc);
1814 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1815 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1816 avg /= enc->frame_size;
1817 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1818 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1820 url_fprintf(pb, "</TABLE>\n");
1821 stream = stream->next_feed;
1824 #endif
1826 /* connection status */
1827 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1829 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1830 nb_connections, nb_max_connections);
1832 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1833 current_bandwidth, max_bandwidth);
1835 url_fprintf(pb, "<TABLE>\n");
1836 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");
1837 c1 = first_http_ctx;
1838 i = 0;
1839 while (c1 != NULL) {
1840 int bitrate;
1841 int j;
1843 bitrate = 0;
1844 if (c1->stream) {
1845 for (j = 0; j < c1->stream->nb_streams; j++) {
1846 if (!c1->stream->feed) {
1847 bitrate += c1->stream->streams[j]->codec->bit_rate;
1848 } else {
1849 if (c1->feed_streams[j] >= 0) {
1850 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1856 i++;
1857 p = inet_ntoa(c1->from_addr.sin_addr);
1858 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1860 c1->stream ? c1->stream->filename : "",
1861 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1863 c1->protocol,
1864 http_state[c1->state]);
1865 fmt_bytecount(pb, bitrate);
1866 url_fprintf(pb, "<td align=right>");
1867 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1868 url_fprintf(pb, "<td align=right>");
1869 fmt_bytecount(pb, c1->data_count);
1870 url_fprintf(pb, "\n");
1871 c1 = c1->next;
1873 url_fprintf(pb, "</TABLE>\n");
1875 /* date */
1876 ti = time(NULL);
1877 p = ctime(&ti);
1878 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1879 url_fprintf(pb, "</BODY>\n</HTML>\n");
1881 len = url_close_dyn_buf(pb, &c->pb_buffer);
1882 c->buffer_ptr = c->pb_buffer;
1883 c->buffer_end = c->pb_buffer + len;
1886 /* check if the parser needs to be opened for stream i */
1887 static void open_parser(AVFormatContext *s, int i)
1889 AVStream *st = s->streams[i];
1890 AVCodec *codec;
1892 if (!st->codec->codec) {
1893 codec = avcodec_find_decoder(st->codec->codec_id);
1894 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1895 st->codec->parse_only = 1;
1896 if (avcodec_open(st->codec, codec) < 0) {
1897 st->codec->parse_only = 0;
1903 static int open_input_stream(HTTPContext *c, const char *info)
1905 char buf[128];
1906 char input_filename[1024];
1907 AVFormatContext *s;
1908 int buf_size, i;
1909 int64_t stream_pos;
1911 /* find file name */
1912 if (c->stream->feed) {
1913 strcpy(input_filename, c->stream->feed->feed_filename);
1914 buf_size = FFM_PACKET_SIZE;
1915 /* compute position (absolute time) */
1916 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1917 stream_pos = parse_date(buf, 0);
1918 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1919 int prebuffer = strtol(buf, 0, 10);
1920 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1921 } else {
1922 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1924 } else {
1925 strcpy(input_filename, c->stream->feed_filename);
1926 buf_size = 0;
1927 /* compute position (relative time) */
1928 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1929 stream_pos = parse_date(buf, 1);
1930 } else {
1931 stream_pos = 0;
1934 if (input_filename[0] == '\0')
1935 return -1;
1937 #if 0
1938 { time_t when = stream_pos / 1000000;
1939 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1941 #endif
1943 /* open stream */
1944 if (av_open_input_file(&s, input_filename, c->stream->ifmt,
1945 buf_size, c->stream->ap_in) < 0) {
1946 http_log("%s not found", input_filename);
1947 return -1;
1949 c->fmt_in = s;
1951 /* open each parser */
1952 for(i=0;i<s->nb_streams;i++)
1953 open_parser(s, i);
1955 /* choose stream as clock source (we favorize video stream if
1956 present) for packet sending */
1957 c->pts_stream_index = 0;
1958 for(i=0;i<c->stream->nb_streams;i++) {
1959 if (c->pts_stream_index == 0 &&
1960 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1961 c->pts_stream_index = i;
1965 #if 1
1966 if (c->fmt_in->iformat->read_seek) {
1967 c->fmt_in->iformat->read_seek(c->fmt_in, 0, stream_pos, 0);
1969 #endif
1970 /* set the start time (needed for maxtime and RTP packet timing) */
1971 c->start_time = cur_time;
1972 c->first_pts = AV_NOPTS_VALUE;
1973 return 0;
1976 /* return the server clock (in us) */
1977 static int64_t get_server_clock(HTTPContext *c)
1979 /* compute current pts value from system time */
1980 return (cur_time - c->start_time) * 1000;
1983 /* return the estimated time at which the current packet must be sent
1984 (in us) */
1985 static int64_t get_packet_send_clock(HTTPContext *c)
1987 int bytes_left, bytes_sent, frame_bytes;
1989 frame_bytes = c->cur_frame_bytes;
1990 if (frame_bytes <= 0) {
1991 return c->cur_pts;
1992 } else {
1993 bytes_left = c->buffer_end - c->buffer_ptr;
1994 bytes_sent = frame_bytes - bytes_left;
1995 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2000 static int http_prepare_data(HTTPContext *c)
2002 int i, len, ret;
2003 AVFormatContext *ctx;
2005 av_freep(&c->pb_buffer);
2006 switch(c->state) {
2007 case HTTPSTATE_SEND_DATA_HEADER:
2008 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2009 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2010 sizeof(c->fmt_ctx.author));
2011 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2012 sizeof(c->fmt_ctx.comment));
2013 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2014 sizeof(c->fmt_ctx.copyright));
2015 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2016 sizeof(c->fmt_ctx.title));
2018 /* open output stream by using specified codecs */
2019 c->fmt_ctx.oformat = c->stream->fmt;
2020 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2021 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2022 AVStream *st;
2023 AVStream *src;
2024 st = av_mallocz(sizeof(AVStream));
2025 st->codec= avcodec_alloc_context();
2026 c->fmt_ctx.streams[i] = st;
2027 /* if file or feed, then just take streams from FFStream struct */
2028 if (!c->stream->feed ||
2029 c->stream->feed == c->stream)
2030 src = c->stream->streams[i];
2031 else
2032 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2034 *st = *src;
2035 st->priv_data = 0;
2036 st->codec->frame_number = 0; /* XXX: should be done in
2037 AVStream, not in codec */
2038 /* I'm pretty sure that this is not correct...
2039 * However, without it, we crash
2041 st->codec->coded_frame = &dummy_frame;
2043 c->got_key_frame = 0;
2045 /* prepare header and save header data in a stream */
2046 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2047 /* XXX: potential leak */
2048 return -1;
2050 c->fmt_ctx.pb.is_streamed = 1;
2052 av_set_parameters(&c->fmt_ctx, NULL);
2053 if (av_write_header(&c->fmt_ctx) < 0)
2054 return -1;
2056 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2057 c->buffer_ptr = c->pb_buffer;
2058 c->buffer_end = c->pb_buffer + len;
2060 c->state = HTTPSTATE_SEND_DATA;
2061 c->last_packet_sent = 0;
2062 break;
2063 case HTTPSTATE_SEND_DATA:
2064 /* find a new packet */
2066 AVPacket pkt;
2068 /* read a packet from the input stream */
2069 if (c->stream->feed) {
2070 ffm_set_write_index(c->fmt_in,
2071 c->stream->feed->feed_write_index,
2072 c->stream->feed->feed_size);
2075 if (c->stream->max_time &&
2076 c->stream->max_time + c->start_time - cur_time < 0) {
2077 /* We have timed out */
2078 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2079 } else {
2080 redo:
2081 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2082 if (c->stream->feed && c->stream->feed->feed_opened) {
2083 /* if coming from feed, it means we reached the end of the
2084 ffm file, so must wait for more data */
2085 c->state = HTTPSTATE_WAIT_FEED;
2086 return 1; /* state changed */
2087 } else {
2088 if (c->stream->loop) {
2089 av_close_input_file(c->fmt_in);
2090 c->fmt_in = NULL;
2091 if (open_input_stream(c, "") < 0)
2092 goto no_loop;
2093 goto redo;
2094 } else {
2095 no_loop:
2096 /* must send trailer now because eof or error */
2097 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2100 } else {
2101 /* update first pts if needed */
2102 if (c->first_pts == AV_NOPTS_VALUE) {
2103 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2104 c->start_time = cur_time;
2106 /* send it to the appropriate stream */
2107 if (c->stream->feed) {
2108 /* if coming from a feed, select the right stream */
2109 if (c->switch_pending) {
2110 c->switch_pending = 0;
2111 for(i=0;i<c->stream->nb_streams;i++) {
2112 if (c->switch_feed_streams[i] == pkt.stream_index) {
2113 if (pkt.flags & PKT_FLAG_KEY) {
2114 do_switch_stream(c, i);
2117 if (c->switch_feed_streams[i] >= 0) {
2118 c->switch_pending = 1;
2122 for(i=0;i<c->stream->nb_streams;i++) {
2123 if (c->feed_streams[i] == pkt.stream_index) {
2124 pkt.stream_index = i;
2125 if (pkt.flags & PKT_FLAG_KEY) {
2126 c->got_key_frame |= 1 << i;
2128 /* See if we have all the key frames, then
2129 * we start to send. This logic is not quite
2130 * right, but it works for the case of a
2131 * single video stream with one or more
2132 * audio streams (for which every frame is
2133 * typically a key frame).
2135 if (!c->stream->send_on_key ||
2136 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2137 goto send_it;
2141 } else {
2142 AVCodecContext *codec;
2144 send_it:
2145 /* specific handling for RTP: we use several
2146 output stream (one for each RTP
2147 connection). XXX: need more abstract handling */
2148 if (c->is_packetized) {
2149 AVStream *st;
2150 /* compute send time and duration */
2151 st = c->fmt_in->streams[pkt.stream_index];
2152 c->cur_pts = av_rescale_q(pkt.dts, st->time_base, AV_TIME_BASE_Q);
2153 if (st->start_time != AV_NOPTS_VALUE)
2154 c->cur_pts -= av_rescale_q(st->start_time, st->time_base, AV_TIME_BASE_Q);
2155 c->cur_frame_duration = av_rescale_q(pkt.duration, st->time_base, AV_TIME_BASE_Q);
2156 #if 0
2157 printf("index=%d pts=%0.3f duration=%0.6f\n",
2158 pkt.stream_index,
2159 (double)c->cur_pts /
2160 AV_TIME_BASE,
2161 (double)c->cur_frame_duration /
2162 AV_TIME_BASE);
2163 #endif
2164 /* find RTP context */
2165 c->packet_stream_index = pkt.stream_index;
2166 ctx = c->rtp_ctx[c->packet_stream_index];
2167 if(!ctx) {
2168 av_free_packet(&pkt);
2169 break;
2171 codec = ctx->streams[0]->codec;
2172 /* only one stream per RTP connection */
2173 pkt.stream_index = 0;
2174 } else {
2175 ctx = &c->fmt_ctx;
2176 /* Fudge here */
2177 codec = ctx->streams[pkt.stream_index]->codec;
2180 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2181 if (c->is_packetized) {
2182 int max_packet_size;
2183 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2184 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2185 else
2186 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2187 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2188 } else {
2189 ret = url_open_dyn_buf(&ctx->pb);
2191 if (ret < 0) {
2192 /* XXX: potential leak */
2193 return -1;
2195 if (pkt.dts != AV_NOPTS_VALUE)
2196 pkt.dts = av_rescale_q(pkt.dts,
2197 c->fmt_in->streams[pkt.stream_index]->time_base,
2198 ctx->streams[pkt.stream_index]->time_base);
2199 if (pkt.pts != AV_NOPTS_VALUE)
2200 pkt.pts = av_rescale_q(pkt.pts,
2201 c->fmt_in->streams[pkt.stream_index]->time_base,
2202 ctx->streams[pkt.stream_index]->time_base);
2203 if (av_write_frame(ctx, &pkt)) {
2204 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2207 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2208 c->cur_frame_bytes = len;
2209 c->buffer_ptr = c->pb_buffer;
2210 c->buffer_end = c->pb_buffer + len;
2212 codec->frame_number++;
2213 if (len == 0)
2214 goto redo;
2216 av_free_packet(&pkt);
2220 break;
2221 default:
2222 case HTTPSTATE_SEND_DATA_TRAILER:
2223 /* last packet test ? */
2224 if (c->last_packet_sent || c->is_packetized)
2225 return -1;
2226 ctx = &c->fmt_ctx;
2227 /* prepare header */
2228 if (url_open_dyn_buf(&ctx->pb) < 0) {
2229 /* XXX: potential leak */
2230 return -1;
2232 av_write_trailer(ctx);
2233 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2234 c->buffer_ptr = c->pb_buffer;
2235 c->buffer_end = c->pb_buffer + len;
2237 c->last_packet_sent = 1;
2238 break;
2240 return 0;
2243 /* should convert the format at the same time */
2244 /* send data starting at c->buffer_ptr to the output connection
2245 (either UDP or TCP connection) */
2246 static int http_send_data(HTTPContext *c)
2248 int len, ret;
2250 for(;;) {
2251 if (c->buffer_ptr >= c->buffer_end) {
2252 ret = http_prepare_data(c);
2253 if (ret < 0)
2254 return -1;
2255 else if (ret != 0) {
2256 /* state change requested */
2257 break;
2259 } else {
2260 if (c->is_packetized) {
2261 /* RTP data output */
2262 len = c->buffer_end - c->buffer_ptr;
2263 if (len < 4) {
2264 /* fail safe - should never happen */
2265 fail1:
2266 c->buffer_ptr = c->buffer_end;
2267 return 0;
2269 len = (c->buffer_ptr[0] << 24) |
2270 (c->buffer_ptr[1] << 16) |
2271 (c->buffer_ptr[2] << 8) |
2272 (c->buffer_ptr[3]);
2273 if (len > (c->buffer_end - c->buffer_ptr))
2274 goto fail1;
2275 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2276 /* nothing to send yet: we can wait */
2277 return 0;
2280 c->data_count += len;
2281 update_datarate(&c->datarate, c->data_count);
2282 if (c->stream)
2283 c->stream->bytes_served += len;
2285 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2286 /* RTP packets are sent inside the RTSP TCP connection */
2287 ByteIOContext pb1, *pb = &pb1;
2288 int interleaved_index, size;
2289 uint8_t header[4];
2290 HTTPContext *rtsp_c;
2292 rtsp_c = c->rtsp_c;
2293 /* if no RTSP connection left, error */
2294 if (!rtsp_c)
2295 return -1;
2296 /* if already sending something, then wait. */
2297 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST) {
2298 break;
2300 if (url_open_dyn_buf(pb) < 0)
2301 goto fail1;
2302 interleaved_index = c->packet_stream_index * 2;
2303 /* RTCP packets are sent at odd indexes */
2304 if (c->buffer_ptr[1] == 200)
2305 interleaved_index++;
2306 /* write RTSP TCP header */
2307 header[0] = '$';
2308 header[1] = interleaved_index;
2309 header[2] = len >> 8;
2310 header[3] = len;
2311 put_buffer(pb, header, 4);
2312 /* write RTP packet data */
2313 c->buffer_ptr += 4;
2314 put_buffer(pb, c->buffer_ptr, len);
2315 size = url_close_dyn_buf(pb, &c->packet_buffer);
2316 /* prepare asynchronous TCP sending */
2317 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2318 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2319 c->buffer_ptr += len;
2321 /* send everything we can NOW */
2322 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2323 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2324 if (len > 0) {
2325 rtsp_c->packet_buffer_ptr += len;
2327 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2328 /* if we could not send all the data, we will
2329 send it later, so a new state is needed to
2330 "lock" the RTSP TCP connection */
2331 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2332 break;
2333 } else {
2334 /* all data has been sent */
2335 av_freep(&c->packet_buffer);
2337 } else {
2338 /* send RTP packet directly in UDP */
2339 c->buffer_ptr += 4;
2340 url_write(c->rtp_handles[c->packet_stream_index],
2341 c->buffer_ptr, len);
2342 c->buffer_ptr += len;
2343 /* here we continue as we can send several packets per 10 ms slot */
2345 } else {
2346 /* TCP data output */
2347 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2348 if (len < 0) {
2349 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2350 ff_neterrno() != FF_NETERROR(EINTR)) {
2351 /* error : close connection */
2352 return -1;
2353 } else {
2354 return 0;
2356 } else {
2357 c->buffer_ptr += len;
2359 c->data_count += len;
2360 update_datarate(&c->datarate, c->data_count);
2361 if (c->stream)
2362 c->stream->bytes_served += len;
2363 break;
2366 } /* for(;;) */
2367 return 0;
2370 static int http_start_receive_data(HTTPContext *c)
2372 int fd;
2374 if (c->stream->feed_opened)
2375 return -1;
2377 /* Don't permit writing to this one */
2378 if (c->stream->readonly)
2379 return -1;
2381 /* open feed */
2382 fd = open(c->stream->feed_filename, O_RDWR);
2383 if (fd < 0)
2384 return -1;
2385 c->feed_fd = fd;
2387 c->stream->feed_write_index = ffm_read_write_index(fd);
2388 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2389 lseek(fd, 0, SEEK_SET);
2391 /* init buffer input */
2392 c->buffer_ptr = c->buffer;
2393 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2394 c->stream->feed_opened = 1;
2395 return 0;
2398 static int http_receive_data(HTTPContext *c)
2400 HTTPContext *c1;
2402 if (c->buffer_end > c->buffer_ptr) {
2403 int len;
2405 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2406 if (len < 0) {
2407 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2408 ff_neterrno() != FF_NETERROR(EINTR)) {
2409 /* error : close connection */
2410 goto fail;
2412 } else if (len == 0) {
2413 /* end of connection : close it */
2414 goto fail;
2415 } else {
2416 c->buffer_ptr += len;
2417 c->data_count += len;
2418 update_datarate(&c->datarate, c->data_count);
2422 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2423 if (c->buffer[0] != 'f' ||
2424 c->buffer[1] != 'm') {
2425 http_log("Feed stream has become desynchronized -- disconnecting\n");
2426 goto fail;
2430 if (c->buffer_ptr >= c->buffer_end) {
2431 FFStream *feed = c->stream;
2432 /* a packet has been received : write it in the store, except
2433 if header */
2434 if (c->data_count > FFM_PACKET_SIZE) {
2436 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2437 /* XXX: use llseek or url_seek */
2438 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2439 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2441 feed->feed_write_index += FFM_PACKET_SIZE;
2442 /* update file size */
2443 if (feed->feed_write_index > c->stream->feed_size)
2444 feed->feed_size = feed->feed_write_index;
2446 /* handle wrap around if max file size reached */
2447 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2448 feed->feed_write_index = FFM_PACKET_SIZE;
2450 /* write index */
2451 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2453 /* wake up any waiting connections */
2454 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2455 if (c1->state == HTTPSTATE_WAIT_FEED &&
2456 c1->stream->feed == c->stream->feed) {
2457 c1->state = HTTPSTATE_SEND_DATA;
2460 } else {
2461 /* We have a header in our hands that contains useful data */
2462 AVFormatContext s;
2463 AVInputFormat *fmt_in;
2464 ByteIOContext *pb = &s.pb;
2465 int i;
2467 memset(&s, 0, sizeof(s));
2469 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2470 pb->buf_end = c->buffer_end; /* ?? */
2471 pb->is_streamed = 1;
2473 /* use feed output format name to find corresponding input format */
2474 fmt_in = av_find_input_format(feed->fmt->name);
2475 if (!fmt_in)
2476 goto fail;
2478 if (fmt_in->priv_data_size > 0) {
2479 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2480 if (!s.priv_data)
2481 goto fail;
2482 } else
2483 s.priv_data = NULL;
2485 if (fmt_in->read_header(&s, 0) < 0) {
2486 av_freep(&s.priv_data);
2487 goto fail;
2490 /* Now we have the actual streams */
2491 if (s.nb_streams != feed->nb_streams) {
2492 av_freep(&s.priv_data);
2493 goto fail;
2495 for (i = 0; i < s.nb_streams; i++) {
2496 memcpy(feed->streams[i]->codec,
2497 s.streams[i]->codec, sizeof(AVCodecContext));
2499 av_freep(&s.priv_data);
2501 c->buffer_ptr = c->buffer;
2504 return 0;
2505 fail:
2506 c->stream->feed_opened = 0;
2507 close(c->feed_fd);
2508 return -1;
2511 /********************************************************************/
2512 /* RTSP handling */
2514 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2516 const char *str;
2517 time_t ti;
2518 char *p;
2519 char buf2[32];
2521 switch(error_number) {
2522 case RTSP_STATUS_OK:
2523 str = "OK";
2524 break;
2525 case RTSP_STATUS_METHOD:
2526 str = "Method Not Allowed";
2527 break;
2528 case RTSP_STATUS_BANDWIDTH:
2529 str = "Not Enough Bandwidth";
2530 break;
2531 case RTSP_STATUS_SESSION:
2532 str = "Session Not Found";
2533 break;
2534 case RTSP_STATUS_STATE:
2535 str = "Method Not Valid in This State";
2536 break;
2537 case RTSP_STATUS_AGGREGATE:
2538 str = "Aggregate operation not allowed";
2539 break;
2540 case RTSP_STATUS_ONLY_AGGREGATE:
2541 str = "Only aggregate operation allowed";
2542 break;
2543 case RTSP_STATUS_TRANSPORT:
2544 str = "Unsupported transport";
2545 break;
2546 case RTSP_STATUS_INTERNAL:
2547 str = "Internal Server Error";
2548 break;
2549 case RTSP_STATUS_SERVICE:
2550 str = "Service Unavailable";
2551 break;
2552 case RTSP_STATUS_VERSION:
2553 str = "RTSP Version not supported";
2554 break;
2555 default:
2556 str = "Unknown Error";
2557 break;
2560 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2561 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2563 /* output GMT time */
2564 ti = time(NULL);
2565 p = ctime(&ti);
2566 strcpy(buf2, p);
2567 p = buf2 + strlen(p) - 1;
2568 if (*p == '\n')
2569 *p = '\0';
2570 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2573 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2575 rtsp_reply_header(c, error_number);
2576 url_fprintf(c->pb, "\r\n");
2579 static int rtsp_parse_request(HTTPContext *c)
2581 const char *p, *p1, *p2;
2582 char cmd[32];
2583 char url[1024];
2584 char protocol[32];
2585 char line[1024];
2586 ByteIOContext pb1;
2587 int len;
2588 RTSPHeader header1, *header = &header1;
2590 c->buffer_ptr[0] = '\0';
2591 p = c->buffer;
2593 get_word(cmd, sizeof(cmd), &p);
2594 get_word(url, sizeof(url), &p);
2595 get_word(protocol, sizeof(protocol), &p);
2597 av_strlcpy(c->method, cmd, sizeof(c->method));
2598 av_strlcpy(c->url, url, sizeof(c->url));
2599 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2601 c->pb = &pb1;
2602 if (url_open_dyn_buf(c->pb) < 0) {
2603 /* XXX: cannot do more */
2604 c->pb = NULL; /* safety */
2605 return -1;
2608 /* check version name */
2609 if (strcmp(protocol, "RTSP/1.0") != 0) {
2610 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2611 goto the_end;
2614 /* parse each header line */
2615 memset(header, 0, sizeof(RTSPHeader));
2616 /* skip to next line */
2617 while (*p != '\n' && *p != '\0')
2618 p++;
2619 if (*p == '\n')
2620 p++;
2621 while (*p != '\0') {
2622 p1 = strchr(p, '\n');
2623 if (!p1)
2624 break;
2625 p2 = p1;
2626 if (p2 > p && p2[-1] == '\r')
2627 p2--;
2628 /* skip empty line */
2629 if (p2 == p)
2630 break;
2631 len = p2 - p;
2632 if (len > sizeof(line) - 1)
2633 len = sizeof(line) - 1;
2634 memcpy(line, p, len);
2635 line[len] = '\0';
2636 rtsp_parse_line(header, line);
2637 p = p1 + 1;
2640 /* handle sequence number */
2641 c->seq = header->seq;
2643 if (!strcmp(cmd, "DESCRIBE")) {
2644 rtsp_cmd_describe(c, url);
2645 } else if (!strcmp(cmd, "OPTIONS")) {
2646 rtsp_cmd_options(c, url);
2647 } else if (!strcmp(cmd, "SETUP")) {
2648 rtsp_cmd_setup(c, url, header);
2649 } else if (!strcmp(cmd, "PLAY")) {
2650 rtsp_cmd_play(c, url, header);
2651 } else if (!strcmp(cmd, "PAUSE")) {
2652 rtsp_cmd_pause(c, url, header);
2653 } else if (!strcmp(cmd, "TEARDOWN")) {
2654 rtsp_cmd_teardown(c, url, header);
2655 } else {
2656 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2658 the_end:
2659 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2660 c->pb = NULL; /* safety */
2661 if (len < 0) {
2662 /* XXX: cannot do more */
2663 return -1;
2665 c->buffer_ptr = c->pb_buffer;
2666 c->buffer_end = c->pb_buffer + len;
2667 c->state = RTSPSTATE_SEND_REPLY;
2668 return 0;
2671 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2672 AVFormatContext */
2673 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2674 struct in_addr my_ip)
2676 ByteIOContext pb1, *pb = &pb1;
2677 int i, payload_type, port, private_payload_type, j;
2678 const char *ipstr, *title, *mediatype;
2679 AVStream *st;
2681 if (url_open_dyn_buf(pb) < 0)
2682 return -1;
2684 /* general media info */
2686 url_fprintf(pb, "v=0\n");
2687 ipstr = inet_ntoa(my_ip);
2688 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2689 title = stream->title;
2690 if (title[0] == '\0')
2691 title = "No Title";
2692 url_fprintf(pb, "s=%s\n", title);
2693 if (stream->comment[0] != '\0')
2694 url_fprintf(pb, "i=%s\n", stream->comment);
2695 if (stream->is_multicast) {
2696 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2698 /* for each stream, we output the necessary info */
2699 private_payload_type = RTP_PT_PRIVATE;
2700 for(i = 0; i < stream->nb_streams; i++) {
2701 st = stream->streams[i];
2702 if (st->codec->codec_id == CODEC_ID_MPEG2TS) {
2703 mediatype = "video";
2704 } else {
2705 switch(st->codec->codec_type) {
2706 case CODEC_TYPE_AUDIO:
2707 mediatype = "audio";
2708 break;
2709 case CODEC_TYPE_VIDEO:
2710 mediatype = "video";
2711 break;
2712 default:
2713 mediatype = "application";
2714 break;
2717 /* NOTE: the port indication is not correct in case of
2718 unicast. It is not an issue because RTSP gives it */
2719 payload_type = rtp_get_payload_type(st->codec);
2720 if (payload_type < 0)
2721 payload_type = private_payload_type++;
2722 if (stream->is_multicast) {
2723 port = stream->multicast_port + 2 * i;
2724 } else {
2725 port = 0;
2727 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2728 mediatype, port, payload_type);
2729 if (payload_type >= RTP_PT_PRIVATE) {
2730 /* for private payload type, we need to give more info */
2731 switch(st->codec->codec_id) {
2732 case CODEC_ID_MPEG4:
2734 uint8_t *data;
2735 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2736 payload_type, 90000);
2737 /* we must also add the mpeg4 header */
2738 data = st->codec->extradata;
2739 if (data) {
2740 url_fprintf(pb, "a=fmtp:%d config=", payload_type);
2741 for(j=0;j<st->codec->extradata_size;j++) {
2742 url_fprintf(pb, "%02x", data[j]);
2744 url_fprintf(pb, "\n");
2747 break;
2748 default:
2749 /* XXX: add other codecs ? */
2750 goto fail;
2753 url_fprintf(pb, "a=control:streamid=%d\n", i);
2755 return url_close_dyn_buf(pb, pbuffer);
2756 fail:
2757 url_close_dyn_buf(pb, pbuffer);
2758 av_free(*pbuffer);
2759 return -1;
2762 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2764 // rtsp_reply_header(c, RTSP_STATUS_OK);
2765 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2766 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2767 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2768 url_fprintf(c->pb, "\r\n");
2771 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2773 FFStream *stream;
2774 char path1[1024];
2775 const char *path;
2776 uint8_t *content;
2777 int content_length, len;
2778 struct sockaddr_in my_addr;
2780 /* find which url is asked */
2781 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2782 path = path1;
2783 if (*path == '/')
2784 path++;
2786 for(stream = first_stream; stream != NULL; stream = stream->next) {
2787 if (!stream->is_feed && stream->fmt == &rtp_muxer &&
2788 !strcmp(path, stream->filename)) {
2789 goto found;
2792 /* no stream found */
2793 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2794 return;
2796 found:
2797 /* prepare the media description in sdp format */
2799 /* get the host IP */
2800 len = sizeof(my_addr);
2801 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2802 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2803 if (content_length < 0) {
2804 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2805 return;
2807 rtsp_reply_header(c, RTSP_STATUS_OK);
2808 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2809 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2810 url_fprintf(c->pb, "\r\n");
2811 put_buffer(c->pb, content, content_length);
2814 static HTTPContext *find_rtp_session(const char *session_id)
2816 HTTPContext *c;
2818 if (session_id[0] == '\0')
2819 return NULL;
2821 for(c = first_http_ctx; c != NULL; c = c->next) {
2822 if (!strcmp(c->session_id, session_id))
2823 return c;
2825 return NULL;
2828 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2830 RTSPTransportField *th;
2831 int i;
2833 for(i=0;i<h->nb_transports;i++) {
2834 th = &h->transports[i];
2835 if (th->protocol == protocol)
2836 return th;
2838 return NULL;
2841 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2842 RTSPHeader *h)
2844 FFStream *stream;
2845 int stream_index, port;
2846 char buf[1024];
2847 char path1[1024];
2848 const char *path;
2849 HTTPContext *rtp_c;
2850 RTSPTransportField *th;
2851 struct sockaddr_in dest_addr;
2852 RTSPActionServerSetup setup;
2854 /* find which url is asked */
2855 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2856 path = path1;
2857 if (*path == '/')
2858 path++;
2860 /* now check each stream */
2861 for(stream = first_stream; stream != NULL; stream = stream->next) {
2862 if (!stream->is_feed && stream->fmt == &rtp_muxer) {
2863 /* accept aggregate filenames only if single stream */
2864 if (!strcmp(path, stream->filename)) {
2865 if (stream->nb_streams != 1) {
2866 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2867 return;
2869 stream_index = 0;
2870 goto found;
2873 for(stream_index = 0; stream_index < stream->nb_streams;
2874 stream_index++) {
2875 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2876 stream->filename, stream_index);
2877 if (!strcmp(path, buf))
2878 goto found;
2882 /* no stream found */
2883 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2884 return;
2885 found:
2887 /* generate session id if needed */
2888 if (h->session_id[0] == '\0') {
2889 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2890 av_random(&random_state), av_random(&random_state));
2893 /* find rtp session, and create it if none found */
2894 rtp_c = find_rtp_session(h->session_id);
2895 if (!rtp_c) {
2896 /* always prefer UDP */
2897 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2898 if (!th) {
2899 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2900 if (!th) {
2901 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2902 return;
2906 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2907 th->protocol);
2908 if (!rtp_c) {
2909 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2910 return;
2913 /* open input stream */
2914 if (open_input_stream(rtp_c, "") < 0) {
2915 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2916 return;
2920 /* test if stream is OK (test needed because several SETUP needs
2921 to be done for a given file) */
2922 if (rtp_c->stream != stream) {
2923 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2924 return;
2927 /* test if stream is already set up */
2928 if (rtp_c->rtp_ctx[stream_index]) {
2929 rtsp_reply_error(c, RTSP_STATUS_STATE);
2930 return;
2933 /* check transport */
2934 th = find_transport(h, rtp_c->rtp_protocol);
2935 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2936 th->client_port_min <= 0)) {
2937 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2938 return;
2941 /* setup default options */
2942 setup.transport_option[0] = '\0';
2943 dest_addr = rtp_c->from_addr;
2944 dest_addr.sin_port = htons(th->client_port_min);
2946 /* setup stream */
2947 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2948 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2949 return;
2952 /* now everything is OK, so we can send the connection parameters */
2953 rtsp_reply_header(c, RTSP_STATUS_OK);
2954 /* session ID */
2955 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2957 switch(rtp_c->rtp_protocol) {
2958 case RTSP_PROTOCOL_RTP_UDP:
2959 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2960 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2961 "client_port=%d-%d;server_port=%d-%d",
2962 th->client_port_min, th->client_port_min + 1,
2963 port, port + 1);
2964 break;
2965 case RTSP_PROTOCOL_RTP_TCP:
2966 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2967 stream_index * 2, stream_index * 2 + 1);
2968 break;
2969 default:
2970 break;
2972 if (setup.transport_option[0] != '\0') {
2973 url_fprintf(c->pb, ";%s", setup.transport_option);
2975 url_fprintf(c->pb, "\r\n");
2978 url_fprintf(c->pb, "\r\n");
2982 /* find an rtp connection by using the session ID. Check consistency
2983 with filename */
2984 static HTTPContext *find_rtp_session_with_url(const char *url,
2985 const char *session_id)
2987 HTTPContext *rtp_c;
2988 char path1[1024];
2989 const char *path;
2990 char buf[1024];
2991 int s;
2993 rtp_c = find_rtp_session(session_id);
2994 if (!rtp_c)
2995 return NULL;
2997 /* find which url is asked */
2998 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2999 path = path1;
3000 if (*path == '/')
3001 path++;
3002 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
3003 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
3004 snprintf(buf, sizeof(buf), "%s/streamid=%d",
3005 rtp_c->stream->filename, s);
3006 if(!strncmp(path, buf, sizeof(buf))) {
3007 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
3008 return rtp_c;
3011 return NULL;
3014 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
3016 HTTPContext *rtp_c;
3018 rtp_c = find_rtp_session_with_url(url, h->session_id);
3019 if (!rtp_c) {
3020 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3021 return;
3024 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3025 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3026 rtp_c->state != HTTPSTATE_READY) {
3027 rtsp_reply_error(c, RTSP_STATUS_STATE);
3028 return;
3031 #if 0
3032 /* XXX: seek in stream */
3033 if (h->range_start != AV_NOPTS_VALUE) {
3034 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3035 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3037 #endif
3039 rtp_c->state = HTTPSTATE_SEND_DATA;
3041 /* now everything is OK, so we can send the connection parameters */
3042 rtsp_reply_header(c, RTSP_STATUS_OK);
3043 /* session ID */
3044 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3045 url_fprintf(c->pb, "\r\n");
3048 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3050 HTTPContext *rtp_c;
3052 rtp_c = find_rtp_session_with_url(url, h->session_id);
3053 if (!rtp_c) {
3054 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3055 return;
3058 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3059 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3060 rtsp_reply_error(c, RTSP_STATUS_STATE);
3061 return;
3064 rtp_c->state = HTTPSTATE_READY;
3065 rtp_c->first_pts = AV_NOPTS_VALUE;
3066 /* now everything is OK, so we can send the connection parameters */
3067 rtsp_reply_header(c, RTSP_STATUS_OK);
3068 /* session ID */
3069 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3070 url_fprintf(c->pb, "\r\n");
3073 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3075 HTTPContext *rtp_c;
3076 char session_id[32];
3078 rtp_c = find_rtp_session_with_url(url, h->session_id);
3079 if (!rtp_c) {
3080 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3081 return;
3084 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3086 /* abort the session */
3087 close_connection(rtp_c);
3089 /* now everything is OK, so we can send the connection parameters */
3090 rtsp_reply_header(c, RTSP_STATUS_OK);
3091 /* session ID */
3092 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3093 url_fprintf(c->pb, "\r\n");
3097 /********************************************************************/
3098 /* RTP handling */
3100 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3101 FFStream *stream, const char *session_id,
3102 enum RTSPProtocol rtp_protocol)
3104 HTTPContext *c = NULL;
3105 const char *proto_str;
3107 /* XXX: should output a warning page when coming
3108 close to the connection limit */
3109 if (nb_connections >= nb_max_connections)
3110 goto fail;
3112 /* add a new connection */
3113 c = av_mallocz(sizeof(HTTPContext));
3114 if (!c)
3115 goto fail;
3117 c->fd = -1;
3118 c->poll_entry = NULL;
3119 c->from_addr = *from_addr;
3120 c->buffer_size = IOBUFFER_INIT_SIZE;
3121 c->buffer = av_malloc(c->buffer_size);
3122 if (!c->buffer)
3123 goto fail;
3124 nb_connections++;
3125 c->stream = stream;
3126 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3127 c->state = HTTPSTATE_READY;
3128 c->is_packetized = 1;
3129 c->rtp_protocol = rtp_protocol;
3131 /* protocol is shown in statistics */
3132 switch(c->rtp_protocol) {
3133 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3134 proto_str = "MCAST";
3135 break;
3136 case RTSP_PROTOCOL_RTP_UDP:
3137 proto_str = "UDP";
3138 break;
3139 case RTSP_PROTOCOL_RTP_TCP:
3140 proto_str = "TCP";
3141 break;
3142 default:
3143 proto_str = "???";
3144 break;
3146 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3147 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3149 current_bandwidth += stream->bandwidth;
3151 c->next = first_http_ctx;
3152 first_http_ctx = c;
3153 return c;
3155 fail:
3156 if (c) {
3157 av_free(c->buffer);
3158 av_free(c);
3160 return NULL;
3163 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3164 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3165 used. */
3166 static int rtp_new_av_stream(HTTPContext *c,
3167 int stream_index, struct sockaddr_in *dest_addr,
3168 HTTPContext *rtsp_c)
3170 AVFormatContext *ctx;
3171 AVStream *st;
3172 char *ipaddr;
3173 URLContext *h;
3174 uint8_t *dummy_buf;
3175 char buf2[32];
3176 int max_packet_size;
3178 /* now we can open the relevant output stream */
3179 ctx = av_alloc_format_context();
3180 if (!ctx)
3181 return -1;
3182 ctx->oformat = &rtp_muxer;
3184 st = av_mallocz(sizeof(AVStream));
3185 if (!st)
3186 goto fail;
3187 st->codec= avcodec_alloc_context();
3188 ctx->nb_streams = 1;
3189 ctx->streams[0] = st;
3191 if (!c->stream->feed ||
3192 c->stream->feed == c->stream) {
3193 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3194 } else {
3195 memcpy(st,
3196 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3197 sizeof(AVStream));
3199 st->priv_data = NULL;
3201 /* build destination RTP address */
3202 ipaddr = inet_ntoa(dest_addr->sin_addr);
3204 switch(c->rtp_protocol) {
3205 case RTSP_PROTOCOL_RTP_UDP:
3206 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3207 /* RTP/UDP case */
3209 /* XXX: also pass as parameter to function ? */
3210 if (c->stream->is_multicast) {
3211 int ttl;
3212 ttl = c->stream->multicast_ttl;
3213 if (!ttl)
3214 ttl = 16;
3215 snprintf(ctx->filename, sizeof(ctx->filename),
3216 "rtp://%s:%d?multicast=1&ttl=%d",
3217 ipaddr, ntohs(dest_addr->sin_port), ttl);
3218 } else {
3219 snprintf(ctx->filename, sizeof(ctx->filename),
3220 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3223 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3224 goto fail;
3225 c->rtp_handles[stream_index] = h;
3226 max_packet_size = url_get_max_packet_size(h);
3227 break;
3228 case RTSP_PROTOCOL_RTP_TCP:
3229 /* RTP/TCP case */
3230 c->rtsp_c = rtsp_c;
3231 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3232 break;
3233 default:
3234 goto fail;
3237 http_log("%s:%d - - [%s] \"PLAY %s/streamid=%d %s\"\n",
3238 ipaddr, ntohs(dest_addr->sin_port),
3239 ctime1(buf2),
3240 c->stream->filename, stream_index, c->protocol);
3242 /* normally, no packets should be output here, but the packet size may be checked */
3243 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3244 /* XXX: close stream */
3245 goto fail;
3247 av_set_parameters(ctx, NULL);
3248 if (av_write_header(ctx) < 0) {
3249 fail:
3250 if (h)
3251 url_close(h);
3252 av_free(ctx);
3253 return -1;
3255 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3256 av_free(dummy_buf);
3258 c->rtp_ctx[stream_index] = ctx;
3259 return 0;
3262 /********************************************************************/
3263 /* ffserver initialization */
3265 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3267 AVStream *fst;
3269 fst = av_mallocz(sizeof(AVStream));
3270 if (!fst)
3271 return NULL;
3272 fst->codec= avcodec_alloc_context();
3273 fst->priv_data = av_mallocz(sizeof(FeedData));
3274 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3275 fst->codec->coded_frame = &dummy_frame;
3276 fst->index = stream->nb_streams;
3277 av_set_pts_info(fst, 33, 1, 90000);
3278 stream->streams[stream->nb_streams++] = fst;
3279 return fst;
3282 /* return the stream number in the feed */
3283 static int add_av_stream(FFStream *feed, AVStream *st)
3285 AVStream *fst;
3286 AVCodecContext *av, *av1;
3287 int i;
3289 av = st->codec;
3290 for(i=0;i<feed->nb_streams;i++) {
3291 st = feed->streams[i];
3292 av1 = st->codec;
3293 if (av1->codec_id == av->codec_id &&
3294 av1->codec_type == av->codec_type &&
3295 av1->bit_rate == av->bit_rate) {
3297 switch(av->codec_type) {
3298 case CODEC_TYPE_AUDIO:
3299 if (av1->channels == av->channels &&
3300 av1->sample_rate == av->sample_rate)
3301 goto found;
3302 break;
3303 case CODEC_TYPE_VIDEO:
3304 if (av1->width == av->width &&
3305 av1->height == av->height &&
3306 av1->time_base.den == av->time_base.den &&
3307 av1->time_base.num == av->time_base.num &&
3308 av1->gop_size == av->gop_size)
3309 goto found;
3310 break;
3311 default:
3312 abort();
3317 fst = add_av_stream1(feed, av);
3318 if (!fst)
3319 return -1;
3320 return feed->nb_streams - 1;
3321 found:
3322 return i;
3325 static void remove_stream(FFStream *stream)
3327 FFStream **ps;
3328 ps = &first_stream;
3329 while (*ps != NULL) {
3330 if (*ps == stream) {
3331 *ps = (*ps)->next;
3332 } else {
3333 ps = &(*ps)->next;
3338 /* specific mpeg4 handling : we extract the raw parameters */
3339 static void extract_mpeg4_header(AVFormatContext *infile)
3341 int mpeg4_count, i, size;
3342 AVPacket pkt;
3343 AVStream *st;
3344 const uint8_t *p;
3346 mpeg4_count = 0;
3347 for(i=0;i<infile->nb_streams;i++) {
3348 st = infile->streams[i];
3349 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3350 st->codec->extradata_size == 0) {
3351 mpeg4_count++;
3354 if (!mpeg4_count)
3355 return;
3357 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3358 while (mpeg4_count > 0) {
3359 if (av_read_packet(infile, &pkt) < 0)
3360 break;
3361 st = infile->streams[pkt.stream_index];
3362 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3363 st->codec->extradata_size == 0) {
3364 av_freep(&st->codec->extradata);
3365 /* fill extradata with the header */
3366 /* XXX: we make hard suppositions here ! */
3367 p = pkt.data;
3368 while (p < pkt.data + pkt.size - 4) {
3369 /* stop when vop header is found */
3370 if (p[0] == 0x00 && p[1] == 0x00 &&
3371 p[2] == 0x01 && p[3] == 0xb6) {
3372 size = p - pkt.data;
3373 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3374 st->codec->extradata = av_malloc(size);
3375 st->codec->extradata_size = size;
3376 memcpy(st->codec->extradata, pkt.data, size);
3377 break;
3379 p++;
3381 mpeg4_count--;
3383 av_free_packet(&pkt);
3387 /* compute the needed AVStream for each file */
3388 static void build_file_streams(void)
3390 FFStream *stream, *stream_next;
3391 AVFormatContext *infile;
3392 int i;
3394 /* gather all streams */
3395 for(stream = first_stream; stream != NULL; stream = stream_next) {
3396 stream_next = stream->next;
3397 if (stream->stream_type == STREAM_TYPE_LIVE &&
3398 !stream->feed) {
3399 /* the stream comes from a file */
3400 /* try to open the file */
3401 /* open stream */
3402 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3403 if (stream->fmt == &rtp_muxer) {
3404 /* specific case : if transport stream output to RTP,
3405 we use a raw transport stream reader */
3406 stream->ap_in->mpeg2ts_raw = 1;
3407 stream->ap_in->mpeg2ts_compute_pcr = 1;
3410 if (av_open_input_file(&infile, stream->feed_filename,
3411 stream->ifmt, 0, stream->ap_in) < 0) {
3412 http_log("%s not found", stream->feed_filename);
3413 /* remove stream (no need to spend more time on it) */
3414 fail:
3415 remove_stream(stream);
3416 } else {
3417 /* find all the AVStreams inside and reference them in
3418 'stream' */
3419 if (av_find_stream_info(infile) < 0) {
3420 http_log("Could not find codec parameters from '%s'",
3421 stream->feed_filename);
3422 av_close_input_file(infile);
3423 goto fail;
3425 extract_mpeg4_header(infile);
3427 for(i=0;i<infile->nb_streams;i++) {
3428 add_av_stream1(stream, infile->streams[i]->codec);
3430 av_close_input_file(infile);
3436 /* compute the needed AVStream for each feed */
3437 static void build_feed_streams(void)
3439 FFStream *stream, *feed;
3440 int i;
3442 /* gather all streams */
3443 for(stream = first_stream; stream != NULL; stream = stream->next) {
3444 feed = stream->feed;
3445 if (feed) {
3446 if (!stream->is_feed) {
3447 /* we handle a stream coming from a feed */
3448 for(i=0;i<stream->nb_streams;i++) {
3449 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3455 /* gather all streams */
3456 for(stream = first_stream; stream != NULL; stream = stream->next) {
3457 feed = stream->feed;
3458 if (feed) {
3459 if (stream->is_feed) {
3460 for(i=0;i<stream->nb_streams;i++) {
3461 stream->feed_streams[i] = i;
3467 /* create feed files if needed */
3468 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3469 int fd;
3471 if (url_exist(feed->feed_filename)) {
3472 /* See if it matches */
3473 AVFormatContext *s;
3474 int matches = 0;
3476 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3477 /* Now see if it matches */
3478 if (s->nb_streams == feed->nb_streams) {
3479 matches = 1;
3480 for(i=0;i<s->nb_streams;i++) {
3481 AVStream *sf, *ss;
3482 sf = feed->streams[i];
3483 ss = s->streams[i];
3485 if (sf->index != ss->index ||
3486 sf->id != ss->id) {
3487 printf("Index & Id do not match for stream %d (%s)\n",
3488 i, feed->feed_filename);
3489 matches = 0;
3490 } else {
3491 AVCodecContext *ccf, *ccs;
3493 ccf = sf->codec;
3494 ccs = ss->codec;
3495 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3497 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3498 printf("Codecs do not match for stream %d\n", i);
3499 matches = 0;
3500 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3501 printf("Codec bitrates do not match for stream %d\n", i);
3502 matches = 0;
3503 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3504 if (CHECK_CODEC(time_base.den) ||
3505 CHECK_CODEC(time_base.num) ||
3506 CHECK_CODEC(width) ||
3507 CHECK_CODEC(height)) {
3508 printf("Codec width, height and framerate do not match for stream %d\n", i);
3509 matches = 0;
3511 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3512 if (CHECK_CODEC(sample_rate) ||
3513 CHECK_CODEC(channels) ||
3514 CHECK_CODEC(frame_size)) {
3515 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3516 matches = 0;
3518 } else {
3519 printf("Unknown codec type\n");
3520 matches = 0;
3523 if (!matches) {
3524 break;
3527 } else {
3528 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3529 feed->feed_filename, s->nb_streams, feed->nb_streams);
3532 av_close_input_file(s);
3533 } else {
3534 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3535 feed->feed_filename);
3537 if (!matches) {
3538 if (feed->readonly) {
3539 printf("Unable to delete feed file '%s' as it is marked readonly\n",
3540 feed->feed_filename);
3541 exit(1);
3543 unlink(feed->feed_filename);
3546 if (!url_exist(feed->feed_filename)) {
3547 AVFormatContext s1, *s = &s1;
3549 if (feed->readonly) {
3550 printf("Unable to create feed file '%s' as it is marked readonly\n",
3551 feed->feed_filename);
3552 exit(1);
3555 /* only write the header of the ffm file */
3556 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3557 fprintf(stderr, "Could not open output feed file '%s'\n",
3558 feed->feed_filename);
3559 exit(1);
3561 s->oformat = feed->fmt;
3562 s->nb_streams = feed->nb_streams;
3563 for(i=0;i<s->nb_streams;i++) {
3564 AVStream *st;
3565 st = feed->streams[i];
3566 s->streams[i] = st;
3568 av_set_parameters(s, NULL);
3569 if (av_write_header(s) < 0) {
3570 fprintf(stderr, "Container doesn't supports the required parameters\n");
3571 exit(1);
3573 /* XXX: need better api */
3574 av_freep(&s->priv_data);
3575 url_fclose(&s->pb);
3577 /* get feed size and write index */
3578 fd = open(feed->feed_filename, O_RDONLY);
3579 if (fd < 0) {
3580 fprintf(stderr, "Could not open output feed file '%s'\n",
3581 feed->feed_filename);
3582 exit(1);
3585 feed->feed_write_index = ffm_read_write_index(fd);
3586 feed->feed_size = lseek(fd, 0, SEEK_END);
3587 /* ensure that we do not wrap before the end of file */
3588 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3589 feed->feed_max_size = feed->feed_size;
3591 close(fd);
3595 /* compute the bandwidth used by each stream */
3596 static void compute_bandwidth(void)
3598 int bandwidth, i;
3599 FFStream *stream;
3601 for(stream = first_stream; stream != NULL; stream = stream->next) {
3602 bandwidth = 0;
3603 for(i=0;i<stream->nb_streams;i++) {
3604 AVStream *st = stream->streams[i];
3605 switch(st->codec->codec_type) {
3606 case CODEC_TYPE_AUDIO:
3607 case CODEC_TYPE_VIDEO:
3608 bandwidth += st->codec->bit_rate;
3609 break;
3610 default:
3611 break;
3614 stream->bandwidth = (bandwidth + 999) / 1000;
3618 static void get_arg(char *buf, int buf_size, const char **pp)
3620 const char *p;
3621 char *q;
3622 int quote;
3624 p = *pp;
3625 while (isspace(*p)) p++;
3626 q = buf;
3627 quote = 0;
3628 if (*p == '\"' || *p == '\'')
3629 quote = *p++;
3630 for(;;) {
3631 if (quote) {
3632 if (*p == quote)
3633 break;
3634 } else {
3635 if (isspace(*p))
3636 break;
3638 if (*p == '\0')
3639 break;
3640 if ((q - buf) < buf_size - 1)
3641 *q++ = *p;
3642 p++;
3644 *q = '\0';
3645 if (quote && *p == quote)
3646 p++;
3647 *pp = p;
3650 /* add a codec and set the default parameters */
3651 static void add_codec(FFStream *stream, AVCodecContext *av)
3653 AVStream *st;
3655 /* compute default parameters */
3656 switch(av->codec_type) {
3657 case CODEC_TYPE_AUDIO:
3658 if (av->bit_rate == 0)
3659 av->bit_rate = 64000;
3660 if (av->sample_rate == 0)
3661 av->sample_rate = 22050;
3662 if (av->channels == 0)
3663 av->channels = 1;
3664 break;
3665 case CODEC_TYPE_VIDEO:
3666 if (av->bit_rate == 0)
3667 av->bit_rate = 64000;
3668 if (av->time_base.num == 0){
3669 av->time_base.den = 5;
3670 av->time_base.num = 1;
3672 if (av->width == 0 || av->height == 0) {
3673 av->width = 160;
3674 av->height = 128;
3676 /* Bitrate tolerance is less for streaming */
3677 if (av->bit_rate_tolerance == 0)
3678 av->bit_rate_tolerance = av->bit_rate / 4;
3679 if (av->qmin == 0)
3680 av->qmin = 3;
3681 if (av->qmax == 0)
3682 av->qmax = 31;
3683 if (av->max_qdiff == 0)
3684 av->max_qdiff = 3;
3685 av->qcompress = 0.5;
3686 av->qblur = 0.5;
3688 if (!av->nsse_weight)
3689 av->nsse_weight = 8;
3691 av->frame_skip_cmp = FF_CMP_DCTMAX;
3692 av->me_method = ME_EPZS;
3693 av->rc_buffer_aggressivity = 1.0;
3695 if (!av->rc_eq)
3696 av->rc_eq = "tex^qComp";
3697 if (!av->i_quant_factor)
3698 av->i_quant_factor = -0.8;
3699 if (!av->b_quant_factor)
3700 av->b_quant_factor = 1.25;
3701 if (!av->b_quant_offset)
3702 av->b_quant_offset = 1.25;
3703 if (!av->rc_max_rate)
3704 av->rc_max_rate = av->bit_rate * 2;
3706 if (av->rc_max_rate && !av->rc_buffer_size) {
3707 av->rc_buffer_size = av->rc_max_rate;
3711 break;
3712 default:
3713 abort();
3716 st = av_mallocz(sizeof(AVStream));
3717 if (!st)
3718 return;
3719 st->codec = avcodec_alloc_context();
3720 stream->streams[stream->nb_streams++] = st;
3721 memcpy(st->codec, av, sizeof(AVCodecContext));
3724 static int opt_audio_codec(const char *arg)
3726 AVCodec *p;
3728 p = first_avcodec;
3729 while (p) {
3730 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3731 break;
3732 p = p->next;
3734 if (p == NULL) {
3735 return CODEC_ID_NONE;
3738 return p->id;
3741 static int opt_video_codec(const char *arg)
3743 AVCodec *p;
3745 p = first_avcodec;
3746 while (p) {
3747 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3748 break;
3749 p = p->next;
3751 if (p == NULL) {
3752 return CODEC_ID_NONE;
3755 return p->id;
3758 /* simplistic plugin support */
3760 #ifdef HAVE_DLOPEN
3761 static void load_module(const char *filename)
3763 void *dll;
3764 void (*init_func)(void);
3765 dll = dlopen(filename, RTLD_NOW);
3766 if (!dll) {
3767 fprintf(stderr, "Could not load module '%s' - %s\n",
3768 filename, dlerror());
3769 return;
3772 init_func = dlsym(dll, "ffserver_module_init");
3773 if (!init_func) {
3774 fprintf(stderr,
3775 "%s: init function 'ffserver_module_init()' not found\n",
3776 filename);
3777 dlclose(dll);
3780 init_func();
3782 #endif
3784 static int parse_ffconfig(const char *filename)
3786 FILE *f;
3787 char line[1024];
3788 char cmd[64];
3789 char arg[1024];
3790 const char *p;
3791 int val, errors, line_num;
3792 FFStream **last_stream, *stream, *redirect;
3793 FFStream **last_feed, *feed;
3794 AVCodecContext audio_enc, video_enc;
3795 int audio_id, video_id;
3797 f = fopen(filename, "r");
3798 if (!f) {
3799 perror(filename);
3800 return -1;
3803 errors = 0;
3804 line_num = 0;
3805 first_stream = NULL;
3806 last_stream = &first_stream;
3807 first_feed = NULL;
3808 last_feed = &first_feed;
3809 stream = NULL;
3810 feed = NULL;
3811 redirect = NULL;
3812 audio_id = CODEC_ID_NONE;
3813 video_id = CODEC_ID_NONE;
3814 for(;;) {
3815 if (fgets(line, sizeof(line), f) == NULL)
3816 break;
3817 line_num++;
3818 p = line;
3819 while (isspace(*p))
3820 p++;
3821 if (*p == '\0' || *p == '#')
3822 continue;
3824 get_arg(cmd, sizeof(cmd), &p);
3826 if (!strcasecmp(cmd, "Port")) {
3827 get_arg(arg, sizeof(arg), &p);
3828 val = atoi(arg);
3829 if (val < 1 || val > 65536) {
3830 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3831 filename, line_num, arg);
3832 errors++;
3834 my_http_addr.sin_port = htons(val);
3835 } else if (!strcasecmp(cmd, "BindAddress")) {
3836 get_arg(arg, sizeof(arg), &p);
3837 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3838 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3839 filename, line_num, arg);
3840 errors++;
3842 } else if (!strcasecmp(cmd, "NoDaemon")) {
3843 ffserver_daemon = 0;
3844 } else if (!strcasecmp(cmd, "RTSPPort")) {
3845 get_arg(arg, sizeof(arg), &p);
3846 val = atoi(arg);
3847 if (val < 1 || val > 65536) {
3848 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3849 filename, line_num, arg);
3850 errors++;
3852 my_rtsp_addr.sin_port = htons(atoi(arg));
3853 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3854 get_arg(arg, sizeof(arg), &p);
3855 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3856 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3857 filename, line_num, arg);
3858 errors++;
3860 } else if (!strcasecmp(cmd, "MaxClients")) {
3861 get_arg(arg, sizeof(arg), &p);
3862 val = atoi(arg);
3863 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3864 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3865 filename, line_num, arg);
3866 errors++;
3867 } else {
3868 nb_max_connections = val;
3870 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3871 get_arg(arg, sizeof(arg), &p);
3872 val = atoi(arg);
3873 if (val < 10 || val > 100000) {
3874 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3875 filename, line_num, arg);
3876 errors++;
3877 } else {
3878 max_bandwidth = val;
3880 } else if (!strcasecmp(cmd, "CustomLog")) {
3881 get_arg(logfilename, sizeof(logfilename), &p);
3882 } else if (!strcasecmp(cmd, "<Feed")) {
3883 /*********************************************/
3884 /* Feed related options */
3885 char *q;
3886 if (stream || feed) {
3887 fprintf(stderr, "%s:%d: Already in a tag\n",
3888 filename, line_num);
3889 } else {
3890 feed = av_mallocz(sizeof(FFStream));
3891 /* add in stream list */
3892 *last_stream = feed;
3893 last_stream = &feed->next;
3894 /* add in feed list */
3895 *last_feed = feed;
3896 last_feed = &feed->next_feed;
3898 get_arg(feed->filename, sizeof(feed->filename), &p);
3899 q = strrchr(feed->filename, '>');
3900 if (*q)
3901 *q = '\0';
3902 feed->fmt = guess_format("ffm", NULL, NULL);
3903 /* defaut feed file */
3904 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3905 "/tmp/%s.ffm", feed->filename);
3906 feed->feed_max_size = 5 * 1024 * 1024;
3907 feed->is_feed = 1;
3908 feed->feed = feed; /* self feeding :-) */
3910 } else if (!strcasecmp(cmd, "Launch")) {
3911 if (feed) {
3912 int i;
3914 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3916 for (i = 0; i < 62; i++) {
3917 get_arg(arg, sizeof(arg), &p);
3918 if (!arg[0])
3919 break;
3921 feed->child_argv[i] = av_strdup(arg);
3924 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3926 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3927 "http://%s:%d/%s",
3928 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3929 inet_ntoa(my_http_addr.sin_addr),
3930 ntohs(my_http_addr.sin_port), feed->filename);
3932 if (ffserver_debug)
3934 int j;
3935 fprintf(stdout, "Launch commandline: ");
3936 for (j = 0; j <= i; j++)
3937 fprintf(stdout, "%s ", feed->child_argv[j]);
3938 fprintf(stdout, "\n");
3941 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3942 if (feed) {
3943 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3944 feed->readonly = 1;
3945 } else if (stream) {
3946 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3948 } else if (!strcasecmp(cmd, "File")) {
3949 if (feed) {
3950 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3951 } else if (stream) {
3952 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3954 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3955 if (feed) {
3956 const char *p1;
3957 double fsize;
3959 get_arg(arg, sizeof(arg), &p);
3960 p1 = arg;
3961 fsize = strtod(p1, (char **)&p1);
3962 switch(toupper(*p1)) {
3963 case 'K':
3964 fsize *= 1024;
3965 break;
3966 case 'M':
3967 fsize *= 1024 * 1024;
3968 break;
3969 case 'G':
3970 fsize *= 1024 * 1024 * 1024;
3971 break;
3973 feed->feed_max_size = (int64_t)fsize;
3975 } else if (!strcasecmp(cmd, "</Feed>")) {
3976 if (!feed) {
3977 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3978 filename, line_num);
3979 errors++;
3981 feed = NULL;
3982 } else if (!strcasecmp(cmd, "<Stream")) {
3983 /*********************************************/
3984 /* Stream related options */
3985 char *q;
3986 if (stream || feed) {
3987 fprintf(stderr, "%s:%d: Already in a tag\n",
3988 filename, line_num);
3989 } else {
3990 stream = av_mallocz(sizeof(FFStream));
3991 *last_stream = stream;
3992 last_stream = &stream->next;
3994 get_arg(stream->filename, sizeof(stream->filename), &p);
3995 q = strrchr(stream->filename, '>');
3996 if (*q)
3997 *q = '\0';
3998 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3999 memset(&audio_enc, 0, sizeof(AVCodecContext));
4000 memset(&video_enc, 0, sizeof(AVCodecContext));
4001 audio_id = CODEC_ID_NONE;
4002 video_id = CODEC_ID_NONE;
4003 if (stream->fmt) {
4004 audio_id = stream->fmt->audio_codec;
4005 video_id = stream->fmt->video_codec;
4008 } else if (!strcasecmp(cmd, "Feed")) {
4009 get_arg(arg, sizeof(arg), &p);
4010 if (stream) {
4011 FFStream *sfeed;
4013 sfeed = first_feed;
4014 while (sfeed != NULL) {
4015 if (!strcmp(sfeed->filename, arg))
4016 break;
4017 sfeed = sfeed->next_feed;
4019 if (!sfeed) {
4020 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
4021 filename, line_num, arg);
4022 } else {
4023 stream->feed = sfeed;
4026 } else if (!strcasecmp(cmd, "Format")) {
4027 get_arg(arg, sizeof(arg), &p);
4028 if (!strcmp(arg, "status")) {
4029 stream->stream_type = STREAM_TYPE_STATUS;
4030 stream->fmt = NULL;
4031 } else {
4032 stream->stream_type = STREAM_TYPE_LIVE;
4033 /* jpeg cannot be used here, so use single frame jpeg */
4034 if (!strcmp(arg, "jpeg"))
4035 strcpy(arg, "mjpeg");
4036 stream->fmt = guess_stream_format(arg, NULL, NULL);
4037 if (!stream->fmt) {
4038 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4039 filename, line_num, arg);
4040 errors++;
4043 if (stream->fmt) {
4044 audio_id = stream->fmt->audio_codec;
4045 video_id = stream->fmt->video_codec;
4047 } else if (!strcasecmp(cmd, "InputFormat")) {
4048 get_arg(arg, sizeof(arg), &p);
4049 stream->ifmt = av_find_input_format(arg);
4050 if (!stream->ifmt) {
4051 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4052 filename, line_num, arg);
4054 } else if (!strcasecmp(cmd, "FaviconURL")) {
4055 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4056 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4057 } else {
4058 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4059 filename, line_num);
4060 errors++;
4062 } else if (!strcasecmp(cmd, "Author")) {
4063 if (stream) {
4064 get_arg(stream->author, sizeof(stream->author), &p);
4066 } else if (!strcasecmp(cmd, "Comment")) {
4067 if (stream) {
4068 get_arg(stream->comment, sizeof(stream->comment), &p);
4070 } else if (!strcasecmp(cmd, "Copyright")) {
4071 if (stream) {
4072 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4074 } else if (!strcasecmp(cmd, "Title")) {
4075 if (stream) {
4076 get_arg(stream->title, sizeof(stream->title), &p);
4078 } else if (!strcasecmp(cmd, "Preroll")) {
4079 get_arg(arg, sizeof(arg), &p);
4080 if (stream) {
4081 stream->prebuffer = atof(arg) * 1000;
4083 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4084 if (stream) {
4085 stream->send_on_key = 1;
4087 } else if (!strcasecmp(cmd, "AudioCodec")) {
4088 get_arg(arg, sizeof(arg), &p);
4089 audio_id = opt_audio_codec(arg);
4090 if (audio_id == CODEC_ID_NONE) {
4091 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4092 filename, line_num, arg);
4093 errors++;
4095 } else if (!strcasecmp(cmd, "VideoCodec")) {
4096 get_arg(arg, sizeof(arg), &p);
4097 video_id = opt_video_codec(arg);
4098 if (video_id == CODEC_ID_NONE) {
4099 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4100 filename, line_num, arg);
4101 errors++;
4103 } else if (!strcasecmp(cmd, "MaxTime")) {
4104 get_arg(arg, sizeof(arg), &p);
4105 if (stream) {
4106 stream->max_time = atof(arg) * 1000;
4108 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4109 get_arg(arg, sizeof(arg), &p);
4110 if (stream) {
4111 audio_enc.bit_rate = atoi(arg) * 1000;
4113 } else if (!strcasecmp(cmd, "AudioChannels")) {
4114 get_arg(arg, sizeof(arg), &p);
4115 if (stream) {
4116 audio_enc.channels = atoi(arg);
4118 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4119 get_arg(arg, sizeof(arg), &p);
4120 if (stream) {
4121 audio_enc.sample_rate = atoi(arg);
4123 } else if (!strcasecmp(cmd, "AudioQuality")) {
4124 get_arg(arg, sizeof(arg), &p);
4125 if (stream) {
4126 // audio_enc.quality = atof(arg) * 1000;
4128 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4129 if (stream) {
4130 int minrate, maxrate;
4132 get_arg(arg, sizeof(arg), &p);
4134 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4135 video_enc.rc_min_rate = minrate * 1000;
4136 video_enc.rc_max_rate = maxrate * 1000;
4137 } else {
4138 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4139 filename, line_num, arg);
4140 errors++;
4143 } else if (!strcasecmp(cmd, "Debug")) {
4144 if (stream) {
4145 get_arg(arg, sizeof(arg), &p);
4146 video_enc.debug = strtol(arg,0,0);
4148 } else if (!strcasecmp(cmd, "Strict")) {
4149 if (stream) {
4150 get_arg(arg, sizeof(arg), &p);
4151 video_enc.strict_std_compliance = atoi(arg);
4153 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4154 if (stream) {
4155 get_arg(arg, sizeof(arg), &p);
4156 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4158 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4159 if (stream) {
4160 get_arg(arg, sizeof(arg), &p);
4161 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4163 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4164 get_arg(arg, sizeof(arg), &p);
4165 if (stream) {
4166 video_enc.bit_rate = atoi(arg) * 1000;
4168 } else if (!strcasecmp(cmd, "VideoSize")) {
4169 get_arg(arg, sizeof(arg), &p);
4170 if (stream) {
4171 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4172 if ((video_enc.width % 16) != 0 ||
4173 (video_enc.height % 16) != 0) {
4174 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4175 filename, line_num);
4176 errors++;
4179 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4180 get_arg(arg, sizeof(arg), &p);
4181 if (stream) {
4182 video_enc.time_base.num= DEFAULT_FRAME_RATE_BASE;
4183 video_enc.time_base.den = (int)(strtod(arg, NULL) * video_enc.time_base.num);
4185 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4186 get_arg(arg, sizeof(arg), &p);
4187 if (stream) {
4188 video_enc.gop_size = atoi(arg);
4190 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4191 if (stream) {
4192 video_enc.gop_size = 1;
4194 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4195 if (stream) {
4196 video_enc.mb_decision = FF_MB_DECISION_BITS;
4198 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4199 if (stream) {
4200 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4201 video_enc.flags |= CODEC_FLAG_4MV;
4203 } else if (!strcasecmp(cmd, "VideoTag")) {
4204 get_arg(arg, sizeof(arg), &p);
4205 if ((strlen(arg) == 4) && stream) {
4206 video_enc.codec_tag = ff_get_fourcc(arg);
4208 } else if (!strcasecmp(cmd, "BitExact")) {
4209 if (stream) {
4210 video_enc.flags |= CODEC_FLAG_BITEXACT;
4212 } else if (!strcasecmp(cmd, "DctFastint")) {
4213 if (stream) {
4214 video_enc.dct_algo = FF_DCT_FASTINT;
4216 } else if (!strcasecmp(cmd, "IdctSimple")) {
4217 if (stream) {
4218 video_enc.idct_algo = FF_IDCT_SIMPLE;
4220 } else if (!strcasecmp(cmd, "Qscale")) {
4221 get_arg(arg, sizeof(arg), &p);
4222 if (stream) {
4223 video_enc.flags |= CODEC_FLAG_QSCALE;
4224 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4226 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4227 get_arg(arg, sizeof(arg), &p);
4228 if (stream) {
4229 video_enc.max_qdiff = atoi(arg);
4230 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4231 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4232 filename, line_num);
4233 errors++;
4236 } else if (!strcasecmp(cmd, "VideoQMax")) {
4237 get_arg(arg, sizeof(arg), &p);
4238 if (stream) {
4239 video_enc.qmax = atoi(arg);
4240 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4241 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4242 filename, line_num);
4243 errors++;
4246 } else if (!strcasecmp(cmd, "VideoQMin")) {
4247 get_arg(arg, sizeof(arg), &p);
4248 if (stream) {
4249 video_enc.qmin = atoi(arg);
4250 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4251 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4252 filename, line_num);
4253 errors++;
4256 } else if (!strcasecmp(cmd, "LumaElim")) {
4257 get_arg(arg, sizeof(arg), &p);
4258 if (stream) {
4259 video_enc.luma_elim_threshold = atoi(arg);
4261 } else if (!strcasecmp(cmd, "ChromaElim")) {
4262 get_arg(arg, sizeof(arg), &p);
4263 if (stream) {
4264 video_enc.chroma_elim_threshold = atoi(arg);
4266 } else if (!strcasecmp(cmd, "LumiMask")) {
4267 get_arg(arg, sizeof(arg), &p);
4268 if (stream) {
4269 video_enc.lumi_masking = atof(arg);
4271 } else if (!strcasecmp(cmd, "DarkMask")) {
4272 get_arg(arg, sizeof(arg), &p);
4273 if (stream) {
4274 video_enc.dark_masking = atof(arg);
4276 } else if (!strcasecmp(cmd, "NoVideo")) {
4277 video_id = CODEC_ID_NONE;
4278 } else if (!strcasecmp(cmd, "NoAudio")) {
4279 audio_id = CODEC_ID_NONE;
4280 } else if (!strcasecmp(cmd, "ACL")) {
4281 IPAddressACL acl;
4283 get_arg(arg, sizeof(arg), &p);
4284 if (strcasecmp(arg, "allow") == 0) {
4285 acl.action = IP_ALLOW;
4286 } else if (strcasecmp(arg, "deny") == 0) {
4287 acl.action = IP_DENY;
4288 } else {
4289 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4290 filename, line_num, arg);
4291 errors++;
4294 get_arg(arg, sizeof(arg), &p);
4296 if (resolve_host(&acl.first, arg) != 0) {
4297 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4298 filename, line_num, arg);
4299 errors++;
4300 } else {
4301 acl.last = acl.first;
4304 get_arg(arg, sizeof(arg), &p);
4306 if (arg[0]) {
4307 if (resolve_host(&acl.last, arg) != 0) {
4308 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4309 filename, line_num, arg);
4310 errors++;
4314 if (!errors) {
4315 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4316 IPAddressACL **naclp = 0;
4318 acl.next = 0;
4319 *nacl = acl;
4321 if (stream) {
4322 naclp = &stream->acl;
4323 } else if (feed) {
4324 naclp = &feed->acl;
4325 } else {
4326 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4327 filename, line_num);
4328 errors++;
4331 if (naclp) {
4332 while (*naclp)
4333 naclp = &(*naclp)->next;
4335 *naclp = nacl;
4338 } else if (!strcasecmp(cmd, "RTSPOption")) {
4339 get_arg(arg, sizeof(arg), &p);
4340 if (stream) {
4341 av_freep(&stream->rtsp_option);
4342 stream->rtsp_option = av_strdup(arg);
4344 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4345 get_arg(arg, sizeof(arg), &p);
4346 if (stream) {
4347 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4348 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4349 filename, line_num, arg);
4350 errors++;
4352 stream->is_multicast = 1;
4353 stream->loop = 1; /* default is looping */
4355 } else if (!strcasecmp(cmd, "MulticastPort")) {
4356 get_arg(arg, sizeof(arg), &p);
4357 if (stream) {
4358 stream->multicast_port = atoi(arg);
4360 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4361 get_arg(arg, sizeof(arg), &p);
4362 if (stream) {
4363 stream->multicast_ttl = atoi(arg);
4365 } else if (!strcasecmp(cmd, "NoLoop")) {
4366 if (stream) {
4367 stream->loop = 0;
4369 } else if (!strcasecmp(cmd, "</Stream>")) {
4370 if (!stream) {
4371 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4372 filename, line_num);
4373 errors++;
4375 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4376 if (audio_id != CODEC_ID_NONE) {
4377 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4378 audio_enc.codec_id = audio_id;
4379 add_codec(stream, &audio_enc);
4381 if (video_id != CODEC_ID_NONE) {
4382 video_enc.codec_type = CODEC_TYPE_VIDEO;
4383 video_enc.codec_id = video_id;
4384 add_codec(stream, &video_enc);
4387 stream = NULL;
4388 } else if (!strcasecmp(cmd, "<Redirect")) {
4389 /*********************************************/
4390 char *q;
4391 if (stream || feed || redirect) {
4392 fprintf(stderr, "%s:%d: Already in a tag\n",
4393 filename, line_num);
4394 errors++;
4395 } else {
4396 redirect = av_mallocz(sizeof(FFStream));
4397 *last_stream = redirect;
4398 last_stream = &redirect->next;
4400 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4401 q = strrchr(redirect->filename, '>');
4402 if (*q)
4403 *q = '\0';
4404 redirect->stream_type = STREAM_TYPE_REDIRECT;
4406 } else if (!strcasecmp(cmd, "URL")) {
4407 if (redirect) {
4408 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4410 } else if (!strcasecmp(cmd, "</Redirect>")) {
4411 if (!redirect) {
4412 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4413 filename, line_num);
4414 errors++;
4416 if (!redirect->feed_filename[0]) {
4417 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4418 filename, line_num);
4419 errors++;
4421 redirect = NULL;
4422 } else if (!strcasecmp(cmd, "LoadModule")) {
4423 get_arg(arg, sizeof(arg), &p);
4424 #ifdef HAVE_DLOPEN
4425 load_module(arg);
4426 #else
4427 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4428 filename, line_num, arg);
4429 errors++;
4430 #endif
4431 } else {
4432 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4433 filename, line_num, cmd);
4434 errors++;
4438 fclose(f);
4439 if (errors)
4440 return -1;
4441 else
4442 return 0;
4445 static void show_banner(void)
4447 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000-2006 Fabrice Bellard, et al.\n");
4450 static void show_help(void)
4452 show_banner();
4453 printf("usage: ffserver [-L] [-h] [-f configfile]\n"
4454 "Hyper fast multi format Audio/Video streaming server\n"
4455 "\n"
4456 "-L : print the LICENSE\n"
4457 "-h : this help\n"
4458 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4462 static void show_license(void)
4464 show_banner();
4465 printf(
4466 "FFmpeg is free software; you can redistribute it and/or\n"
4467 "modify it under the terms of the GNU Lesser General Public\n"
4468 "License as published by the Free Software Foundation; either\n"
4469 "version 2.1 of the License, or (at your option) any later version.\n"
4470 "\n"
4471 "FFmpeg is distributed in the hope that it will be useful,\n"
4472 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4473 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4474 "Lesser General Public License for more details.\n"
4475 "\n"
4476 "You should have received a copy of the GNU Lesser General Public\n"
4477 "License along with FFmpeg; if not, write to the Free Software\n"
4478 "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n"
4482 static void handle_child_exit(int sig)
4484 pid_t pid;
4485 int status;
4487 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4488 FFStream *feed;
4490 for (feed = first_feed; feed; feed = feed->next) {
4491 if (feed->pid == pid) {
4492 int uptime = time(0) - feed->pid_start;
4494 feed->pid = 0;
4495 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4497 if (uptime < 30) {
4498 /* Turn off any more restarts */
4499 feed->child_argv = 0;
4505 need_to_start_children = 1;
4508 int main(int argc, char **argv)
4510 const char *config_filename;
4511 int c;
4512 struct sigaction sigact;
4514 av_register_all();
4516 config_filename = "/etc/ffserver.conf";
4518 my_program_name = argv[0];
4519 my_program_dir = getcwd(0, 0);
4520 ffserver_daemon = 1;
4522 for(;;) {
4523 c = getopt(argc, argv, "ndLh?f:");
4524 if (c == -1)
4525 break;
4526 switch(c) {
4527 case 'L':
4528 show_license();
4529 exit(1);
4530 case '?':
4531 case 'h':
4532 show_help();
4533 exit(1);
4534 case 'n':
4535 no_launch = 1;
4536 break;
4537 case 'd':
4538 ffserver_debug = 1;
4539 ffserver_daemon = 0;
4540 break;
4541 case 'f':
4542 config_filename = optarg;
4543 break;
4544 default:
4545 exit(2);
4549 putenv("http_proxy"); /* Kill the http_proxy */
4551 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4553 /* address on which the server will handle HTTP connections */
4554 my_http_addr.sin_family = AF_INET;
4555 my_http_addr.sin_port = htons (8080);
4556 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4558 /* address on which the server will handle RTSP connections */
4559 my_rtsp_addr.sin_family = AF_INET;
4560 my_rtsp_addr.sin_port = htons (5454);
4561 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4563 nb_max_connections = 5;
4564 max_bandwidth = 1000;
4565 first_stream = NULL;
4566 logfilename[0] = '\0';
4568 memset(&sigact, 0, sizeof(sigact));
4569 sigact.sa_handler = handle_child_exit;
4570 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4571 sigaction(SIGCHLD, &sigact, 0);
4573 if (parse_ffconfig(config_filename) < 0) {
4574 fprintf(stderr, "Incorrect config file - exiting.\n");
4575 exit(1);
4578 build_file_streams();
4580 build_feed_streams();
4582 compute_bandwidth();
4584 /* put the process in background and detach it from its TTY */
4585 if (ffserver_daemon) {
4586 int pid;
4588 pid = fork();
4589 if (pid < 0) {
4590 perror("fork");
4591 exit(1);
4592 } else if (pid > 0) {
4593 /* parent : exit */
4594 exit(0);
4595 } else {
4596 /* child */
4597 setsid();
4598 chdir("/");
4599 close(0);
4600 open("/dev/null", O_RDWR);
4601 if (strcmp(logfilename, "-") != 0) {
4602 close(1);
4603 dup(0);
4605 close(2);
4606 dup(0);
4610 /* signal init */
4611 signal(SIGPIPE, SIG_IGN);
4613 /* open log file if needed */
4614 if (logfilename[0] != '\0') {
4615 if (!strcmp(logfilename, "-"))
4616 logfile = stdout;
4617 else
4618 logfile = fopen(logfilename, "w");
4621 if (http_server() < 0) {
4622 fprintf(stderr, "Could not start server\n");
4623 exit(1);
4626 return 0;