Simplify rms(): merge a few operations in the same statement
[ffmpeg-lucabe.git] / ffserver.c
blobccb5c0ab5c310473a77d069a7a5324e00e45362e
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 #ifndef HAVE_CLOSESOCKET
24 #define closesocket close
25 #endif
26 #include <string.h>
27 #include <stdlib.h>
28 #include "libavutil/random.h"
29 #include "libavutil/avstring.h"
30 #include "libavformat/avformat.h"
31 #include "libavformat/network.h"
32 #include "libavformat/os_support.h"
33 #include "libavformat/rtp.h"
34 #include "libavformat/rtsp.h"
35 #include "libavcodec/opt.h"
36 #include <stdarg.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <sys/ioctl.h>
40 #ifdef HAVE_POLL_H
41 #include <poll.h>
42 #endif
43 #include <errno.h>
44 #include <sys/time.h>
45 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
46 #include <time.h>
47 #include <sys/wait.h>
48 #include <signal.h>
49 #ifdef HAVE_DLFCN_H
50 #include <dlfcn.h>
51 #endif
53 #include "cmdutils.h"
55 #undef exit
57 const char program_name[] = "FFserver";
58 const int program_birth_year = 2000;
60 static const OptionDef options[];
62 /* maximum number of simultaneous HTTP connections */
63 #define HTTP_MAX_CONNECTIONS 2000
65 enum HTTPState {
66 HTTPSTATE_WAIT_REQUEST,
67 HTTPSTATE_SEND_HEADER,
68 HTTPSTATE_SEND_DATA_HEADER,
69 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
70 HTTPSTATE_SEND_DATA_TRAILER,
71 HTTPSTATE_RECEIVE_DATA,
72 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
73 HTTPSTATE_READY,
75 RTSPSTATE_WAIT_REQUEST,
76 RTSPSTATE_SEND_REPLY,
77 RTSPSTATE_SEND_PACKET,
80 static const char *http_state[] = {
81 "HTTP_WAIT_REQUEST",
82 "HTTP_SEND_HEADER",
84 "SEND_DATA_HEADER",
85 "SEND_DATA",
86 "SEND_DATA_TRAILER",
87 "RECEIVE_DATA",
88 "WAIT_FEED",
89 "READY",
91 "RTSP_WAIT_REQUEST",
92 "RTSP_SEND_REPLY",
93 "RTSP_SEND_PACKET",
96 #define IOBUFFER_INIT_SIZE 8192
98 /* timeouts are in ms */
99 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
100 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
102 #define SYNC_TIMEOUT (10 * 1000)
104 typedef struct {
105 int64_t count1, count2;
106 int64_t time1, time2;
107 } DataRateData;
109 /* context associated with one connection */
110 typedef struct HTTPContext {
111 enum HTTPState state;
112 int fd; /* socket file descriptor */
113 struct sockaddr_in from_addr; /* origin */
114 struct pollfd *poll_entry; /* used when polling */
115 int64_t timeout;
116 uint8_t *buffer_ptr, *buffer_end;
117 int http_error;
118 int post;
119 struct HTTPContext *next;
120 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
121 int64_t data_count;
122 /* feed input */
123 int feed_fd;
124 /* input format handling */
125 AVFormatContext *fmt_in;
126 int64_t start_time; /* In milliseconds - this wraps fairly often */
127 int64_t first_pts; /* initial pts value */
128 int64_t cur_pts; /* current pts value from the stream in us */
129 int64_t cur_frame_duration; /* duration of the current frame in us */
130 int cur_frame_bytes; /* output frame size, needed to compute
131 the time at which we send each
132 packet */
133 int pts_stream_index; /* stream we choose as clock reference */
134 int64_t cur_clock; /* current clock reference value in us */
135 /* output format handling */
136 struct FFStream *stream;
137 /* -1 is invalid stream */
138 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
139 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
140 int switch_pending;
141 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
142 int last_packet_sent; /* true if last data packet was sent */
143 int suppress_log;
144 DataRateData datarate;
145 int wmp_client_id;
146 char protocol[16];
147 char method[16];
148 char url[128];
149 int buffer_size;
150 uint8_t *buffer;
151 int is_packetized; /* if true, the stream is packetized */
152 int packet_stream_index; /* current stream for output in state machine */
154 /* RTSP state specific */
155 uint8_t *pb_buffer; /* XXX: use that in all the code */
156 ByteIOContext *pb;
157 int seq; /* RTSP sequence number */
159 /* RTP state specific */
160 enum RTSPProtocol rtp_protocol;
161 char session_id[32]; /* session id */
162 AVFormatContext *rtp_ctx[MAX_STREAMS];
164 /* RTP/UDP specific */
165 URLContext *rtp_handles[MAX_STREAMS];
167 /* RTP/TCP specific */
168 struct HTTPContext *rtsp_c;
169 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
170 } HTTPContext;
172 /* each generated stream is described here */
173 enum StreamType {
174 STREAM_TYPE_LIVE,
175 STREAM_TYPE_STATUS,
176 STREAM_TYPE_REDIRECT,
179 enum IPAddressAction {
180 IP_ALLOW = 1,
181 IP_DENY,
184 typedef struct IPAddressACL {
185 struct IPAddressACL *next;
186 enum IPAddressAction action;
187 /* These are in host order */
188 struct in_addr first;
189 struct in_addr last;
190 } IPAddressACL;
192 /* description of each stream of the ffserver.conf file */
193 typedef struct FFStream {
194 enum StreamType stream_type;
195 char filename[1024]; /* stream filename */
196 struct FFStream *feed; /* feed we are using (can be null if
197 coming from file) */
198 AVFormatParameters *ap_in; /* input parameters */
199 AVInputFormat *ifmt; /* if non NULL, force input format */
200 AVOutputFormat *fmt;
201 IPAddressACL *acl;
202 int nb_streams;
203 int prebuffer; /* Number of millseconds early to start */
204 int64_t max_time; /* Number of milliseconds to run */
205 int send_on_key;
206 AVStream *streams[MAX_STREAMS];
207 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
208 char feed_filename[1024]; /* file name of the feed storage, or
209 input file name for a stream */
210 char author[512];
211 char title[512];
212 char copyright[512];
213 char comment[512];
214 pid_t pid; /* Of ffmpeg process */
215 time_t pid_start; /* Of ffmpeg process */
216 char **child_argv;
217 struct FFStream *next;
218 unsigned bandwidth; /* bandwidth, in kbits/s */
219 /* RTSP options */
220 char *rtsp_option;
221 /* multicast specific */
222 int is_multicast;
223 struct in_addr multicast_ip;
224 int multicast_port; /* first port used for multicast */
225 int multicast_ttl;
226 int loop; /* if true, send the stream in loops (only meaningful if file) */
228 /* feed specific */
229 int feed_opened; /* true if someone is writing to the feed */
230 int is_feed; /* true if it is a feed */
231 int readonly; /* True if writing is prohibited to the file */
232 int conns_served;
233 int64_t bytes_served;
234 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
235 int64_t feed_write_index; /* current write position in feed (it wraps around) */
236 int64_t feed_size; /* current size of feed */
237 struct FFStream *next_feed;
238 } FFStream;
240 typedef struct FeedData {
241 long long data_count;
242 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
243 } FeedData;
245 static struct sockaddr_in my_http_addr;
246 static struct sockaddr_in my_rtsp_addr;
248 static char logfilename[1024];
249 static HTTPContext *first_http_ctx;
250 static FFStream *first_feed; /* contains only feeds */
251 static FFStream *first_stream; /* contains all streams, including feeds */
253 static void new_connection(int server_fd, int is_rtsp);
254 static void close_connection(HTTPContext *c);
256 /* HTTP handling */
257 static int handle_connection(HTTPContext *c);
258 static int http_parse_request(HTTPContext *c);
259 static int http_send_data(HTTPContext *c);
260 static void compute_status(HTTPContext *c);
261 static int open_input_stream(HTTPContext *c, const char *info);
262 static int http_start_receive_data(HTTPContext *c);
263 static int http_receive_data(HTTPContext *c);
265 /* RTSP handling */
266 static int rtsp_parse_request(HTTPContext *c);
267 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
268 static void rtsp_cmd_options(HTTPContext *c, const char *url);
269 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
270 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
271 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
272 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
274 /* SDP handling */
275 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
276 struct in_addr my_ip);
278 /* RTP handling */
279 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
280 FFStream *stream, const char *session_id,
281 enum RTSPProtocol rtp_protocol);
282 static int rtp_new_av_stream(HTTPContext *c,
283 int stream_index, struct sockaddr_in *dest_addr,
284 HTTPContext *rtsp_c);
286 static const char *my_program_name;
287 static const char *my_program_dir;
289 static const char *config_filename;
290 static int ffserver_debug;
291 static int ffserver_daemon;
292 static int no_launch;
293 static int need_to_start_children;
295 static int nb_max_connections = 5;
296 static int nb_connections;
298 static uint64_t max_bandwidth = 1000;
299 static uint64_t current_bandwidth;
301 static int64_t cur_time; // Making this global saves on passing it around everywhere
303 static AVRandomState random_state;
305 static FILE *logfile = NULL;
307 static char *ctime1(char *buf2)
309 time_t ti;
310 char *p;
312 ti = time(NULL);
313 p = ctime(&ti);
314 strcpy(buf2, p);
315 p = buf2 + strlen(p) - 1;
316 if (*p == '\n')
317 *p = '\0';
318 return buf2;
321 static void http_vlog(const char *fmt, va_list vargs)
323 static int print_prefix = 1;
324 if (logfile) {
325 if (print_prefix) {
326 char buf[32];
327 ctime1(buf);
328 fprintf(logfile, "%s ", buf);
330 print_prefix = strstr(fmt, "\n") != NULL;
331 vfprintf(logfile, fmt, vargs);
332 fflush(logfile);
336 void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
338 va_list vargs;
339 va_start(vargs, fmt);
340 http_vlog(fmt, vargs);
341 va_end(vargs);
344 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
346 static int print_prefix = 1;
347 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
348 if (level > av_log_level)
349 return;
350 if (print_prefix && avc)
351 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
352 print_prefix = strstr(fmt, "\n") != NULL;
353 http_vlog(fmt, vargs);
356 static void log_connection(HTTPContext *c)
358 if (c->suppress_log)
359 return;
361 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
362 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
363 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
366 static void update_datarate(DataRateData *drd, int64_t count)
368 if (!drd->time1 && !drd->count1) {
369 drd->time1 = drd->time2 = cur_time;
370 drd->count1 = drd->count2 = count;
371 } else if (cur_time - drd->time2 > 5000) {
372 drd->time1 = drd->time2;
373 drd->count1 = drd->count2;
374 drd->time2 = cur_time;
375 drd->count2 = count;
379 /* In bytes per second */
380 static int compute_datarate(DataRateData *drd, int64_t count)
382 if (cur_time == drd->time1)
383 return 0;
385 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
389 static void start_children(FFStream *feed)
391 if (no_launch)
392 return;
394 for (; feed; feed = feed->next) {
395 if (feed->child_argv && !feed->pid) {
396 feed->pid_start = time(0);
398 feed->pid = fork();
400 if (feed->pid < 0) {
401 http_log("Unable to create children\n");
402 exit(1);
404 if (!feed->pid) {
405 /* In child */
406 char pathname[1024];
407 char *slash;
408 int i;
410 av_strlcpy(pathname, my_program_name, sizeof(pathname));
412 slash = strrchr(pathname, '/');
413 if (!slash)
414 slash = pathname;
415 else
416 slash++;
417 strcpy(slash, "ffmpeg");
419 http_log("Launch commandline: ");
420 http_log("%s ", pathname);
421 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
422 http_log("%s ", feed->child_argv[i]);
423 http_log("\n");
425 for (i = 3; i < 256; i++)
426 close(i);
428 if (!ffserver_debug) {
429 i = open("/dev/null", O_RDWR);
430 if (i != -1) {
431 dup2(i, 0);
432 dup2(i, 1);
433 dup2(i, 2);
434 close(i);
438 /* This is needed to make relative pathnames work */
439 chdir(my_program_dir);
441 signal(SIGPIPE, SIG_DFL);
443 execvp(pathname, feed->child_argv);
445 _exit(1);
451 /* open a listening socket */
452 static int socket_open_listen(struct sockaddr_in *my_addr)
454 int server_fd, tmp;
456 server_fd = socket(AF_INET,SOCK_STREAM,0);
457 if (server_fd < 0) {
458 perror ("socket");
459 return -1;
462 tmp = 1;
463 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
465 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
466 char bindmsg[32];
467 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
468 perror (bindmsg);
469 closesocket(server_fd);
470 return -1;
473 if (listen (server_fd, 5) < 0) {
474 perror ("listen");
475 closesocket(server_fd);
476 return -1;
478 ff_socket_nonblock(server_fd, 1);
480 return server_fd;
483 /* start all multicast streams */
484 static void start_multicast(void)
486 FFStream *stream;
487 char session_id[32];
488 HTTPContext *rtp_c;
489 struct sockaddr_in dest_addr;
490 int default_port, stream_index;
492 default_port = 6000;
493 for(stream = first_stream; stream != NULL; stream = stream->next) {
494 if (stream->is_multicast) {
495 /* open the RTP connection */
496 snprintf(session_id, sizeof(session_id), "%08x%08x",
497 av_random(&random_state), av_random(&random_state));
499 /* choose a port if none given */
500 if (stream->multicast_port == 0) {
501 stream->multicast_port = default_port;
502 default_port += 100;
505 dest_addr.sin_family = AF_INET;
506 dest_addr.sin_addr = stream->multicast_ip;
507 dest_addr.sin_port = htons(stream->multicast_port);
509 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
510 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
511 if (!rtp_c)
512 continue;
514 if (open_input_stream(rtp_c, "") < 0) {
515 http_log("Could not open input stream for stream '%s'\n",
516 stream->filename);
517 continue;
520 /* open each RTP stream */
521 for(stream_index = 0; stream_index < stream->nb_streams;
522 stream_index++) {
523 dest_addr.sin_port = htons(stream->multicast_port +
524 2 * stream_index);
525 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
526 http_log("Could not open output stream '%s/streamid=%d'\n",
527 stream->filename, stream_index);
528 exit(1);
532 /* change state to send data */
533 rtp_c->state = HTTPSTATE_SEND_DATA;
538 /* main loop of the http server */
539 static int http_server(void)
541 int server_fd = 0, rtsp_server_fd = 0;
542 int ret, delay, delay1;
543 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
544 HTTPContext *c, *c_next;
546 if (my_http_addr.sin_port) {
547 server_fd = socket_open_listen(&my_http_addr);
548 if (server_fd < 0)
549 return -1;
552 if (my_rtsp_addr.sin_port) {
553 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
554 if (rtsp_server_fd < 0)
555 return -1;
558 if (!rtsp_server_fd && !server_fd) {
559 http_log("HTTP and RTSP disabled.\n");
560 return -1;
563 http_log("ffserver started.\n");
565 start_children(first_feed);
567 start_multicast();
569 for(;;) {
570 poll_entry = poll_table;
571 if (server_fd) {
572 poll_entry->fd = server_fd;
573 poll_entry->events = POLLIN;
574 poll_entry++;
576 if (rtsp_server_fd) {
577 poll_entry->fd = rtsp_server_fd;
578 poll_entry->events = POLLIN;
579 poll_entry++;
582 /* wait for events on each HTTP handle */
583 c = first_http_ctx;
584 delay = 1000;
585 while (c != NULL) {
586 int fd;
587 fd = c->fd;
588 switch(c->state) {
589 case HTTPSTATE_SEND_HEADER:
590 case RTSPSTATE_SEND_REPLY:
591 case RTSPSTATE_SEND_PACKET:
592 c->poll_entry = poll_entry;
593 poll_entry->fd = fd;
594 poll_entry->events = POLLOUT;
595 poll_entry++;
596 break;
597 case HTTPSTATE_SEND_DATA_HEADER:
598 case HTTPSTATE_SEND_DATA:
599 case HTTPSTATE_SEND_DATA_TRAILER:
600 if (!c->is_packetized) {
601 /* for TCP, we output as much as we can (may need to put a limit) */
602 c->poll_entry = poll_entry;
603 poll_entry->fd = fd;
604 poll_entry->events = POLLOUT;
605 poll_entry++;
606 } else {
607 /* when ffserver is doing the timing, we work by
608 looking at which packet need to be sent every
609 10 ms */
610 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
611 if (delay1 < delay)
612 delay = delay1;
614 break;
615 case HTTPSTATE_WAIT_REQUEST:
616 case HTTPSTATE_RECEIVE_DATA:
617 case HTTPSTATE_WAIT_FEED:
618 case RTSPSTATE_WAIT_REQUEST:
619 /* need to catch errors */
620 c->poll_entry = poll_entry;
621 poll_entry->fd = fd;
622 poll_entry->events = POLLIN;/* Maybe this will work */
623 poll_entry++;
624 break;
625 default:
626 c->poll_entry = NULL;
627 break;
629 c = c->next;
632 /* wait for an event on one connection. We poll at least every
633 second to handle timeouts */
634 do {
635 ret = poll(poll_table, poll_entry - poll_table, delay);
636 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
637 ff_neterrno() != FF_NETERROR(EINTR))
638 return -1;
639 } while (ret < 0);
641 cur_time = av_gettime() / 1000;
643 if (need_to_start_children) {
644 need_to_start_children = 0;
645 start_children(first_feed);
648 /* now handle the events */
649 for(c = first_http_ctx; c != NULL; c = c_next) {
650 c_next = c->next;
651 if (handle_connection(c) < 0) {
652 /* close and free the connection */
653 log_connection(c);
654 close_connection(c);
658 poll_entry = poll_table;
659 if (server_fd) {
660 /* new HTTP connection request ? */
661 if (poll_entry->revents & POLLIN)
662 new_connection(server_fd, 0);
663 poll_entry++;
665 if (rtsp_server_fd) {
666 /* new RTSP connection request ? */
667 if (poll_entry->revents & POLLIN)
668 new_connection(rtsp_server_fd, 1);
673 /* start waiting for a new HTTP/RTSP request */
674 static void start_wait_request(HTTPContext *c, int is_rtsp)
676 c->buffer_ptr = c->buffer;
677 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
679 if (is_rtsp) {
680 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
681 c->state = RTSPSTATE_WAIT_REQUEST;
682 } else {
683 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
684 c->state = HTTPSTATE_WAIT_REQUEST;
688 static void new_connection(int server_fd, int is_rtsp)
690 struct sockaddr_in from_addr;
691 int fd, len;
692 HTTPContext *c = NULL;
694 len = sizeof(from_addr);
695 fd = accept(server_fd, (struct sockaddr *)&from_addr,
696 &len);
697 if (fd < 0) {
698 http_log("error during accept %s\n", strerror(errno));
699 return;
701 ff_socket_nonblock(fd, 1);
703 /* XXX: should output a warning page when coming
704 close to the connection limit */
705 if (nb_connections >= nb_max_connections)
706 goto fail;
708 /* add a new connection */
709 c = av_mallocz(sizeof(HTTPContext));
710 if (!c)
711 goto fail;
713 c->fd = fd;
714 c->poll_entry = NULL;
715 c->from_addr = from_addr;
716 c->buffer_size = IOBUFFER_INIT_SIZE;
717 c->buffer = av_malloc(c->buffer_size);
718 if (!c->buffer)
719 goto fail;
721 c->next = first_http_ctx;
722 first_http_ctx = c;
723 nb_connections++;
725 start_wait_request(c, is_rtsp);
727 return;
729 fail:
730 if (c) {
731 av_free(c->buffer);
732 av_free(c);
734 closesocket(fd);
737 static void close_connection(HTTPContext *c)
739 HTTPContext **cp, *c1;
740 int i, nb_streams;
741 AVFormatContext *ctx;
742 URLContext *h;
743 AVStream *st;
745 /* remove connection from list */
746 cp = &first_http_ctx;
747 while ((*cp) != NULL) {
748 c1 = *cp;
749 if (c1 == c)
750 *cp = c->next;
751 else
752 cp = &c1->next;
755 /* remove references, if any (XXX: do it faster) */
756 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
757 if (c1->rtsp_c == c)
758 c1->rtsp_c = NULL;
761 /* remove connection associated resources */
762 if (c->fd >= 0)
763 closesocket(c->fd);
764 if (c->fmt_in) {
765 /* close each frame parser */
766 for(i=0;i<c->fmt_in->nb_streams;i++) {
767 st = c->fmt_in->streams[i];
768 if (st->codec->codec)
769 avcodec_close(st->codec);
771 av_close_input_file(c->fmt_in);
774 /* free RTP output streams if any */
775 nb_streams = 0;
776 if (c->stream)
777 nb_streams = c->stream->nb_streams;
779 for(i=0;i<nb_streams;i++) {
780 ctx = c->rtp_ctx[i];
781 if (ctx) {
782 av_write_trailer(ctx);
783 av_free(ctx);
785 h = c->rtp_handles[i];
786 if (h)
787 url_close(h);
790 ctx = &c->fmt_ctx;
792 if (!c->last_packet_sent) {
793 if (ctx->oformat) {
794 /* prepare header */
795 if (url_open_dyn_buf(&ctx->pb) >= 0) {
796 av_write_trailer(ctx);
797 av_freep(&c->pb_buffer);
798 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
803 for(i=0; i<ctx->nb_streams; i++)
804 av_free(ctx->streams[i]);
806 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
807 current_bandwidth -= c->stream->bandwidth;
809 /* signal that there is no feed if we are the feeder socket */
810 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
811 c->stream->feed_opened = 0;
812 close(c->feed_fd);
815 av_freep(&c->pb_buffer);
816 av_freep(&c->packet_buffer);
817 av_free(c->buffer);
818 av_free(c);
819 nb_connections--;
822 static int handle_connection(HTTPContext *c)
824 int len, ret;
826 switch(c->state) {
827 case HTTPSTATE_WAIT_REQUEST:
828 case RTSPSTATE_WAIT_REQUEST:
829 /* timeout ? */
830 if ((c->timeout - cur_time) < 0)
831 return -1;
832 if (c->poll_entry->revents & (POLLERR | POLLHUP))
833 return -1;
835 /* no need to read if no events */
836 if (!(c->poll_entry->revents & POLLIN))
837 return 0;
838 /* read the data */
839 read_loop:
840 len = recv(c->fd, c->buffer_ptr, 1, 0);
841 if (len < 0) {
842 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
843 ff_neterrno() != FF_NETERROR(EINTR))
844 return -1;
845 } else if (len == 0) {
846 return -1;
847 } else {
848 /* search for end of request. */
849 uint8_t *ptr;
850 c->buffer_ptr += len;
851 ptr = c->buffer_ptr;
852 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
853 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
854 /* request found : parse it and reply */
855 if (c->state == HTTPSTATE_WAIT_REQUEST) {
856 ret = http_parse_request(c);
857 } else {
858 ret = rtsp_parse_request(c);
860 if (ret < 0)
861 return -1;
862 } else if (ptr >= c->buffer_end) {
863 /* request too long: cannot do anything */
864 return -1;
865 } else goto read_loop;
867 break;
869 case HTTPSTATE_SEND_HEADER:
870 if (c->poll_entry->revents & (POLLERR | POLLHUP))
871 return -1;
873 /* no need to write if no events */
874 if (!(c->poll_entry->revents & POLLOUT))
875 return 0;
876 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
877 if (len < 0) {
878 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
879 ff_neterrno() != FF_NETERROR(EINTR)) {
880 /* error : close connection */
881 av_freep(&c->pb_buffer);
882 return -1;
884 } else {
885 c->buffer_ptr += len;
886 if (c->stream)
887 c->stream->bytes_served += len;
888 c->data_count += len;
889 if (c->buffer_ptr >= c->buffer_end) {
890 av_freep(&c->pb_buffer);
891 /* if error, exit */
892 if (c->http_error)
893 return -1;
894 /* all the buffer was sent : synchronize to the incoming stream */
895 c->state = HTTPSTATE_SEND_DATA_HEADER;
896 c->buffer_ptr = c->buffer_end = c->buffer;
899 break;
901 case HTTPSTATE_SEND_DATA:
902 case HTTPSTATE_SEND_DATA_HEADER:
903 case HTTPSTATE_SEND_DATA_TRAILER:
904 /* for packetized output, we consider we can always write (the
905 input streams sets the speed). It may be better to verify
906 that we do not rely too much on the kernel queues */
907 if (!c->is_packetized) {
908 if (c->poll_entry->revents & (POLLERR | POLLHUP))
909 return -1;
911 /* no need to read if no events */
912 if (!(c->poll_entry->revents & POLLOUT))
913 return 0;
915 if (http_send_data(c) < 0)
916 return -1;
917 /* close connection if trailer sent */
918 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
919 return -1;
920 break;
921 case HTTPSTATE_RECEIVE_DATA:
922 /* no need to read if no events */
923 if (c->poll_entry->revents & (POLLERR | POLLHUP))
924 return -1;
925 if (!(c->poll_entry->revents & POLLIN))
926 return 0;
927 if (http_receive_data(c) < 0)
928 return -1;
929 break;
930 case HTTPSTATE_WAIT_FEED:
931 /* no need to read if no events */
932 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
933 return -1;
935 /* nothing to do, we'll be waken up by incoming feed packets */
936 break;
938 case RTSPSTATE_SEND_REPLY:
939 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
940 av_freep(&c->pb_buffer);
941 return -1;
943 /* no need to write if no events */
944 if (!(c->poll_entry->revents & POLLOUT))
945 return 0;
946 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
947 if (len < 0) {
948 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
949 ff_neterrno() != FF_NETERROR(EINTR)) {
950 /* error : close connection */
951 av_freep(&c->pb_buffer);
952 return -1;
954 } else {
955 c->buffer_ptr += len;
956 c->data_count += len;
957 if (c->buffer_ptr >= c->buffer_end) {
958 /* all the buffer was sent : wait for a new request */
959 av_freep(&c->pb_buffer);
960 start_wait_request(c, 1);
963 break;
964 case RTSPSTATE_SEND_PACKET:
965 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
966 av_freep(&c->packet_buffer);
967 return -1;
969 /* no need to write if no events */
970 if (!(c->poll_entry->revents & POLLOUT))
971 return 0;
972 len = send(c->fd, c->packet_buffer_ptr,
973 c->packet_buffer_end - c->packet_buffer_ptr, 0);
974 if (len < 0) {
975 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
976 ff_neterrno() != FF_NETERROR(EINTR)) {
977 /* error : close connection */
978 av_freep(&c->packet_buffer);
979 return -1;
981 } else {
982 c->packet_buffer_ptr += len;
983 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
984 /* all the buffer was sent : wait for a new request */
985 av_freep(&c->packet_buffer);
986 c->state = RTSPSTATE_WAIT_REQUEST;
989 break;
990 case HTTPSTATE_READY:
991 /* nothing to do */
992 break;
993 default:
994 return -1;
996 return 0;
999 static int extract_rates(char *rates, int ratelen, const char *request)
1001 const char *p;
1003 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1004 if (strncasecmp(p, "Pragma:", 7) == 0) {
1005 const char *q = p + 7;
1007 while (*q && *q != '\n' && isspace(*q))
1008 q++;
1010 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1011 int stream_no;
1012 int rate_no;
1014 q += 20;
1016 memset(rates, 0xff, ratelen);
1018 while (1) {
1019 while (*q && *q != '\n' && *q != ':')
1020 q++;
1022 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1023 break;
1025 stream_no--;
1026 if (stream_no < ratelen && stream_no >= 0)
1027 rates[stream_no] = rate_no;
1029 while (*q && *q != '\n' && !isspace(*q))
1030 q++;
1033 return 1;
1036 p = strchr(p, '\n');
1037 if (!p)
1038 break;
1040 p++;
1043 return 0;
1046 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1048 int i;
1049 int best_bitrate = 100000000;
1050 int best = -1;
1052 for (i = 0; i < feed->nb_streams; i++) {
1053 AVCodecContext *feed_codec = feed->streams[i]->codec;
1055 if (feed_codec->codec_id != codec->codec_id ||
1056 feed_codec->sample_rate != codec->sample_rate ||
1057 feed_codec->width != codec->width ||
1058 feed_codec->height != codec->height)
1059 continue;
1061 /* Potential stream */
1063 /* We want the fastest stream less than bit_rate, or the slowest
1064 * faster than bit_rate
1067 if (feed_codec->bit_rate <= bit_rate) {
1068 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1069 best_bitrate = feed_codec->bit_rate;
1070 best = i;
1072 } else {
1073 if (feed_codec->bit_rate < best_bitrate) {
1074 best_bitrate = feed_codec->bit_rate;
1075 best = i;
1080 return best;
1083 static int modify_current_stream(HTTPContext *c, char *rates)
1085 int i;
1086 FFStream *req = c->stream;
1087 int action_required = 0;
1089 /* Not much we can do for a feed */
1090 if (!req->feed)
1091 return 0;
1093 for (i = 0; i < req->nb_streams; i++) {
1094 AVCodecContext *codec = req->streams[i]->codec;
1096 switch(rates[i]) {
1097 case 0:
1098 c->switch_feed_streams[i] = req->feed_streams[i];
1099 break;
1100 case 1:
1101 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1102 break;
1103 case 2:
1104 /* Wants off or slow */
1105 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1106 #ifdef WANTS_OFF
1107 /* This doesn't work well when it turns off the only stream! */
1108 c->switch_feed_streams[i] = -2;
1109 c->feed_streams[i] = -2;
1110 #endif
1111 break;
1114 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1115 action_required = 1;
1118 return action_required;
1122 static void do_switch_stream(HTTPContext *c, int i)
1124 if (c->switch_feed_streams[i] >= 0) {
1125 #ifdef PHILIP
1126 c->feed_streams[i] = c->switch_feed_streams[i];
1127 #endif
1129 /* Now update the stream */
1131 c->switch_feed_streams[i] = -1;
1134 /* XXX: factorize in utils.c ? */
1135 /* XXX: take care with different space meaning */
1136 static void skip_spaces(const char **pp)
1138 const char *p;
1139 p = *pp;
1140 while (*p == ' ' || *p == '\t')
1141 p++;
1142 *pp = p;
1145 static void get_word(char *buf, int buf_size, const char **pp)
1147 const char *p;
1148 char *q;
1150 p = *pp;
1151 skip_spaces(&p);
1152 q = buf;
1153 while (!isspace(*p) && *p != '\0') {
1154 if ((q - buf) < buf_size - 1)
1155 *q++ = *p;
1156 p++;
1158 if (buf_size > 0)
1159 *q = '\0';
1160 *pp = p;
1163 static int validate_acl(FFStream *stream, HTTPContext *c)
1165 enum IPAddressAction last_action = IP_DENY;
1166 IPAddressACL *acl;
1167 struct in_addr *src = &c->from_addr.sin_addr;
1168 unsigned long src_addr = src->s_addr;
1170 for (acl = stream->acl; acl; acl = acl->next) {
1171 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1172 return (acl->action == IP_ALLOW) ? 1 : 0;
1173 last_action = acl->action;
1176 /* Nothing matched, so return not the last action */
1177 return (last_action == IP_DENY) ? 1 : 0;
1180 /* compute the real filename of a file by matching it without its
1181 extensions to all the stream filenames */
1182 static void compute_real_filename(char *filename, int max_size)
1184 char file1[1024];
1185 char file2[1024];
1186 char *p;
1187 FFStream *stream;
1189 /* compute filename by matching without the file extensions */
1190 av_strlcpy(file1, filename, sizeof(file1));
1191 p = strrchr(file1, '.');
1192 if (p)
1193 *p = '\0';
1194 for(stream = first_stream; stream != NULL; stream = stream->next) {
1195 av_strlcpy(file2, stream->filename, sizeof(file2));
1196 p = strrchr(file2, '.');
1197 if (p)
1198 *p = '\0';
1199 if (!strcmp(file1, file2)) {
1200 av_strlcpy(filename, stream->filename, max_size);
1201 break;
1206 enum RedirType {
1207 REDIR_NONE,
1208 REDIR_ASX,
1209 REDIR_RAM,
1210 REDIR_ASF,
1211 REDIR_RTSP,
1212 REDIR_SDP,
1215 /* parse http request and prepare header */
1216 static int http_parse_request(HTTPContext *c)
1218 char *p;
1219 enum RedirType redir_type;
1220 char cmd[32];
1221 char info[1024], filename[1024];
1222 char url[1024], *q;
1223 char protocol[32];
1224 char msg[1024];
1225 const char *mime_type;
1226 FFStream *stream;
1227 int i;
1228 char ratebuf[32];
1229 char *useragent = 0;
1231 p = c->buffer;
1232 get_word(cmd, sizeof(cmd), (const char **)&p);
1233 av_strlcpy(c->method, cmd, sizeof(c->method));
1235 if (!strcmp(cmd, "GET"))
1236 c->post = 0;
1237 else if (!strcmp(cmd, "POST"))
1238 c->post = 1;
1239 else
1240 return -1;
1242 get_word(url, sizeof(url), (const char **)&p);
1243 av_strlcpy(c->url, url, sizeof(c->url));
1245 get_word(protocol, sizeof(protocol), (const char **)&p);
1246 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1247 return -1;
1249 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1251 if (ffserver_debug)
1252 http_log("New connection: %s %s\n", cmd, url);
1254 /* find the filename and the optional info string in the request */
1255 p = strchr(url, '?');
1256 if (p) {
1257 av_strlcpy(info, p, sizeof(info));
1258 *p = '\0';
1259 } else
1260 info[0] = '\0';
1262 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1264 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1265 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1266 useragent = p + 11;
1267 if (*useragent && *useragent != '\n' && isspace(*useragent))
1268 useragent++;
1269 break;
1271 p = strchr(p, '\n');
1272 if (!p)
1273 break;
1275 p++;
1278 redir_type = REDIR_NONE;
1279 if (match_ext(filename, "asx")) {
1280 redir_type = REDIR_ASX;
1281 filename[strlen(filename)-1] = 'f';
1282 } else if (match_ext(filename, "asf") &&
1283 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1284 /* if this isn't WMP or lookalike, return the redirector file */
1285 redir_type = REDIR_ASF;
1286 } else if (match_ext(filename, "rpm,ram")) {
1287 redir_type = REDIR_RAM;
1288 strcpy(filename + strlen(filename)-2, "m");
1289 } else if (match_ext(filename, "rtsp")) {
1290 redir_type = REDIR_RTSP;
1291 compute_real_filename(filename, sizeof(filename) - 1);
1292 } else if (match_ext(filename, "sdp")) {
1293 redir_type = REDIR_SDP;
1294 compute_real_filename(filename, sizeof(filename) - 1);
1297 // "redirect" / request to index.html
1298 if (!strlen(filename))
1299 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1301 stream = first_stream;
1302 while (stream != NULL) {
1303 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1304 break;
1305 stream = stream->next;
1307 if (stream == NULL) {
1308 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1309 goto send_error;
1312 c->stream = stream;
1313 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1314 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1316 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1317 c->http_error = 301;
1318 q = c->buffer;
1319 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1320 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1321 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1322 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1323 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1324 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1325 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1327 /* prepare output buffer */
1328 c->buffer_ptr = c->buffer;
1329 c->buffer_end = q;
1330 c->state = HTTPSTATE_SEND_HEADER;
1331 return 0;
1334 /* If this is WMP, get the rate information */
1335 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1336 if (modify_current_stream(c, ratebuf)) {
1337 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1338 if (c->switch_feed_streams[i] >= 0)
1339 do_switch_stream(c, i);
1344 /* If already streaming this feed, do not let start another feeder. */
1345 if (stream->feed_opened) {
1346 snprintf(msg, sizeof(msg), "This feed is already being received.");
1347 http_log("feed %s already being received\n", stream->feed_filename);
1348 goto send_error;
1351 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1352 current_bandwidth += stream->bandwidth;
1354 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1355 c->http_error = 200;
1356 q = c->buffer;
1357 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1358 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1359 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1360 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1361 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");
1362 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<p>The bandwidth being served (including your stream) is %lldkbit/sec, and this exceeds the limit of %lldkbit/sec.</p>\r\n",
1363 current_bandwidth, max_bandwidth);
1364 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1366 /* prepare output buffer */
1367 c->buffer_ptr = c->buffer;
1368 c->buffer_end = q;
1369 c->state = HTTPSTATE_SEND_HEADER;
1370 return 0;
1373 if (redir_type != REDIR_NONE) {
1374 char *hostinfo = 0;
1376 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1377 if (strncasecmp(p, "Host:", 5) == 0) {
1378 hostinfo = p + 5;
1379 break;
1381 p = strchr(p, '\n');
1382 if (!p)
1383 break;
1385 p++;
1388 if (hostinfo) {
1389 char *eoh;
1390 char hostbuf[260];
1392 while (isspace(*hostinfo))
1393 hostinfo++;
1395 eoh = strchr(hostinfo, '\n');
1396 if (eoh) {
1397 if (eoh[-1] == '\r')
1398 eoh--;
1400 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1401 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1402 hostbuf[eoh - hostinfo] = 0;
1404 c->http_error = 200;
1405 q = c->buffer;
1406 switch(redir_type) {
1407 case REDIR_ASX:
1408 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1409 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\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, "<ASX Version=\"3\">\r\n");
1412 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1413 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1414 hostbuf, filename, info);
1415 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1416 break;
1417 case REDIR_RAM:
1418 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1419 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1420 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1421 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1422 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1423 hostbuf, filename, info);
1424 break;
1425 case REDIR_ASF:
1426 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1427 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1428 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1429 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1430 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1431 hostbuf, filename, info);
1432 break;
1433 case REDIR_RTSP:
1435 char hostname[256], *p;
1436 /* extract only hostname */
1437 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1438 p = strrchr(hostname, ':');
1439 if (p)
1440 *p = '\0';
1441 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1442 /* XXX: incorrect mime type ? */
1443 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1444 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1445 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1446 hostname, ntohs(my_rtsp_addr.sin_port),
1447 filename);
1449 break;
1450 case REDIR_SDP:
1452 uint8_t *sdp_data;
1453 int sdp_data_size, len;
1454 struct sockaddr_in my_addr;
1456 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1457 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1458 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1460 len = sizeof(my_addr);
1461 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1463 /* XXX: should use a dynamic buffer */
1464 sdp_data_size = prepare_sdp_description(stream,
1465 &sdp_data,
1466 my_addr.sin_addr);
1467 if (sdp_data_size > 0) {
1468 memcpy(q, sdp_data, sdp_data_size);
1469 q += sdp_data_size;
1470 *q = '\0';
1471 av_free(sdp_data);
1474 break;
1475 default:
1476 abort();
1477 break;
1480 /* prepare output buffer */
1481 c->buffer_ptr = c->buffer;
1482 c->buffer_end = q;
1483 c->state = HTTPSTATE_SEND_HEADER;
1484 return 0;
1489 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1490 goto send_error;
1493 stream->conns_served++;
1495 /* XXX: add there authenticate and IP match */
1497 if (c->post) {
1498 /* if post, it means a feed is being sent */
1499 if (!stream->is_feed) {
1500 /* However it might be a status report from WMP! Let us log the
1501 * data as it might come in handy one day. */
1502 char *logline = 0;
1503 int client_id = 0;
1505 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1506 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1507 logline = p;
1508 break;
1510 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1511 client_id = strtol(p + 18, 0, 10);
1512 p = strchr(p, '\n');
1513 if (!p)
1514 break;
1516 p++;
1519 if (logline) {
1520 char *eol = strchr(logline, '\n');
1522 logline += 17;
1524 if (eol) {
1525 if (eol[-1] == '\r')
1526 eol--;
1527 http_log("%.*s\n", (int) (eol - logline), logline);
1528 c->suppress_log = 1;
1532 #ifdef DEBUG_WMP
1533 http_log("\nGot request:\n%s\n", c->buffer);
1534 #endif
1536 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1537 HTTPContext *wmpc;
1539 /* Now we have to find the client_id */
1540 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1541 if (wmpc->wmp_client_id == client_id)
1542 break;
1545 if (wmpc && modify_current_stream(wmpc, ratebuf))
1546 wmpc->switch_pending = 1;
1549 snprintf(msg, sizeof(msg), "POST command not handled");
1550 c->stream = 0;
1551 goto send_error;
1553 if (http_start_receive_data(c) < 0) {
1554 snprintf(msg, sizeof(msg), "could not open feed");
1555 goto send_error;
1557 c->http_error = 0;
1558 c->state = HTTPSTATE_RECEIVE_DATA;
1559 return 0;
1562 #ifdef DEBUG_WMP
1563 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1564 http_log("\nGot request:\n%s\n", c->buffer);
1565 #endif
1567 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1568 goto send_status;
1570 /* open input stream */
1571 if (open_input_stream(c, info) < 0) {
1572 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1573 goto send_error;
1576 /* prepare http header */
1577 q = c->buffer;
1578 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1579 mime_type = c->stream->fmt->mime_type;
1580 if (!mime_type)
1581 mime_type = "application/x-octet-stream";
1582 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1584 /* for asf, we need extra headers */
1585 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1586 /* Need to allocate a client id */
1588 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1590 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);
1592 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1593 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1595 /* prepare output buffer */
1596 c->http_error = 0;
1597 c->buffer_ptr = c->buffer;
1598 c->buffer_end = q;
1599 c->state = HTTPSTATE_SEND_HEADER;
1600 return 0;
1601 send_error:
1602 c->http_error = 404;
1603 q = c->buffer;
1604 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1605 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1606 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1607 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1608 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1609 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1610 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1612 /* prepare output buffer */
1613 c->buffer_ptr = c->buffer;
1614 c->buffer_end = q;
1615 c->state = HTTPSTATE_SEND_HEADER;
1616 return 0;
1617 send_status:
1618 compute_status(c);
1619 c->http_error = 200; /* horrible : we use this value to avoid
1620 going to the send data state */
1621 c->state = HTTPSTATE_SEND_HEADER;
1622 return 0;
1625 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1627 static const char *suffix = " kMGTP";
1628 const char *s;
1630 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1632 url_fprintf(pb, "%"PRId64"%c", count, *s);
1635 static void compute_status(HTTPContext *c)
1637 HTTPContext *c1;
1638 FFStream *stream;
1639 char *p;
1640 time_t ti;
1641 int i, len;
1642 ByteIOContext *pb;
1644 if (url_open_dyn_buf(&pb) < 0) {
1645 /* XXX: return an error ? */
1646 c->buffer_ptr = c->buffer;
1647 c->buffer_end = c->buffer;
1648 return;
1651 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1652 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1653 url_fprintf(pb, "Pragma: no-cache\r\n");
1654 url_fprintf(pb, "\r\n");
1656 url_fprintf(pb, "<HEAD><TITLE>%s Status</TITLE>\n", program_name);
1657 if (c->stream->feed_filename[0])
1658 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1659 url_fprintf(pb, "</HEAD>\n<BODY>");
1660 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1661 /* format status */
1662 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1663 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1664 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");
1665 stream = first_stream;
1666 while (stream != NULL) {
1667 char sfilename[1024];
1668 char *eosf;
1670 if (stream->feed != stream) {
1671 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1672 eosf = sfilename + strlen(sfilename);
1673 if (eosf - sfilename >= 4) {
1674 if (strcmp(eosf - 4, ".asf") == 0)
1675 strcpy(eosf - 4, ".asx");
1676 else if (strcmp(eosf - 3, ".rm") == 0)
1677 strcpy(eosf - 3, ".ram");
1678 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1679 /* generate a sample RTSP director if
1680 unicast. Generate an SDP redirector if
1681 multicast */
1682 eosf = strrchr(sfilename, '.');
1683 if (!eosf)
1684 eosf = sfilename + strlen(sfilename);
1685 if (stream->is_multicast)
1686 strcpy(eosf, ".sdp");
1687 else
1688 strcpy(eosf, ".rtsp");
1692 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1693 sfilename, stream->filename);
1694 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1695 stream->conns_served);
1696 fmt_bytecount(pb, stream->bytes_served);
1697 switch(stream->stream_type) {
1698 case STREAM_TYPE_LIVE: {
1699 int audio_bit_rate = 0;
1700 int video_bit_rate = 0;
1701 const char *audio_codec_name = "";
1702 const char *video_codec_name = "";
1703 const char *audio_codec_name_extra = "";
1704 const char *video_codec_name_extra = "";
1706 for(i=0;i<stream->nb_streams;i++) {
1707 AVStream *st = stream->streams[i];
1708 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1709 switch(st->codec->codec_type) {
1710 case CODEC_TYPE_AUDIO:
1711 audio_bit_rate += st->codec->bit_rate;
1712 if (codec) {
1713 if (*audio_codec_name)
1714 audio_codec_name_extra = "...";
1715 audio_codec_name = codec->name;
1717 break;
1718 case CODEC_TYPE_VIDEO:
1719 video_bit_rate += st->codec->bit_rate;
1720 if (codec) {
1721 if (*video_codec_name)
1722 video_codec_name_extra = "...";
1723 video_codec_name = codec->name;
1725 break;
1726 case CODEC_TYPE_DATA:
1727 video_bit_rate += st->codec->bit_rate;
1728 break;
1729 default:
1730 abort();
1733 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",
1734 stream->fmt->name,
1735 stream->bandwidth,
1736 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1737 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1738 if (stream->feed)
1739 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1740 else
1741 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1742 url_fprintf(pb, "\n");
1744 break;
1745 default:
1746 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1747 break;
1750 stream = stream->next;
1752 url_fprintf(pb, "</TABLE>\n");
1754 stream = first_stream;
1755 while (stream != NULL) {
1756 if (stream->feed == stream) {
1757 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1758 if (stream->pid) {
1759 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1761 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1763 FILE *pid_stat;
1764 char ps_cmd[64];
1766 /* This is somewhat linux specific I guess */
1767 snprintf(ps_cmd, sizeof(ps_cmd),
1768 "ps -o \"%%cpu,cputime\" --no-headers %d",
1769 stream->pid);
1771 pid_stat = popen(ps_cmd, "r");
1772 if (pid_stat) {
1773 char cpuperc[10];
1774 char cpuused[64];
1776 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1777 cpuused) == 2) {
1778 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1779 cpuperc, cpuused);
1781 fclose(pid_stat);
1784 #endif
1786 url_fprintf(pb, "<p>");
1788 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");
1790 for (i = 0; i < stream->nb_streams; i++) {
1791 AVStream *st = stream->streams[i];
1792 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1793 const char *type = "unknown";
1794 char parameters[64];
1796 parameters[0] = 0;
1798 switch(st->codec->codec_type) {
1799 case CODEC_TYPE_AUDIO:
1800 type = "audio";
1801 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1802 break;
1803 case CODEC_TYPE_VIDEO:
1804 type = "video";
1805 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1806 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1807 break;
1808 default:
1809 abort();
1811 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1812 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1814 url_fprintf(pb, "</table>\n");
1817 stream = stream->next;
1820 #if 0
1822 float avg;
1823 AVCodecContext *enc;
1824 char buf[1024];
1826 /* feed status */
1827 stream = first_feed;
1828 while (stream != NULL) {
1829 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1830 url_fprintf(pb, "<TABLE>\n");
1831 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1832 for(i=0;i<stream->nb_streams;i++) {
1833 AVStream *st = stream->streams[i];
1834 FeedData *fdata = st->priv_data;
1835 enc = st->codec;
1837 avcodec_string(buf, sizeof(buf), enc);
1838 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1839 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1840 avg /= enc->frame_size;
1841 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1842 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1844 url_fprintf(pb, "</TABLE>\n");
1845 stream = stream->next_feed;
1848 #endif
1850 /* connection status */
1851 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1853 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1854 nb_connections, nb_max_connections);
1856 url_fprintf(pb, "Bandwidth in use: %lldk / %lldk<BR>\n",
1857 current_bandwidth, max_bandwidth);
1859 url_fprintf(pb, "<TABLE>\n");
1860 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");
1861 c1 = first_http_ctx;
1862 i = 0;
1863 while (c1 != NULL) {
1864 int bitrate;
1865 int j;
1867 bitrate = 0;
1868 if (c1->stream) {
1869 for (j = 0; j < c1->stream->nb_streams; j++) {
1870 if (!c1->stream->feed)
1871 bitrate += c1->stream->streams[j]->codec->bit_rate;
1872 else if (c1->feed_streams[j] >= 0)
1873 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1877 i++;
1878 p = inet_ntoa(c1->from_addr.sin_addr);
1879 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1881 c1->stream ? c1->stream->filename : "",
1882 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1884 c1->protocol,
1885 http_state[c1->state]);
1886 fmt_bytecount(pb, bitrate);
1887 url_fprintf(pb, "<td align=right>");
1888 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1889 url_fprintf(pb, "<td align=right>");
1890 fmt_bytecount(pb, c1->data_count);
1891 url_fprintf(pb, "\n");
1892 c1 = c1->next;
1894 url_fprintf(pb, "</TABLE>\n");
1896 /* date */
1897 ti = time(NULL);
1898 p = ctime(&ti);
1899 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1900 url_fprintf(pb, "</BODY>\n</HTML>\n");
1902 len = url_close_dyn_buf(pb, &c->pb_buffer);
1903 c->buffer_ptr = c->pb_buffer;
1904 c->buffer_end = c->pb_buffer + len;
1907 /* check if the parser needs to be opened for stream i */
1908 static void open_parser(AVFormatContext *s, int i)
1910 AVStream *st = s->streams[i];
1911 AVCodec *codec;
1913 if (!st->codec->codec) {
1914 codec = avcodec_find_decoder(st->codec->codec_id);
1915 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1916 st->codec->parse_only = 1;
1917 if (avcodec_open(st->codec, codec) < 0)
1918 st->codec->parse_only = 0;
1923 static int open_input_stream(HTTPContext *c, const char *info)
1925 char buf[128];
1926 char input_filename[1024];
1927 AVFormatContext *s;
1928 int buf_size, i, ret;
1929 int64_t stream_pos;
1931 /* find file name */
1932 if (c->stream->feed) {
1933 strcpy(input_filename, c->stream->feed->feed_filename);
1934 buf_size = FFM_PACKET_SIZE;
1935 /* compute position (absolute time) */
1936 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1937 stream_pos = parse_date(buf, 0);
1938 if (stream_pos == INT64_MIN)
1939 return -1;
1940 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1941 int prebuffer = strtol(buf, 0, 10);
1942 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1943 } else
1944 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1945 } else {
1946 strcpy(input_filename, c->stream->feed_filename);
1947 buf_size = 0;
1948 /* compute position (relative time) */
1949 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1950 stream_pos = parse_date(buf, 1);
1951 if (stream_pos == INT64_MIN)
1952 return -1;
1953 } else
1954 stream_pos = 0;
1956 if (input_filename[0] == '\0')
1957 return -1;
1959 #if 0
1960 { time_t when = stream_pos / 1000000;
1961 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1963 #endif
1965 /* open stream */
1966 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1967 buf_size, c->stream->ap_in)) < 0) {
1968 http_log("could not open %s: %d\n", input_filename, ret);
1969 return -1;
1971 s->flags |= AVFMT_FLAG_GENPTS;
1972 c->fmt_in = s;
1973 av_find_stream_info(c->fmt_in);
1975 /* open each parser */
1976 for(i=0;i<s->nb_streams;i++)
1977 open_parser(s, i);
1979 /* choose stream as clock source (we favorize video stream if
1980 present) for packet sending */
1981 c->pts_stream_index = 0;
1982 for(i=0;i<c->stream->nb_streams;i++) {
1983 if (c->pts_stream_index == 0 &&
1984 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1985 c->pts_stream_index = i;
1989 #if 1
1990 if (c->fmt_in->iformat->read_seek)
1991 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
1992 #endif
1993 /* set the start time (needed for maxtime and RTP packet timing) */
1994 c->start_time = cur_time;
1995 c->first_pts = AV_NOPTS_VALUE;
1996 return 0;
1999 /* return the server clock (in us) */
2000 static int64_t get_server_clock(HTTPContext *c)
2002 /* compute current pts value from system time */
2003 return (cur_time - c->start_time) * 1000;
2006 /* return the estimated time at which the current packet must be sent
2007 (in us) */
2008 static int64_t get_packet_send_clock(HTTPContext *c)
2010 int bytes_left, bytes_sent, frame_bytes;
2012 frame_bytes = c->cur_frame_bytes;
2013 if (frame_bytes <= 0)
2014 return c->cur_pts;
2015 else {
2016 bytes_left = c->buffer_end - c->buffer_ptr;
2017 bytes_sent = frame_bytes - bytes_left;
2018 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2023 static int http_prepare_data(HTTPContext *c)
2025 int i, len, ret;
2026 AVFormatContext *ctx;
2028 av_freep(&c->pb_buffer);
2029 switch(c->state) {
2030 case HTTPSTATE_SEND_DATA_HEADER:
2031 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2032 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2033 sizeof(c->fmt_ctx.author));
2034 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2035 sizeof(c->fmt_ctx.comment));
2036 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2037 sizeof(c->fmt_ctx.copyright));
2038 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2039 sizeof(c->fmt_ctx.title));
2041 for(i=0;i<c->stream->nb_streams;i++) {
2042 AVStream *st;
2043 AVStream *src;
2044 st = av_mallocz(sizeof(AVStream));
2045 c->fmt_ctx.streams[i] = st;
2046 /* if file or feed, then just take streams from FFStream struct */
2047 if (!c->stream->feed ||
2048 c->stream->feed == c->stream)
2049 src = c->stream->streams[i];
2050 else
2051 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2053 *st = *src;
2054 st->priv_data = 0;
2055 st->codec->frame_number = 0; /* XXX: should be done in
2056 AVStream, not in codec */
2058 /* set output format parameters */
2059 c->fmt_ctx.oformat = c->stream->fmt;
2060 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2062 c->got_key_frame = 0;
2064 /* prepare header and save header data in a stream */
2065 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2066 /* XXX: potential leak */
2067 return -1;
2069 c->fmt_ctx.pb->is_streamed = 1;
2072 * HACK to avoid mpeg ps muxer to spit many underflow errors
2073 * Default value from FFmpeg
2074 * Try to set it use configuration option
2076 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2077 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2079 av_set_parameters(&c->fmt_ctx, NULL);
2080 if (av_write_header(&c->fmt_ctx) < 0) {
2081 http_log("Error writing output header\n");
2082 return -1;
2085 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2086 c->buffer_ptr = c->pb_buffer;
2087 c->buffer_end = c->pb_buffer + len;
2089 c->state = HTTPSTATE_SEND_DATA;
2090 c->last_packet_sent = 0;
2091 break;
2092 case HTTPSTATE_SEND_DATA:
2093 /* find a new packet */
2094 /* read a packet from the input stream */
2095 if (c->stream->feed)
2096 ffm_set_write_index(c->fmt_in,
2097 c->stream->feed->feed_write_index,
2098 c->stream->feed->feed_size);
2100 if (c->stream->max_time &&
2101 c->stream->max_time + c->start_time - cur_time < 0)
2102 /* We have timed out */
2103 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2104 else {
2105 AVPacket pkt;
2106 redo:
2107 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2108 if (c->stream->feed && c->stream->feed->feed_opened) {
2109 /* if coming from feed, it means we reached the end of the
2110 ffm file, so must wait for more data */
2111 c->state = HTTPSTATE_WAIT_FEED;
2112 return 1; /* state changed */
2113 } else {
2114 if (c->stream->loop) {
2115 av_close_input_file(c->fmt_in);
2116 c->fmt_in = NULL;
2117 if (open_input_stream(c, "") < 0)
2118 goto no_loop;
2119 goto redo;
2120 } else {
2121 no_loop:
2122 /* must send trailer now because eof or error */
2123 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2126 } else {
2127 int source_index = pkt.stream_index;
2128 /* update first pts if needed */
2129 if (c->first_pts == AV_NOPTS_VALUE) {
2130 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2131 c->start_time = cur_time;
2133 /* send it to the appropriate stream */
2134 if (c->stream->feed) {
2135 /* if coming from a feed, select the right stream */
2136 if (c->switch_pending) {
2137 c->switch_pending = 0;
2138 for(i=0;i<c->stream->nb_streams;i++) {
2139 if (c->switch_feed_streams[i] == pkt.stream_index)
2140 if (pkt.flags & PKT_FLAG_KEY)
2141 do_switch_stream(c, i);
2142 if (c->switch_feed_streams[i] >= 0)
2143 c->switch_pending = 1;
2146 for(i=0;i<c->stream->nb_streams;i++) {
2147 if (c->feed_streams[i] == pkt.stream_index) {
2148 AVStream *st = c->fmt_in->streams[source_index];
2149 pkt.stream_index = i;
2150 if (pkt.flags & PKT_FLAG_KEY &&
2151 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2152 c->stream->nb_streams == 1))
2153 c->got_key_frame = 1;
2154 if (!c->stream->send_on_key || c->got_key_frame)
2155 goto send_it;
2158 } else {
2159 AVCodecContext *codec;
2160 AVStream *ist, *ost;
2161 send_it:
2162 ist = c->fmt_in->streams[source_index];
2163 /* specific handling for RTP: we use several
2164 output stream (one for each RTP
2165 connection). XXX: need more abstract handling */
2166 if (c->is_packetized) {
2167 /* compute send time and duration */
2168 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2169 if (ist->start_time != AV_NOPTS_VALUE)
2170 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
2171 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2172 #if 0
2173 printf("index=%d pts=%0.3f duration=%0.6f\n",
2174 pkt.stream_index,
2175 (double)c->cur_pts /
2176 AV_TIME_BASE,
2177 (double)c->cur_frame_duration /
2178 AV_TIME_BASE);
2179 #endif
2180 /* find RTP context */
2181 c->packet_stream_index = pkt.stream_index;
2182 ctx = c->rtp_ctx[c->packet_stream_index];
2183 if(!ctx) {
2184 av_free_packet(&pkt);
2185 break;
2187 codec = ctx->streams[0]->codec;
2188 /* only one stream per RTP connection */
2189 pkt.stream_index = 0;
2190 } else {
2191 ctx = &c->fmt_ctx;
2192 /* Fudge here */
2193 codec = ctx->streams[pkt.stream_index]->codec;
2196 if (c->is_packetized) {
2197 int max_packet_size;
2198 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2199 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2200 else
2201 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2202 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2203 } else {
2204 ret = url_open_dyn_buf(&ctx->pb);
2206 if (ret < 0) {
2207 /* XXX: potential leak */
2208 return -1;
2210 ost = ctx->streams[pkt.stream_index];
2212 ctx->pb->is_streamed = 1;
2213 if (pkt.dts != AV_NOPTS_VALUE)
2214 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2215 if (pkt.pts != AV_NOPTS_VALUE)
2216 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2217 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2218 if (av_write_frame(ctx, &pkt) < 0) {
2219 http_log("Error writing frame to output\n");
2220 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2223 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2224 c->cur_frame_bytes = len;
2225 c->buffer_ptr = c->pb_buffer;
2226 c->buffer_end = c->pb_buffer + len;
2228 codec->frame_number++;
2229 if (len == 0) {
2230 av_free_packet(&pkt);
2231 goto redo;
2234 av_free_packet(&pkt);
2237 break;
2238 default:
2239 case HTTPSTATE_SEND_DATA_TRAILER:
2240 /* last packet test ? */
2241 if (c->last_packet_sent || c->is_packetized)
2242 return -1;
2243 ctx = &c->fmt_ctx;
2244 /* prepare header */
2245 if (url_open_dyn_buf(&ctx->pb) < 0) {
2246 /* XXX: potential leak */
2247 return -1;
2249 c->fmt_ctx.pb->is_streamed = 1;
2250 av_write_trailer(ctx);
2251 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2252 c->buffer_ptr = c->pb_buffer;
2253 c->buffer_end = c->pb_buffer + len;
2255 c->last_packet_sent = 1;
2256 break;
2258 return 0;
2261 /* should convert the format at the same time */
2262 /* send data starting at c->buffer_ptr to the output connection
2263 (either UDP or TCP connection) */
2264 static int http_send_data(HTTPContext *c)
2266 int len, ret;
2268 for(;;) {
2269 if (c->buffer_ptr >= c->buffer_end) {
2270 ret = http_prepare_data(c);
2271 if (ret < 0)
2272 return -1;
2273 else if (ret != 0)
2274 /* state change requested */
2275 break;
2276 } else {
2277 if (c->is_packetized) {
2278 /* RTP data output */
2279 len = c->buffer_end - c->buffer_ptr;
2280 if (len < 4) {
2281 /* fail safe - should never happen */
2282 fail1:
2283 c->buffer_ptr = c->buffer_end;
2284 return 0;
2286 len = (c->buffer_ptr[0] << 24) |
2287 (c->buffer_ptr[1] << 16) |
2288 (c->buffer_ptr[2] << 8) |
2289 (c->buffer_ptr[3]);
2290 if (len > (c->buffer_end - c->buffer_ptr))
2291 goto fail1;
2292 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2293 /* nothing to send yet: we can wait */
2294 return 0;
2297 c->data_count += len;
2298 update_datarate(&c->datarate, c->data_count);
2299 if (c->stream)
2300 c->stream->bytes_served += len;
2302 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2303 /* RTP packets are sent inside the RTSP TCP connection */
2304 ByteIOContext *pb;
2305 int interleaved_index, size;
2306 uint8_t header[4];
2307 HTTPContext *rtsp_c;
2309 rtsp_c = c->rtsp_c;
2310 /* if no RTSP connection left, error */
2311 if (!rtsp_c)
2312 return -1;
2313 /* if already sending something, then wait. */
2314 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2315 break;
2316 if (url_open_dyn_buf(&pb) < 0)
2317 goto fail1;
2318 interleaved_index = c->packet_stream_index * 2;
2319 /* RTCP packets are sent at odd indexes */
2320 if (c->buffer_ptr[1] == 200)
2321 interleaved_index++;
2322 /* write RTSP TCP header */
2323 header[0] = '$';
2324 header[1] = interleaved_index;
2325 header[2] = len >> 8;
2326 header[3] = len;
2327 put_buffer(pb, header, 4);
2328 /* write RTP packet data */
2329 c->buffer_ptr += 4;
2330 put_buffer(pb, c->buffer_ptr, len);
2331 size = url_close_dyn_buf(pb, &c->packet_buffer);
2332 /* prepare asynchronous TCP sending */
2333 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2334 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2335 c->buffer_ptr += len;
2337 /* send everything we can NOW */
2338 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2339 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2340 if (len > 0)
2341 rtsp_c->packet_buffer_ptr += len;
2342 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2343 /* if we could not send all the data, we will
2344 send it later, so a new state is needed to
2345 "lock" the RTSP TCP connection */
2346 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2347 break;
2348 } else
2349 /* all data has been sent */
2350 av_freep(&c->packet_buffer);
2351 } else {
2352 /* send RTP packet directly in UDP */
2353 c->buffer_ptr += 4;
2354 url_write(c->rtp_handles[c->packet_stream_index],
2355 c->buffer_ptr, len);
2356 c->buffer_ptr += len;
2357 /* here we continue as we can send several packets per 10 ms slot */
2359 } else {
2360 /* TCP data output */
2361 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2362 if (len < 0) {
2363 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2364 ff_neterrno() != FF_NETERROR(EINTR))
2365 /* error : close connection */
2366 return -1;
2367 else
2368 return 0;
2369 } else
2370 c->buffer_ptr += len;
2372 c->data_count += len;
2373 update_datarate(&c->datarate, c->data_count);
2374 if (c->stream)
2375 c->stream->bytes_served += len;
2376 break;
2379 } /* for(;;) */
2380 return 0;
2383 static int http_start_receive_data(HTTPContext *c)
2385 int fd;
2387 if (c->stream->feed_opened)
2388 return -1;
2390 /* Don't permit writing to this one */
2391 if (c->stream->readonly)
2392 return -1;
2394 /* open feed */
2395 fd = open(c->stream->feed_filename, O_RDWR);
2396 if (fd < 0) {
2397 http_log("Error opening feeder file: %s\n", strerror(errno));
2398 return -1;
2400 c->feed_fd = fd;
2402 c->stream->feed_write_index = ffm_read_write_index(fd);
2403 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2404 lseek(fd, 0, SEEK_SET);
2406 /* init buffer input */
2407 c->buffer_ptr = c->buffer;
2408 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2409 c->stream->feed_opened = 1;
2410 return 0;
2413 static int http_receive_data(HTTPContext *c)
2415 HTTPContext *c1;
2417 if (c->buffer_end > c->buffer_ptr) {
2418 int len;
2420 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2421 if (len < 0) {
2422 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2423 ff_neterrno() != FF_NETERROR(EINTR))
2424 /* error : close connection */
2425 goto fail;
2426 } else if (len == 0)
2427 /* end of connection : close it */
2428 goto fail;
2429 else {
2430 c->buffer_ptr += len;
2431 c->data_count += len;
2432 update_datarate(&c->datarate, c->data_count);
2436 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2437 if (c->buffer[0] != 'f' ||
2438 c->buffer[1] != 'm') {
2439 http_log("Feed stream has become desynchronized -- disconnecting\n");
2440 goto fail;
2444 if (c->buffer_ptr >= c->buffer_end) {
2445 FFStream *feed = c->stream;
2446 /* a packet has been received : write it in the store, except
2447 if header */
2448 if (c->data_count > FFM_PACKET_SIZE) {
2450 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2451 /* XXX: use llseek or url_seek */
2452 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2453 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2454 http_log("Error writing to feed file: %s\n", strerror(errno));
2455 goto fail;
2458 feed->feed_write_index += FFM_PACKET_SIZE;
2459 /* update file size */
2460 if (feed->feed_write_index > c->stream->feed_size)
2461 feed->feed_size = feed->feed_write_index;
2463 /* handle wrap around if max file size reached */
2464 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2465 feed->feed_write_index = FFM_PACKET_SIZE;
2467 /* write index */
2468 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2470 /* wake up any waiting connections */
2471 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2472 if (c1->state == HTTPSTATE_WAIT_FEED &&
2473 c1->stream->feed == c->stream->feed)
2474 c1->state = HTTPSTATE_SEND_DATA;
2476 } else {
2477 /* We have a header in our hands that contains useful data */
2478 AVFormatContext *s = NULL;
2479 ByteIOContext *pb;
2480 AVInputFormat *fmt_in;
2481 int i;
2483 /* use feed output format name to find corresponding input format */
2484 fmt_in = av_find_input_format(feed->fmt->name);
2485 if (!fmt_in)
2486 goto fail;
2488 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2489 pb->is_streamed = 1;
2491 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2492 av_free(pb);
2493 goto fail;
2496 /* Now we have the actual streams */
2497 if (s->nb_streams != feed->nb_streams) {
2498 av_close_input_stream(s);
2499 av_free(pb);
2500 goto fail;
2503 for (i = 0; i < s->nb_streams; i++) {
2504 AVStream *fst = feed->streams[i];
2505 AVStream *st = s->streams[i];
2506 memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
2507 if (fst->codec->extradata_size) {
2508 fst->codec->extradata = av_malloc(fst->codec->extradata_size);
2509 if (!fst->codec->extradata)
2510 goto fail;
2511 memcpy(fst->codec->extradata, st->codec->extradata,
2512 fst->codec->extradata_size);
2516 av_close_input_stream(s);
2517 av_free(pb);
2519 c->buffer_ptr = c->buffer;
2522 return 0;
2523 fail:
2524 c->stream->feed_opened = 0;
2525 close(c->feed_fd);
2526 /* wake up any waiting connections to stop waiting for feed */
2527 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2528 if (c1->state == HTTPSTATE_WAIT_FEED &&
2529 c1->stream->feed == c->stream->feed)
2530 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2532 return -1;
2535 /********************************************************************/
2536 /* RTSP handling */
2538 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2540 const char *str;
2541 time_t ti;
2542 char *p;
2543 char buf2[32];
2545 switch(error_number) {
2546 case RTSP_STATUS_OK:
2547 str = "OK";
2548 break;
2549 case RTSP_STATUS_METHOD:
2550 str = "Method Not Allowed";
2551 break;
2552 case RTSP_STATUS_BANDWIDTH:
2553 str = "Not Enough Bandwidth";
2554 break;
2555 case RTSP_STATUS_SESSION:
2556 str = "Session Not Found";
2557 break;
2558 case RTSP_STATUS_STATE:
2559 str = "Method Not Valid in This State";
2560 break;
2561 case RTSP_STATUS_AGGREGATE:
2562 str = "Aggregate operation not allowed";
2563 break;
2564 case RTSP_STATUS_ONLY_AGGREGATE:
2565 str = "Only aggregate operation allowed";
2566 break;
2567 case RTSP_STATUS_TRANSPORT:
2568 str = "Unsupported transport";
2569 break;
2570 case RTSP_STATUS_INTERNAL:
2571 str = "Internal Server Error";
2572 break;
2573 case RTSP_STATUS_SERVICE:
2574 str = "Service Unavailable";
2575 break;
2576 case RTSP_STATUS_VERSION:
2577 str = "RTSP Version not supported";
2578 break;
2579 default:
2580 str = "Unknown Error";
2581 break;
2584 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2585 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2587 /* output GMT time */
2588 ti = time(NULL);
2589 p = ctime(&ti);
2590 strcpy(buf2, p);
2591 p = buf2 + strlen(p) - 1;
2592 if (*p == '\n')
2593 *p = '\0';
2594 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2597 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2599 rtsp_reply_header(c, error_number);
2600 url_fprintf(c->pb, "\r\n");
2603 static int rtsp_parse_request(HTTPContext *c)
2605 const char *p, *p1, *p2;
2606 char cmd[32];
2607 char url[1024];
2608 char protocol[32];
2609 char line[1024];
2610 int len;
2611 RTSPHeader header1, *header = &header1;
2613 c->buffer_ptr[0] = '\0';
2614 p = c->buffer;
2616 get_word(cmd, sizeof(cmd), &p);
2617 get_word(url, sizeof(url), &p);
2618 get_word(protocol, sizeof(protocol), &p);
2620 av_strlcpy(c->method, cmd, sizeof(c->method));
2621 av_strlcpy(c->url, url, sizeof(c->url));
2622 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2624 if (url_open_dyn_buf(&c->pb) < 0) {
2625 /* XXX: cannot do more */
2626 c->pb = NULL; /* safety */
2627 return -1;
2630 /* check version name */
2631 if (strcmp(protocol, "RTSP/1.0") != 0) {
2632 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2633 goto the_end;
2636 /* parse each header line */
2637 memset(header, 0, sizeof(RTSPHeader));
2638 /* skip to next line */
2639 while (*p != '\n' && *p != '\0')
2640 p++;
2641 if (*p == '\n')
2642 p++;
2643 while (*p != '\0') {
2644 p1 = strchr(p, '\n');
2645 if (!p1)
2646 break;
2647 p2 = p1;
2648 if (p2 > p && p2[-1] == '\r')
2649 p2--;
2650 /* skip empty line */
2651 if (p2 == p)
2652 break;
2653 len = p2 - p;
2654 if (len > sizeof(line) - 1)
2655 len = sizeof(line) - 1;
2656 memcpy(line, p, len);
2657 line[len] = '\0';
2658 rtsp_parse_line(header, line);
2659 p = p1 + 1;
2662 /* handle sequence number */
2663 c->seq = header->seq;
2665 if (!strcmp(cmd, "DESCRIBE"))
2666 rtsp_cmd_describe(c, url);
2667 else if (!strcmp(cmd, "OPTIONS"))
2668 rtsp_cmd_options(c, url);
2669 else if (!strcmp(cmd, "SETUP"))
2670 rtsp_cmd_setup(c, url, header);
2671 else if (!strcmp(cmd, "PLAY"))
2672 rtsp_cmd_play(c, url, header);
2673 else if (!strcmp(cmd, "PAUSE"))
2674 rtsp_cmd_pause(c, url, header);
2675 else if (!strcmp(cmd, "TEARDOWN"))
2676 rtsp_cmd_teardown(c, url, header);
2677 else
2678 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2680 the_end:
2681 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2682 c->pb = NULL; /* safety */
2683 if (len < 0) {
2684 /* XXX: cannot do more */
2685 return -1;
2687 c->buffer_ptr = c->pb_buffer;
2688 c->buffer_end = c->pb_buffer + len;
2689 c->state = RTSPSTATE_SEND_REPLY;
2690 return 0;
2693 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2694 struct in_addr my_ip)
2696 AVFormatContext *avc;
2697 AVStream avs[MAX_STREAMS];
2698 int i;
2700 avc = av_alloc_format_context();
2701 if (avc == NULL) {
2702 return -1;
2704 if (stream->title[0] != 0) {
2705 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2706 } else {
2707 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2709 avc->nb_streams = stream->nb_streams;
2710 if (stream->is_multicast) {
2711 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2712 inet_ntoa(stream->multicast_ip),
2713 stream->multicast_port, stream->multicast_ttl);
2716 for(i = 0; i < stream->nb_streams; i++) {
2717 avc->streams[i] = &avs[i];
2718 avc->streams[i]->codec = stream->streams[i]->codec;
2720 *pbuffer = av_mallocz(2048);
2721 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2722 av_free(avc);
2724 return strlen(*pbuffer);
2727 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2729 // rtsp_reply_header(c, RTSP_STATUS_OK);
2730 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2731 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2732 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2733 url_fprintf(c->pb, "\r\n");
2736 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2738 FFStream *stream;
2739 char path1[1024];
2740 const char *path;
2741 uint8_t *content;
2742 int content_length, len;
2743 struct sockaddr_in my_addr;
2745 /* find which url is asked */
2746 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2747 path = path1;
2748 if (*path == '/')
2749 path++;
2751 for(stream = first_stream; stream != NULL; stream = stream->next) {
2752 if (!stream->is_feed &&
2753 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2754 !strcmp(path, stream->filename)) {
2755 goto found;
2758 /* no stream found */
2759 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2760 return;
2762 found:
2763 /* prepare the media description in sdp format */
2765 /* get the host IP */
2766 len = sizeof(my_addr);
2767 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2768 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2769 if (content_length < 0) {
2770 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2771 return;
2773 rtsp_reply_header(c, RTSP_STATUS_OK);
2774 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2775 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2776 url_fprintf(c->pb, "\r\n");
2777 put_buffer(c->pb, content, content_length);
2780 static HTTPContext *find_rtp_session(const char *session_id)
2782 HTTPContext *c;
2784 if (session_id[0] == '\0')
2785 return NULL;
2787 for(c = first_http_ctx; c != NULL; c = c->next) {
2788 if (!strcmp(c->session_id, session_id))
2789 return c;
2791 return NULL;
2794 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2796 RTSPTransportField *th;
2797 int i;
2799 for(i=0;i<h->nb_transports;i++) {
2800 th = &h->transports[i];
2801 if (th->protocol == protocol)
2802 return th;
2804 return NULL;
2807 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2808 RTSPHeader *h)
2810 FFStream *stream;
2811 int stream_index, port;
2812 char buf[1024];
2813 char path1[1024];
2814 const char *path;
2815 HTTPContext *rtp_c;
2816 RTSPTransportField *th;
2817 struct sockaddr_in dest_addr;
2818 RTSPActionServerSetup setup;
2820 /* find which url is asked */
2821 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2822 path = path1;
2823 if (*path == '/')
2824 path++;
2826 /* now check each stream */
2827 for(stream = first_stream; stream != NULL; stream = stream->next) {
2828 if (!stream->is_feed &&
2829 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2830 /* accept aggregate filenames only if single stream */
2831 if (!strcmp(path, stream->filename)) {
2832 if (stream->nb_streams != 1) {
2833 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2834 return;
2836 stream_index = 0;
2837 goto found;
2840 for(stream_index = 0; stream_index < stream->nb_streams;
2841 stream_index++) {
2842 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2843 stream->filename, stream_index);
2844 if (!strcmp(path, buf))
2845 goto found;
2849 /* no stream found */
2850 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2851 return;
2852 found:
2854 /* generate session id if needed */
2855 if (h->session_id[0] == '\0')
2856 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2857 av_random(&random_state), av_random(&random_state));
2859 /* find rtp session, and create it if none found */
2860 rtp_c = find_rtp_session(h->session_id);
2861 if (!rtp_c) {
2862 /* always prefer UDP */
2863 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2864 if (!th) {
2865 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2866 if (!th) {
2867 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2868 return;
2872 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2873 th->protocol);
2874 if (!rtp_c) {
2875 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2876 return;
2879 /* open input stream */
2880 if (open_input_stream(rtp_c, "") < 0) {
2881 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2882 return;
2886 /* test if stream is OK (test needed because several SETUP needs
2887 to be done for a given file) */
2888 if (rtp_c->stream != stream) {
2889 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2890 return;
2893 /* test if stream is already set up */
2894 if (rtp_c->rtp_ctx[stream_index]) {
2895 rtsp_reply_error(c, RTSP_STATUS_STATE);
2896 return;
2899 /* check transport */
2900 th = find_transport(h, rtp_c->rtp_protocol);
2901 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2902 th->client_port_min <= 0)) {
2903 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2904 return;
2907 /* setup default options */
2908 setup.transport_option[0] = '\0';
2909 dest_addr = rtp_c->from_addr;
2910 dest_addr.sin_port = htons(th->client_port_min);
2912 /* setup stream */
2913 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2914 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2915 return;
2918 /* now everything is OK, so we can send the connection parameters */
2919 rtsp_reply_header(c, RTSP_STATUS_OK);
2920 /* session ID */
2921 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2923 switch(rtp_c->rtp_protocol) {
2924 case RTSP_PROTOCOL_RTP_UDP:
2925 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2926 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2927 "client_port=%d-%d;server_port=%d-%d",
2928 th->client_port_min, th->client_port_min + 1,
2929 port, port + 1);
2930 break;
2931 case RTSP_PROTOCOL_RTP_TCP:
2932 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2933 stream_index * 2, stream_index * 2 + 1);
2934 break;
2935 default:
2936 break;
2938 if (setup.transport_option[0] != '\0')
2939 url_fprintf(c->pb, ";%s", setup.transport_option);
2940 url_fprintf(c->pb, "\r\n");
2943 url_fprintf(c->pb, "\r\n");
2947 /* find an rtp connection by using the session ID. Check consistency
2948 with filename */
2949 static HTTPContext *find_rtp_session_with_url(const char *url,
2950 const char *session_id)
2952 HTTPContext *rtp_c;
2953 char path1[1024];
2954 const char *path;
2955 char buf[1024];
2956 int s;
2958 rtp_c = find_rtp_session(session_id);
2959 if (!rtp_c)
2960 return NULL;
2962 /* find which url is asked */
2963 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2964 path = path1;
2965 if (*path == '/')
2966 path++;
2967 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2968 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2969 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2970 rtp_c->stream->filename, s);
2971 if(!strncmp(path, buf, sizeof(buf))) {
2972 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2973 return rtp_c;
2976 return NULL;
2979 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2981 HTTPContext *rtp_c;
2983 rtp_c = find_rtp_session_with_url(url, h->session_id);
2984 if (!rtp_c) {
2985 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2986 return;
2989 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2990 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2991 rtp_c->state != HTTPSTATE_READY) {
2992 rtsp_reply_error(c, RTSP_STATUS_STATE);
2993 return;
2996 #if 0
2997 /* XXX: seek in stream */
2998 if (h->range_start != AV_NOPTS_VALUE) {
2999 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3000 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3002 #endif
3004 rtp_c->state = HTTPSTATE_SEND_DATA;
3006 /* now everything is OK, so we can send the connection parameters */
3007 rtsp_reply_header(c, RTSP_STATUS_OK);
3008 /* session ID */
3009 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3010 url_fprintf(c->pb, "\r\n");
3013 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3015 HTTPContext *rtp_c;
3017 rtp_c = find_rtp_session_with_url(url, h->session_id);
3018 if (!rtp_c) {
3019 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3020 return;
3023 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3024 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3025 rtsp_reply_error(c, RTSP_STATUS_STATE);
3026 return;
3029 rtp_c->state = HTTPSTATE_READY;
3030 rtp_c->first_pts = AV_NOPTS_VALUE;
3031 /* now everything is OK, so we can send the connection parameters */
3032 rtsp_reply_header(c, RTSP_STATUS_OK);
3033 /* session ID */
3034 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3035 url_fprintf(c->pb, "\r\n");
3038 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3040 HTTPContext *rtp_c;
3041 char session_id[32];
3043 rtp_c = find_rtp_session_with_url(url, h->session_id);
3044 if (!rtp_c) {
3045 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3046 return;
3049 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3051 /* abort the session */
3052 close_connection(rtp_c);
3054 /* now everything is OK, so we can send the connection parameters */
3055 rtsp_reply_header(c, RTSP_STATUS_OK);
3056 /* session ID */
3057 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3058 url_fprintf(c->pb, "\r\n");
3062 /********************************************************************/
3063 /* RTP handling */
3065 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3066 FFStream *stream, const char *session_id,
3067 enum RTSPProtocol rtp_protocol)
3069 HTTPContext *c = NULL;
3070 const char *proto_str;
3072 /* XXX: should output a warning page when coming
3073 close to the connection limit */
3074 if (nb_connections >= nb_max_connections)
3075 goto fail;
3077 /* add a new connection */
3078 c = av_mallocz(sizeof(HTTPContext));
3079 if (!c)
3080 goto fail;
3082 c->fd = -1;
3083 c->poll_entry = NULL;
3084 c->from_addr = *from_addr;
3085 c->buffer_size = IOBUFFER_INIT_SIZE;
3086 c->buffer = av_malloc(c->buffer_size);
3087 if (!c->buffer)
3088 goto fail;
3089 nb_connections++;
3090 c->stream = stream;
3091 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3092 c->state = HTTPSTATE_READY;
3093 c->is_packetized = 1;
3094 c->rtp_protocol = rtp_protocol;
3096 /* protocol is shown in statistics */
3097 switch(c->rtp_protocol) {
3098 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3099 proto_str = "MCAST";
3100 break;
3101 case RTSP_PROTOCOL_RTP_UDP:
3102 proto_str = "UDP";
3103 break;
3104 case RTSP_PROTOCOL_RTP_TCP:
3105 proto_str = "TCP";
3106 break;
3107 default:
3108 proto_str = "???";
3109 break;
3111 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3112 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3114 current_bandwidth += stream->bandwidth;
3116 c->next = first_http_ctx;
3117 first_http_ctx = c;
3118 return c;
3120 fail:
3121 if (c) {
3122 av_free(c->buffer);
3123 av_free(c);
3125 return NULL;
3128 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3129 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3130 used. */
3131 static int rtp_new_av_stream(HTTPContext *c,
3132 int stream_index, struct sockaddr_in *dest_addr,
3133 HTTPContext *rtsp_c)
3135 AVFormatContext *ctx;
3136 AVStream *st;
3137 char *ipaddr;
3138 URLContext *h = NULL;
3139 uint8_t *dummy_buf;
3140 int max_packet_size;
3142 /* now we can open the relevant output stream */
3143 ctx = av_alloc_format_context();
3144 if (!ctx)
3145 return -1;
3146 ctx->oformat = guess_format("rtp", NULL, NULL);
3148 st = av_mallocz(sizeof(AVStream));
3149 if (!st)
3150 goto fail;
3151 st->codec= avcodec_alloc_context();
3152 ctx->nb_streams = 1;
3153 ctx->streams[0] = st;
3155 if (!c->stream->feed ||
3156 c->stream->feed == c->stream)
3157 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3158 else
3159 memcpy(st,
3160 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3161 sizeof(AVStream));
3162 st->priv_data = NULL;
3164 /* build destination RTP address */
3165 ipaddr = inet_ntoa(dest_addr->sin_addr);
3167 switch(c->rtp_protocol) {
3168 case RTSP_PROTOCOL_RTP_UDP:
3169 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3170 /* RTP/UDP case */
3172 /* XXX: also pass as parameter to function ? */
3173 if (c->stream->is_multicast) {
3174 int ttl;
3175 ttl = c->stream->multicast_ttl;
3176 if (!ttl)
3177 ttl = 16;
3178 snprintf(ctx->filename, sizeof(ctx->filename),
3179 "rtp://%s:%d?multicast=1&ttl=%d",
3180 ipaddr, ntohs(dest_addr->sin_port), ttl);
3181 } else {
3182 snprintf(ctx->filename, sizeof(ctx->filename),
3183 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3186 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3187 goto fail;
3188 c->rtp_handles[stream_index] = h;
3189 max_packet_size = url_get_max_packet_size(h);
3190 break;
3191 case RTSP_PROTOCOL_RTP_TCP:
3192 /* RTP/TCP case */
3193 c->rtsp_c = rtsp_c;
3194 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3195 break;
3196 default:
3197 goto fail;
3200 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3201 ipaddr, ntohs(dest_addr->sin_port),
3202 c->stream->filename, stream_index, c->protocol);
3204 /* normally, no packets should be output here, but the packet size may be checked */
3205 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3206 /* XXX: close stream */
3207 goto fail;
3209 av_set_parameters(ctx, NULL);
3210 if (av_write_header(ctx) < 0) {
3211 fail:
3212 if (h)
3213 url_close(h);
3214 av_free(ctx);
3215 return -1;
3217 url_close_dyn_buf(ctx->pb, &dummy_buf);
3218 av_free(dummy_buf);
3220 c->rtp_ctx[stream_index] = ctx;
3221 return 0;
3224 /********************************************************************/
3225 /* ffserver initialization */
3227 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3229 AVStream *fst;
3231 fst = av_mallocz(sizeof(AVStream));
3232 if (!fst)
3233 return NULL;
3234 fst->codec= avcodec_alloc_context();
3235 fst->priv_data = av_mallocz(sizeof(FeedData));
3236 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3237 fst->index = stream->nb_streams;
3238 av_set_pts_info(fst, 33, 1, 90000);
3239 stream->streams[stream->nb_streams++] = fst;
3240 return fst;
3243 /* return the stream number in the feed */
3244 static int add_av_stream(FFStream *feed, AVStream *st)
3246 AVStream *fst;
3247 AVCodecContext *av, *av1;
3248 int i;
3250 av = st->codec;
3251 for(i=0;i<feed->nb_streams;i++) {
3252 st = feed->streams[i];
3253 av1 = st->codec;
3254 if (av1->codec_id == av->codec_id &&
3255 av1->codec_type == av->codec_type &&
3256 av1->bit_rate == av->bit_rate) {
3258 switch(av->codec_type) {
3259 case CODEC_TYPE_AUDIO:
3260 if (av1->channels == av->channels &&
3261 av1->sample_rate == av->sample_rate)
3262 goto found;
3263 break;
3264 case CODEC_TYPE_VIDEO:
3265 if (av1->width == av->width &&
3266 av1->height == av->height &&
3267 av1->time_base.den == av->time_base.den &&
3268 av1->time_base.num == av->time_base.num &&
3269 av1->gop_size == av->gop_size)
3270 goto found;
3271 break;
3272 default:
3273 abort();
3278 fst = add_av_stream1(feed, av);
3279 if (!fst)
3280 return -1;
3281 return feed->nb_streams - 1;
3282 found:
3283 return i;
3286 static void remove_stream(FFStream *stream)
3288 FFStream **ps;
3289 ps = &first_stream;
3290 while (*ps != NULL) {
3291 if (*ps == stream)
3292 *ps = (*ps)->next;
3293 else
3294 ps = &(*ps)->next;
3298 /* specific mpeg4 handling : we extract the raw parameters */
3299 static void extract_mpeg4_header(AVFormatContext *infile)
3301 int mpeg4_count, i, size;
3302 AVPacket pkt;
3303 AVStream *st;
3304 const uint8_t *p;
3306 mpeg4_count = 0;
3307 for(i=0;i<infile->nb_streams;i++) {
3308 st = infile->streams[i];
3309 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3310 st->codec->extradata_size == 0) {
3311 mpeg4_count++;
3314 if (!mpeg4_count)
3315 return;
3317 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3318 while (mpeg4_count > 0) {
3319 if (av_read_packet(infile, &pkt) < 0)
3320 break;
3321 st = infile->streams[pkt.stream_index];
3322 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3323 st->codec->extradata_size == 0) {
3324 av_freep(&st->codec->extradata);
3325 /* fill extradata with the header */
3326 /* XXX: we make hard suppositions here ! */
3327 p = pkt.data;
3328 while (p < pkt.data + pkt.size - 4) {
3329 /* stop when vop header is found */
3330 if (p[0] == 0x00 && p[1] == 0x00 &&
3331 p[2] == 0x01 && p[3] == 0xb6) {
3332 size = p - pkt.data;
3333 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3334 st->codec->extradata = av_malloc(size);
3335 st->codec->extradata_size = size;
3336 memcpy(st->codec->extradata, pkt.data, size);
3337 break;
3339 p++;
3341 mpeg4_count--;
3343 av_free_packet(&pkt);
3347 /* compute the needed AVStream for each file */
3348 static void build_file_streams(void)
3350 FFStream *stream, *stream_next;
3351 AVFormatContext *infile;
3352 int i, ret;
3354 /* gather all streams */
3355 for(stream = first_stream; stream != NULL; stream = stream_next) {
3356 stream_next = stream->next;
3357 if (stream->stream_type == STREAM_TYPE_LIVE &&
3358 !stream->feed) {
3359 /* the stream comes from a file */
3360 /* try to open the file */
3361 /* open stream */
3362 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3363 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3364 /* specific case : if transport stream output to RTP,
3365 we use a raw transport stream reader */
3366 stream->ap_in->mpeg2ts_raw = 1;
3367 stream->ap_in->mpeg2ts_compute_pcr = 1;
3370 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3371 stream->ifmt, 0, stream->ap_in)) < 0) {
3372 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3373 /* remove stream (no need to spend more time on it) */
3374 fail:
3375 remove_stream(stream);
3376 } else {
3377 /* find all the AVStreams inside and reference them in
3378 'stream' */
3379 if (av_find_stream_info(infile) < 0) {
3380 http_log("Could not find codec parameters from '%s'\n",
3381 stream->feed_filename);
3382 av_close_input_file(infile);
3383 goto fail;
3385 extract_mpeg4_header(infile);
3387 for(i=0;i<infile->nb_streams;i++)
3388 add_av_stream1(stream, infile->streams[i]->codec);
3390 av_close_input_file(infile);
3396 /* compute the needed AVStream for each feed */
3397 static void build_feed_streams(void)
3399 FFStream *stream, *feed;
3400 int i;
3402 /* gather all streams */
3403 for(stream = first_stream; stream != NULL; stream = stream->next) {
3404 feed = stream->feed;
3405 if (feed) {
3406 if (!stream->is_feed) {
3407 /* we handle a stream coming from a feed */
3408 for(i=0;i<stream->nb_streams;i++)
3409 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3414 /* gather all streams */
3415 for(stream = first_stream; stream != NULL; stream = stream->next) {
3416 feed = stream->feed;
3417 if (feed) {
3418 if (stream->is_feed) {
3419 for(i=0;i<stream->nb_streams;i++)
3420 stream->feed_streams[i] = i;
3425 /* create feed files if needed */
3426 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3427 int fd;
3429 if (url_exist(feed->feed_filename)) {
3430 /* See if it matches */
3431 AVFormatContext *s;
3432 int matches = 0;
3434 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3435 /* Now see if it matches */
3436 if (s->nb_streams == feed->nb_streams) {
3437 matches = 1;
3438 for(i=0;i<s->nb_streams;i++) {
3439 AVStream *sf, *ss;
3440 sf = feed->streams[i];
3441 ss = s->streams[i];
3443 if (sf->index != ss->index ||
3444 sf->id != ss->id) {
3445 http_log("Index & Id do not match for stream %d (%s)\n",
3446 i, feed->feed_filename);
3447 matches = 0;
3448 } else {
3449 AVCodecContext *ccf, *ccs;
3451 ccf = sf->codec;
3452 ccs = ss->codec;
3453 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3455 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3456 http_log("Codecs do not match for stream %d\n", i);
3457 matches = 0;
3458 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3459 http_log("Codec bitrates do not match for stream %d\n", i);
3460 matches = 0;
3461 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3462 if (CHECK_CODEC(time_base.den) ||
3463 CHECK_CODEC(time_base.num) ||
3464 CHECK_CODEC(width) ||
3465 CHECK_CODEC(height)) {
3466 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3467 matches = 0;
3469 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3470 if (CHECK_CODEC(sample_rate) ||
3471 CHECK_CODEC(channels) ||
3472 CHECK_CODEC(frame_size)) {
3473 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3474 matches = 0;
3476 } else {
3477 http_log("Unknown codec type\n");
3478 matches = 0;
3481 if (!matches)
3482 break;
3484 } else
3485 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3486 feed->feed_filename, s->nb_streams, feed->nb_streams);
3488 av_close_input_file(s);
3489 } else
3490 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3491 feed->feed_filename);
3493 if (!matches) {
3494 if (feed->readonly) {
3495 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3496 feed->feed_filename);
3497 exit(1);
3499 unlink(feed->feed_filename);
3502 if (!url_exist(feed->feed_filename)) {
3503 AVFormatContext s1, *s = &s1;
3505 if (feed->readonly) {
3506 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3507 feed->feed_filename);
3508 exit(1);
3511 /* only write the header of the ffm file */
3512 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3513 http_log("Could not open output feed file '%s'\n",
3514 feed->feed_filename);
3515 exit(1);
3517 s->oformat = feed->fmt;
3518 s->nb_streams = feed->nb_streams;
3519 for(i=0;i<s->nb_streams;i++) {
3520 AVStream *st;
3521 st = feed->streams[i];
3522 s->streams[i] = st;
3524 av_set_parameters(s, NULL);
3525 if (av_write_header(s) < 0) {
3526 http_log("Container doesn't supports the required parameters\n");
3527 exit(1);
3529 /* XXX: need better api */
3530 av_freep(&s->priv_data);
3531 url_fclose(s->pb);
3533 /* get feed size and write index */
3534 fd = open(feed->feed_filename, O_RDONLY);
3535 if (fd < 0) {
3536 http_log("Could not open output feed file '%s'\n",
3537 feed->feed_filename);
3538 exit(1);
3541 feed->feed_write_index = ffm_read_write_index(fd);
3542 feed->feed_size = lseek(fd, 0, SEEK_END);
3543 /* ensure that we do not wrap before the end of file */
3544 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3545 feed->feed_max_size = feed->feed_size;
3547 close(fd);
3551 /* compute the bandwidth used by each stream */
3552 static void compute_bandwidth(void)
3554 unsigned bandwidth;
3555 int i;
3556 FFStream *stream;
3558 for(stream = first_stream; stream != NULL; stream = stream->next) {
3559 bandwidth = 0;
3560 for(i=0;i<stream->nb_streams;i++) {
3561 AVStream *st = stream->streams[i];
3562 switch(st->codec->codec_type) {
3563 case CODEC_TYPE_AUDIO:
3564 case CODEC_TYPE_VIDEO:
3565 bandwidth += st->codec->bit_rate;
3566 break;
3567 default:
3568 break;
3571 stream->bandwidth = (bandwidth + 999) / 1000;
3575 static void get_arg(char *buf, int buf_size, const char **pp)
3577 const char *p;
3578 char *q;
3579 int quote;
3581 p = *pp;
3582 while (isspace(*p)) p++;
3583 q = buf;
3584 quote = 0;
3585 if (*p == '\"' || *p == '\'')
3586 quote = *p++;
3587 for(;;) {
3588 if (quote) {
3589 if (*p == quote)
3590 break;
3591 } else {
3592 if (isspace(*p))
3593 break;
3595 if (*p == '\0')
3596 break;
3597 if ((q - buf) < buf_size - 1)
3598 *q++ = *p;
3599 p++;
3601 *q = '\0';
3602 if (quote && *p == quote)
3603 p++;
3604 *pp = p;
3607 /* add a codec and set the default parameters */
3608 static void add_codec(FFStream *stream, AVCodecContext *av)
3610 AVStream *st;
3612 /* compute default parameters */
3613 switch(av->codec_type) {
3614 case CODEC_TYPE_AUDIO:
3615 if (av->bit_rate == 0)
3616 av->bit_rate = 64000;
3617 if (av->sample_rate == 0)
3618 av->sample_rate = 22050;
3619 if (av->channels == 0)
3620 av->channels = 1;
3621 break;
3622 case CODEC_TYPE_VIDEO:
3623 if (av->bit_rate == 0)
3624 av->bit_rate = 64000;
3625 if (av->time_base.num == 0){
3626 av->time_base.den = 5;
3627 av->time_base.num = 1;
3629 if (av->width == 0 || av->height == 0) {
3630 av->width = 160;
3631 av->height = 128;
3633 /* Bitrate tolerance is less for streaming */
3634 if (av->bit_rate_tolerance == 0)
3635 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3636 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3637 if (av->qmin == 0)
3638 av->qmin = 3;
3639 if (av->qmax == 0)
3640 av->qmax = 31;
3641 if (av->max_qdiff == 0)
3642 av->max_qdiff = 3;
3643 av->qcompress = 0.5;
3644 av->qblur = 0.5;
3646 if (!av->nsse_weight)
3647 av->nsse_weight = 8;
3649 av->frame_skip_cmp = FF_CMP_DCTMAX;
3650 av->me_method = ME_EPZS;
3651 av->rc_buffer_aggressivity = 1.0;
3653 if (!av->rc_eq)
3654 av->rc_eq = "tex^qComp";
3655 if (!av->i_quant_factor)
3656 av->i_quant_factor = -0.8;
3657 if (!av->b_quant_factor)
3658 av->b_quant_factor = 1.25;
3659 if (!av->b_quant_offset)
3660 av->b_quant_offset = 1.25;
3661 if (!av->rc_max_rate)
3662 av->rc_max_rate = av->bit_rate * 2;
3664 if (av->rc_max_rate && !av->rc_buffer_size) {
3665 av->rc_buffer_size = av->rc_max_rate;
3669 break;
3670 default:
3671 abort();
3674 st = av_mallocz(sizeof(AVStream));
3675 if (!st)
3676 return;
3677 st->codec = avcodec_alloc_context();
3678 stream->streams[stream->nb_streams++] = st;
3679 memcpy(st->codec, av, sizeof(AVCodecContext));
3682 static int opt_audio_codec(const char *arg)
3684 AVCodec *p= avcodec_find_encoder_by_name(arg);
3686 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3687 return CODEC_ID_NONE;
3689 return p->id;
3692 static int opt_video_codec(const char *arg)
3694 AVCodec *p= avcodec_find_encoder_by_name(arg);
3696 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3697 return CODEC_ID_NONE;
3699 return p->id;
3702 /* simplistic plugin support */
3704 #ifdef HAVE_DLOPEN
3705 static void load_module(const char *filename)
3707 void *dll;
3708 void (*init_func)(void);
3709 dll = dlopen(filename, RTLD_NOW);
3710 if (!dll) {
3711 fprintf(stderr, "Could not load module '%s' - %s\n",
3712 filename, dlerror());
3713 return;
3716 init_func = dlsym(dll, "ffserver_module_init");
3717 if (!init_func) {
3718 fprintf(stderr,
3719 "%s: init function 'ffserver_module_init()' not found\n",
3720 filename);
3721 dlclose(dll);
3724 init_func();
3726 #endif
3728 static int opt_default(const char *opt, const char *arg,
3729 AVCodecContext *avctx, int type)
3731 const AVOption *o = NULL;
3732 const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
3733 if(o2)
3734 o = av_set_string2(avctx, opt, arg, 1);
3735 if(!o)
3736 return -1;
3737 return 0;
3740 static int parse_ffconfig(const char *filename)
3742 FILE *f;
3743 char line[1024];
3744 char cmd[64];
3745 char arg[1024];
3746 const char *p;
3747 int val, errors, line_num;
3748 FFStream **last_stream, *stream, *redirect;
3749 FFStream **last_feed, *feed;
3750 AVCodecContext audio_enc, video_enc;
3751 int audio_id, video_id;
3753 f = fopen(filename, "r");
3754 if (!f) {
3755 perror(filename);
3756 return -1;
3759 errors = 0;
3760 line_num = 0;
3761 first_stream = NULL;
3762 last_stream = &first_stream;
3763 first_feed = NULL;
3764 last_feed = &first_feed;
3765 stream = NULL;
3766 feed = NULL;
3767 redirect = NULL;
3768 audio_id = CODEC_ID_NONE;
3769 video_id = CODEC_ID_NONE;
3770 for(;;) {
3771 if (fgets(line, sizeof(line), f) == NULL)
3772 break;
3773 line_num++;
3774 p = line;
3775 while (isspace(*p))
3776 p++;
3777 if (*p == '\0' || *p == '#')
3778 continue;
3780 get_arg(cmd, sizeof(cmd), &p);
3782 if (!strcasecmp(cmd, "Port")) {
3783 get_arg(arg, sizeof(arg), &p);
3784 val = atoi(arg);
3785 if (val < 1 || val > 65536) {
3786 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3787 filename, line_num, arg);
3788 errors++;
3790 my_http_addr.sin_port = htons(val);
3791 } else if (!strcasecmp(cmd, "BindAddress")) {
3792 get_arg(arg, sizeof(arg), &p);
3793 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3794 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3795 filename, line_num, arg);
3796 errors++;
3798 } else if (!strcasecmp(cmd, "NoDaemon")) {
3799 ffserver_daemon = 0;
3800 } else if (!strcasecmp(cmd, "RTSPPort")) {
3801 get_arg(arg, sizeof(arg), &p);
3802 val = atoi(arg);
3803 if (val < 1 || val > 65536) {
3804 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3805 filename, line_num, arg);
3806 errors++;
3808 my_rtsp_addr.sin_port = htons(atoi(arg));
3809 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3810 get_arg(arg, sizeof(arg), &p);
3811 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3812 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3813 filename, line_num, arg);
3814 errors++;
3816 } else if (!strcasecmp(cmd, "MaxClients")) {
3817 get_arg(arg, sizeof(arg), &p);
3818 val = atoi(arg);
3819 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3820 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3821 filename, line_num, arg);
3822 errors++;
3823 } else {
3824 nb_max_connections = val;
3826 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3827 int64_t llval;
3828 get_arg(arg, sizeof(arg), &p);
3829 llval = atoll(arg);
3830 if (llval < 10 || llval > 10000000) {
3831 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3832 filename, line_num, arg);
3833 errors++;
3834 } else
3835 max_bandwidth = llval;
3836 } else if (!strcasecmp(cmd, "CustomLog")) {
3837 if (!ffserver_debug)
3838 get_arg(logfilename, sizeof(logfilename), &p);
3839 } else if (!strcasecmp(cmd, "<Feed")) {
3840 /*********************************************/
3841 /* Feed related options */
3842 char *q;
3843 if (stream || feed) {
3844 fprintf(stderr, "%s:%d: Already in a tag\n",
3845 filename, line_num);
3846 } else {
3847 feed = av_mallocz(sizeof(FFStream));
3848 /* add in stream list */
3849 *last_stream = feed;
3850 last_stream = &feed->next;
3851 /* add in feed list */
3852 *last_feed = feed;
3853 last_feed = &feed->next_feed;
3855 get_arg(feed->filename, sizeof(feed->filename), &p);
3856 q = strrchr(feed->filename, '>');
3857 if (*q)
3858 *q = '\0';
3859 feed->fmt = guess_format("ffm", NULL, NULL);
3860 /* defaut feed file */
3861 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3862 "/tmp/%s.ffm", feed->filename);
3863 feed->feed_max_size = 5 * 1024 * 1024;
3864 feed->is_feed = 1;
3865 feed->feed = feed; /* self feeding :-) */
3867 } else if (!strcasecmp(cmd, "Launch")) {
3868 if (feed) {
3869 int i;
3871 feed->child_argv = av_mallocz(64 * sizeof(char *));
3873 for (i = 0; i < 62; i++) {
3874 get_arg(arg, sizeof(arg), &p);
3875 if (!arg[0])
3876 break;
3878 feed->child_argv[i] = av_strdup(arg);
3881 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3883 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3884 "http://%s:%d/%s",
3885 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3886 inet_ntoa(my_http_addr.sin_addr),
3887 ntohs(my_http_addr.sin_port), feed->filename);
3889 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3890 if (feed) {
3891 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3892 feed->readonly = 1;
3893 } else if (stream) {
3894 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3896 } else if (!strcasecmp(cmd, "File")) {
3897 if (feed) {
3898 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3899 } else if (stream)
3900 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3901 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3902 if (feed) {
3903 char *p1;
3904 double fsize;
3906 get_arg(arg, sizeof(arg), &p);
3907 p1 = arg;
3908 fsize = strtod(p1, &p1);
3909 switch(toupper(*p1)) {
3910 case 'K':
3911 fsize *= 1024;
3912 break;
3913 case 'M':
3914 fsize *= 1024 * 1024;
3915 break;
3916 case 'G':
3917 fsize *= 1024 * 1024 * 1024;
3918 break;
3920 feed->feed_max_size = (int64_t)fsize;
3922 } else if (!strcasecmp(cmd, "</Feed>")) {
3923 if (!feed) {
3924 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3925 filename, line_num);
3926 errors++;
3928 feed = NULL;
3929 } else if (!strcasecmp(cmd, "<Stream")) {
3930 /*********************************************/
3931 /* Stream related options */
3932 char *q;
3933 if (stream || feed) {
3934 fprintf(stderr, "%s:%d: Already in a tag\n",
3935 filename, line_num);
3936 } else {
3937 const AVClass *class;
3938 stream = av_mallocz(sizeof(FFStream));
3939 *last_stream = stream;
3940 last_stream = &stream->next;
3942 get_arg(stream->filename, sizeof(stream->filename), &p);
3943 q = strrchr(stream->filename, '>');
3944 if (*q)
3945 *q = '\0';
3946 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3947 /* fetch avclass so AVOption works
3948 * FIXME try to use avcodec_get_context_defaults2
3949 * without changing defaults too much */
3950 avcodec_get_context_defaults(&video_enc);
3951 class = video_enc.av_class;
3952 memset(&audio_enc, 0, sizeof(AVCodecContext));
3953 memset(&video_enc, 0, sizeof(AVCodecContext));
3954 audio_enc.av_class = class;
3955 video_enc.av_class = class;
3956 audio_id = CODEC_ID_NONE;
3957 video_id = CODEC_ID_NONE;
3958 if (stream->fmt) {
3959 audio_id = stream->fmt->audio_codec;
3960 video_id = stream->fmt->video_codec;
3963 } else if (!strcasecmp(cmd, "Feed")) {
3964 get_arg(arg, sizeof(arg), &p);
3965 if (stream) {
3966 FFStream *sfeed;
3968 sfeed = first_feed;
3969 while (sfeed != NULL) {
3970 if (!strcmp(sfeed->filename, arg))
3971 break;
3972 sfeed = sfeed->next_feed;
3974 if (!sfeed)
3975 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3976 filename, line_num, arg);
3977 else
3978 stream->feed = sfeed;
3980 } else if (!strcasecmp(cmd, "Format")) {
3981 get_arg(arg, sizeof(arg), &p);
3982 if (stream) {
3983 if (!strcmp(arg, "status")) {
3984 stream->stream_type = STREAM_TYPE_STATUS;
3985 stream->fmt = NULL;
3986 } else {
3987 stream->stream_type = STREAM_TYPE_LIVE;
3988 /* jpeg cannot be used here, so use single frame jpeg */
3989 if (!strcmp(arg, "jpeg"))
3990 strcpy(arg, "mjpeg");
3991 stream->fmt = guess_stream_format(arg, NULL, NULL);
3992 if (!stream->fmt) {
3993 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3994 filename, line_num, arg);
3995 errors++;
3998 if (stream->fmt) {
3999 audio_id = stream->fmt->audio_codec;
4000 video_id = stream->fmt->video_codec;
4003 } else if (!strcasecmp(cmd, "InputFormat")) {
4004 get_arg(arg, sizeof(arg), &p);
4005 stream->ifmt = av_find_input_format(arg);
4006 if (!stream->ifmt) {
4007 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4008 filename, line_num, arg);
4010 } else if (!strcasecmp(cmd, "FaviconURL")) {
4011 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4012 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4013 } else {
4014 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4015 filename, line_num);
4016 errors++;
4018 } else if (!strcasecmp(cmd, "Author")) {
4019 if (stream)
4020 get_arg(stream->author, sizeof(stream->author), &p);
4021 } else if (!strcasecmp(cmd, "Comment")) {
4022 if (stream)
4023 get_arg(stream->comment, sizeof(stream->comment), &p);
4024 } else if (!strcasecmp(cmd, "Copyright")) {
4025 if (stream)
4026 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4027 } else if (!strcasecmp(cmd, "Title")) {
4028 if (stream)
4029 get_arg(stream->title, sizeof(stream->title), &p);
4030 } else if (!strcasecmp(cmd, "Preroll")) {
4031 get_arg(arg, sizeof(arg), &p);
4032 if (stream)
4033 stream->prebuffer = atof(arg) * 1000;
4034 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4035 if (stream)
4036 stream->send_on_key = 1;
4037 } else if (!strcasecmp(cmd, "AudioCodec")) {
4038 get_arg(arg, sizeof(arg), &p);
4039 audio_id = opt_audio_codec(arg);
4040 if (audio_id == CODEC_ID_NONE) {
4041 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4042 filename, line_num, arg);
4043 errors++;
4045 } else if (!strcasecmp(cmd, "VideoCodec")) {
4046 get_arg(arg, sizeof(arg), &p);
4047 video_id = opt_video_codec(arg);
4048 if (video_id == CODEC_ID_NONE) {
4049 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4050 filename, line_num, arg);
4051 errors++;
4053 } else if (!strcasecmp(cmd, "MaxTime")) {
4054 get_arg(arg, sizeof(arg), &p);
4055 if (stream)
4056 stream->max_time = atof(arg) * 1000;
4057 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4058 get_arg(arg, sizeof(arg), &p);
4059 if (stream)
4060 audio_enc.bit_rate = atoi(arg) * 1000;
4061 } else if (!strcasecmp(cmd, "AudioChannels")) {
4062 get_arg(arg, sizeof(arg), &p);
4063 if (stream)
4064 audio_enc.channels = atoi(arg);
4065 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4066 get_arg(arg, sizeof(arg), &p);
4067 if (stream)
4068 audio_enc.sample_rate = atoi(arg);
4069 } else if (!strcasecmp(cmd, "AudioQuality")) {
4070 get_arg(arg, sizeof(arg), &p);
4071 if (stream) {
4072 // audio_enc.quality = atof(arg) * 1000;
4074 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4075 if (stream) {
4076 int minrate, maxrate;
4078 get_arg(arg, sizeof(arg), &p);
4080 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4081 video_enc.rc_min_rate = minrate * 1000;
4082 video_enc.rc_max_rate = maxrate * 1000;
4083 } else {
4084 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4085 filename, line_num, arg);
4086 errors++;
4089 } else if (!strcasecmp(cmd, "Debug")) {
4090 if (stream) {
4091 get_arg(arg, sizeof(arg), &p);
4092 video_enc.debug = strtol(arg,0,0);
4094 } else if (!strcasecmp(cmd, "Strict")) {
4095 if (stream) {
4096 get_arg(arg, sizeof(arg), &p);
4097 video_enc.strict_std_compliance = atoi(arg);
4099 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4100 if (stream) {
4101 get_arg(arg, sizeof(arg), &p);
4102 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4104 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4105 if (stream) {
4106 get_arg(arg, sizeof(arg), &p);
4107 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4109 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4110 get_arg(arg, sizeof(arg), &p);
4111 if (stream) {
4112 video_enc.bit_rate = atoi(arg) * 1000;
4114 } else if (!strcasecmp(cmd, "VideoSize")) {
4115 get_arg(arg, sizeof(arg), &p);
4116 if (stream) {
4117 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4118 if ((video_enc.width % 16) != 0 ||
4119 (video_enc.height % 16) != 0) {
4120 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4121 filename, line_num);
4122 errors++;
4125 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4126 get_arg(arg, sizeof(arg), &p);
4127 if (stream) {
4128 AVRational frame_rate;
4129 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4130 fprintf(stderr, "Incorrect frame rate\n");
4131 errors++;
4132 } else {
4133 video_enc.time_base.num = frame_rate.den;
4134 video_enc.time_base.den = frame_rate.num;
4137 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4138 get_arg(arg, sizeof(arg), &p);
4139 if (stream)
4140 video_enc.gop_size = atoi(arg);
4141 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4142 if (stream)
4143 video_enc.gop_size = 1;
4144 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4145 if (stream)
4146 video_enc.mb_decision = FF_MB_DECISION_BITS;
4147 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4148 if (stream) {
4149 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4150 video_enc.flags |= CODEC_FLAG_4MV;
4152 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4153 !strcasecmp(cmd, "AVOptionAudio")) {
4154 char arg2[1024];
4155 AVCodecContext *avctx;
4156 int type;
4157 get_arg(arg, sizeof(arg), &p);
4158 get_arg(arg2, sizeof(arg2), &p);
4159 if (!strcasecmp(cmd, "AVOptionVideo")) {
4160 avctx = &video_enc;
4161 type = AV_OPT_FLAG_VIDEO_PARAM;
4162 } else {
4163 avctx = &audio_enc;
4164 type = AV_OPT_FLAG_AUDIO_PARAM;
4166 if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4167 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4168 errors++;
4170 } else if (!strcasecmp(cmd, "VideoTag")) {
4171 get_arg(arg, sizeof(arg), &p);
4172 if ((strlen(arg) == 4) && stream)
4173 video_enc.codec_tag = ff_get_fourcc(arg);
4174 } else if (!strcasecmp(cmd, "BitExact")) {
4175 if (stream)
4176 video_enc.flags |= CODEC_FLAG_BITEXACT;
4177 } else if (!strcasecmp(cmd, "DctFastint")) {
4178 if (stream)
4179 video_enc.dct_algo = FF_DCT_FASTINT;
4180 } else if (!strcasecmp(cmd, "IdctSimple")) {
4181 if (stream)
4182 video_enc.idct_algo = FF_IDCT_SIMPLE;
4183 } else if (!strcasecmp(cmd, "Qscale")) {
4184 get_arg(arg, sizeof(arg), &p);
4185 if (stream) {
4186 video_enc.flags |= CODEC_FLAG_QSCALE;
4187 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4189 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4190 get_arg(arg, sizeof(arg), &p);
4191 if (stream) {
4192 video_enc.max_qdiff = atoi(arg);
4193 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4194 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4195 filename, line_num);
4196 errors++;
4199 } else if (!strcasecmp(cmd, "VideoQMax")) {
4200 get_arg(arg, sizeof(arg), &p);
4201 if (stream) {
4202 video_enc.qmax = atoi(arg);
4203 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4204 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4205 filename, line_num);
4206 errors++;
4209 } else if (!strcasecmp(cmd, "VideoQMin")) {
4210 get_arg(arg, sizeof(arg), &p);
4211 if (stream) {
4212 video_enc.qmin = atoi(arg);
4213 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4214 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4215 filename, line_num);
4216 errors++;
4219 } else if (!strcasecmp(cmd, "LumaElim")) {
4220 get_arg(arg, sizeof(arg), &p);
4221 if (stream)
4222 video_enc.luma_elim_threshold = atoi(arg);
4223 } else if (!strcasecmp(cmd, "ChromaElim")) {
4224 get_arg(arg, sizeof(arg), &p);
4225 if (stream)
4226 video_enc.chroma_elim_threshold = atoi(arg);
4227 } else if (!strcasecmp(cmd, "LumiMask")) {
4228 get_arg(arg, sizeof(arg), &p);
4229 if (stream)
4230 video_enc.lumi_masking = atof(arg);
4231 } else if (!strcasecmp(cmd, "DarkMask")) {
4232 get_arg(arg, sizeof(arg), &p);
4233 if (stream)
4234 video_enc.dark_masking = atof(arg);
4235 } else if (!strcasecmp(cmd, "NoVideo")) {
4236 video_id = CODEC_ID_NONE;
4237 } else if (!strcasecmp(cmd, "NoAudio")) {
4238 audio_id = CODEC_ID_NONE;
4239 } else if (!strcasecmp(cmd, "ACL")) {
4240 IPAddressACL acl;
4242 get_arg(arg, sizeof(arg), &p);
4243 if (strcasecmp(arg, "allow") == 0)
4244 acl.action = IP_ALLOW;
4245 else if (strcasecmp(arg, "deny") == 0)
4246 acl.action = IP_DENY;
4247 else {
4248 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4249 filename, line_num, arg);
4250 errors++;
4253 get_arg(arg, sizeof(arg), &p);
4255 if (resolve_host(&acl.first, arg) != 0) {
4256 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4257 filename, line_num, arg);
4258 errors++;
4259 } else
4260 acl.last = acl.first;
4262 get_arg(arg, sizeof(arg), &p);
4264 if (arg[0]) {
4265 if (resolve_host(&acl.last, arg) != 0) {
4266 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4267 filename, line_num, arg);
4268 errors++;
4272 if (!errors) {
4273 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4274 IPAddressACL **naclp = 0;
4276 acl.next = 0;
4277 *nacl = acl;
4279 if (stream)
4280 naclp = &stream->acl;
4281 else if (feed)
4282 naclp = &feed->acl;
4283 else {
4284 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4285 filename, line_num);
4286 errors++;
4289 if (naclp) {
4290 while (*naclp)
4291 naclp = &(*naclp)->next;
4293 *naclp = nacl;
4296 } else if (!strcasecmp(cmd, "RTSPOption")) {
4297 get_arg(arg, sizeof(arg), &p);
4298 if (stream) {
4299 av_freep(&stream->rtsp_option);
4300 stream->rtsp_option = av_strdup(arg);
4302 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4303 get_arg(arg, sizeof(arg), &p);
4304 if (stream) {
4305 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4306 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4307 filename, line_num, arg);
4308 errors++;
4310 stream->is_multicast = 1;
4311 stream->loop = 1; /* default is looping */
4313 } else if (!strcasecmp(cmd, "MulticastPort")) {
4314 get_arg(arg, sizeof(arg), &p);
4315 if (stream)
4316 stream->multicast_port = atoi(arg);
4317 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4318 get_arg(arg, sizeof(arg), &p);
4319 if (stream)
4320 stream->multicast_ttl = atoi(arg);
4321 } else if (!strcasecmp(cmd, "NoLoop")) {
4322 if (stream)
4323 stream->loop = 0;
4324 } else if (!strcasecmp(cmd, "</Stream>")) {
4325 if (!stream) {
4326 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4327 filename, line_num);
4328 errors++;
4329 } else {
4330 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4331 if (audio_id != CODEC_ID_NONE) {
4332 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4333 audio_enc.codec_id = audio_id;
4334 add_codec(stream, &audio_enc);
4336 if (video_id != CODEC_ID_NONE) {
4337 video_enc.codec_type = CODEC_TYPE_VIDEO;
4338 video_enc.codec_id = video_id;
4339 add_codec(stream, &video_enc);
4342 stream = NULL;
4344 } else if (!strcasecmp(cmd, "<Redirect")) {
4345 /*********************************************/
4346 char *q;
4347 if (stream || feed || redirect) {
4348 fprintf(stderr, "%s:%d: Already in a tag\n",
4349 filename, line_num);
4350 errors++;
4351 } else {
4352 redirect = av_mallocz(sizeof(FFStream));
4353 *last_stream = redirect;
4354 last_stream = &redirect->next;
4356 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4357 q = strrchr(redirect->filename, '>');
4358 if (*q)
4359 *q = '\0';
4360 redirect->stream_type = STREAM_TYPE_REDIRECT;
4362 } else if (!strcasecmp(cmd, "URL")) {
4363 if (redirect)
4364 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4365 } else if (!strcasecmp(cmd, "</Redirect>")) {
4366 if (!redirect) {
4367 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4368 filename, line_num);
4369 errors++;
4370 } else {
4371 if (!redirect->feed_filename[0]) {
4372 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4373 filename, line_num);
4374 errors++;
4376 redirect = NULL;
4378 } else if (!strcasecmp(cmd, "LoadModule")) {
4379 get_arg(arg, sizeof(arg), &p);
4380 #ifdef HAVE_DLOPEN
4381 load_module(arg);
4382 #else
4383 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4384 filename, line_num, arg);
4385 errors++;
4386 #endif
4387 } else {
4388 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4389 filename, line_num, cmd);
4390 errors++;
4394 fclose(f);
4395 if (errors)
4396 return -1;
4397 else
4398 return 0;
4401 static void handle_child_exit(int sig)
4403 pid_t pid;
4404 int status;
4406 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4407 FFStream *feed;
4409 for (feed = first_feed; feed; feed = feed->next) {
4410 if (feed->pid == pid) {
4411 int uptime = time(0) - feed->pid_start;
4413 feed->pid = 0;
4414 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4416 if (uptime < 30)
4417 /* Turn off any more restarts */
4418 feed->child_argv = 0;
4423 need_to_start_children = 1;
4426 static void opt_debug()
4428 ffserver_debug = 1;
4429 ffserver_daemon = 0;
4430 logfilename[0] = '-';
4433 static void opt_show_help(void)
4435 printf("usage: ffserver [options]\n"
4436 "Hyper fast multi format Audio/Video streaming server\n");
4437 printf("\n");
4438 show_help_options(options, "Main options:\n", 0, 0);
4441 static const OptionDef options[] = {
4442 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4443 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4444 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4445 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4446 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4447 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4448 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4449 { NULL },
4452 int main(int argc, char **argv)
4454 struct sigaction sigact;
4456 av_register_all();
4458 show_banner();
4460 config_filename = "/etc/ffserver.conf";
4462 my_program_name = argv[0];
4463 my_program_dir = getcwd(0, 0);
4464 ffserver_daemon = 1;
4466 parse_options(argc, argv, options, NULL);
4468 unsetenv("http_proxy"); /* Kill the http_proxy */
4470 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4472 memset(&sigact, 0, sizeof(sigact));
4473 sigact.sa_handler = handle_child_exit;
4474 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4475 sigaction(SIGCHLD, &sigact, 0);
4477 if (parse_ffconfig(config_filename) < 0) {
4478 fprintf(stderr, "Incorrect config file - exiting.\n");
4479 exit(1);
4482 /* open log file if needed */
4483 if (logfilename[0] != '\0') {
4484 if (!strcmp(logfilename, "-"))
4485 logfile = stdout;
4486 else
4487 logfile = fopen(logfilename, "a");
4488 av_log_set_callback(http_av_log);
4491 build_file_streams();
4493 build_feed_streams();
4495 compute_bandwidth();
4497 /* put the process in background and detach it from its TTY */
4498 if (ffserver_daemon) {
4499 int pid;
4501 pid = fork();
4502 if (pid < 0) {
4503 perror("fork");
4504 exit(1);
4505 } else if (pid > 0) {
4506 /* parent : exit */
4507 exit(0);
4508 } else {
4509 /* child */
4510 setsid();
4511 close(0);
4512 open("/dev/null", O_RDWR);
4513 if (strcmp(logfilename, "-") != 0) {
4514 close(1);
4515 dup(0);
4517 close(2);
4518 dup(0);
4522 /* signal init */
4523 signal(SIGPIPE, SIG_IGN);
4525 if (ffserver_daemon)
4526 chdir("/");
4528 if (http_server() < 0) {
4529 http_log("Could not start server\n");
4530 exit(1);
4533 return 0;