rename AC3_MAX_FRAME_SIZE to AC3_FRAME_BUFFER_SIZE and increase the size
[ffmpeg-lucabe.git] / ffserver.c
blob08b89a885f178e2853617bb321a6d465b6807a0e
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 #define _XOPEN_SOURCE 600
24 #include "config.h"
25 #ifndef HAVE_CLOSESOCKET
26 #define closesocket close
27 #endif
28 #include <string.h>
29 #include <strings.h>
30 #include <stdlib.h>
31 #include "libavutil/random.h"
32 #include "libavutil/avstring.h"
33 #include "libavformat/avformat.h"
34 #include "libavformat/network.h"
35 #include "libavformat/os_support.h"
36 #include "libavformat/rtp.h"
37 #include "libavformat/rtsp.h"
38 #include "libavcodec/opt.h"
39 #include <stdarg.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42 #include <sys/ioctl.h>
43 #ifdef HAVE_POLL_H
44 #include <poll.h>
45 #endif
46 #include <errno.h>
47 #include <sys/time.h>
48 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
49 #include <time.h>
50 #include <sys/wait.h>
51 #include <signal.h>
52 #ifdef HAVE_DLFCN_H
53 #include <dlfcn.h>
54 #endif
56 #include "cmdutils.h"
58 #undef exit
60 const char program_name[] = "FFserver";
61 const int program_birth_year = 2000;
63 static const OptionDef options[];
65 /* maximum number of simultaneous HTTP connections */
66 #define HTTP_MAX_CONNECTIONS 2000
68 enum HTTPState {
69 HTTPSTATE_WAIT_REQUEST,
70 HTTPSTATE_SEND_HEADER,
71 HTTPSTATE_SEND_DATA_HEADER,
72 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
73 HTTPSTATE_SEND_DATA_TRAILER,
74 HTTPSTATE_RECEIVE_DATA,
75 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
76 HTTPSTATE_READY,
78 RTSPSTATE_WAIT_REQUEST,
79 RTSPSTATE_SEND_REPLY,
80 RTSPSTATE_SEND_PACKET,
83 static const char *http_state[] = {
84 "HTTP_WAIT_REQUEST",
85 "HTTP_SEND_HEADER",
87 "SEND_DATA_HEADER",
88 "SEND_DATA",
89 "SEND_DATA_TRAILER",
90 "RECEIVE_DATA",
91 "WAIT_FEED",
92 "READY",
94 "RTSP_WAIT_REQUEST",
95 "RTSP_SEND_REPLY",
96 "RTSP_SEND_PACKET",
99 #define IOBUFFER_INIT_SIZE 8192
101 /* timeouts are in ms */
102 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
103 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
105 #define SYNC_TIMEOUT (10 * 1000)
107 typedef struct {
108 int64_t count1, count2;
109 int64_t time1, time2;
110 } DataRateData;
112 /* context associated with one connection */
113 typedef struct HTTPContext {
114 enum HTTPState state;
115 int fd; /* socket file descriptor */
116 struct sockaddr_in from_addr; /* origin */
117 struct pollfd *poll_entry; /* used when polling */
118 int64_t timeout;
119 uint8_t *buffer_ptr, *buffer_end;
120 int http_error;
121 int post;
122 struct HTTPContext *next;
123 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
124 int64_t data_count;
125 /* feed input */
126 int feed_fd;
127 /* input format handling */
128 AVFormatContext *fmt_in;
129 int64_t start_time; /* In milliseconds - this wraps fairly often */
130 int64_t first_pts; /* initial pts value */
131 int64_t cur_pts; /* current pts value from the stream in us */
132 int64_t cur_frame_duration; /* duration of the current frame in us */
133 int cur_frame_bytes; /* output frame size, needed to compute
134 the time at which we send each
135 packet */
136 int pts_stream_index; /* stream we choose as clock reference */
137 int64_t cur_clock; /* current clock reference value in us */
138 /* output format handling */
139 struct FFStream *stream;
140 /* -1 is invalid stream */
141 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
142 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
143 int switch_pending;
144 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
145 int last_packet_sent; /* true if last data packet was sent */
146 int suppress_log;
147 DataRateData datarate;
148 int wmp_client_id;
149 char protocol[16];
150 char method[16];
151 char url[128];
152 int buffer_size;
153 uint8_t *buffer;
154 int is_packetized; /* if true, the stream is packetized */
155 int packet_stream_index; /* current stream for output in state machine */
157 /* RTSP state specific */
158 uint8_t *pb_buffer; /* XXX: use that in all the code */
159 ByteIOContext *pb;
160 int seq; /* RTSP sequence number */
162 /* RTP state specific */
163 enum RTSPProtocol rtp_protocol;
164 char session_id[32]; /* session id */
165 AVFormatContext *rtp_ctx[MAX_STREAMS];
167 /* RTP/UDP specific */
168 URLContext *rtp_handles[MAX_STREAMS];
170 /* RTP/TCP specific */
171 struct HTTPContext *rtsp_c;
172 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
173 } HTTPContext;
175 /* each generated stream is described here */
176 enum StreamType {
177 STREAM_TYPE_LIVE,
178 STREAM_TYPE_STATUS,
179 STREAM_TYPE_REDIRECT,
182 enum IPAddressAction {
183 IP_ALLOW = 1,
184 IP_DENY,
187 typedef struct IPAddressACL {
188 struct IPAddressACL *next;
189 enum IPAddressAction action;
190 /* These are in host order */
191 struct in_addr first;
192 struct in_addr last;
193 } IPAddressACL;
195 /* description of each stream of the ffserver.conf file */
196 typedef struct FFStream {
197 enum StreamType stream_type;
198 char filename[1024]; /* stream filename */
199 struct FFStream *feed; /* feed we are using (can be null if
200 coming from file) */
201 AVFormatParameters *ap_in; /* input parameters */
202 AVInputFormat *ifmt; /* if non NULL, force input format */
203 AVOutputFormat *fmt;
204 IPAddressACL *acl;
205 int nb_streams;
206 int prebuffer; /* Number of millseconds early to start */
207 int64_t max_time; /* Number of milliseconds to run */
208 int send_on_key;
209 AVStream *streams[MAX_STREAMS];
210 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
211 char feed_filename[1024]; /* file name of the feed storage, or
212 input file name for a stream */
213 char author[512];
214 char title[512];
215 char copyright[512];
216 char comment[512];
217 pid_t pid; /* Of ffmpeg process */
218 time_t pid_start; /* Of ffmpeg process */
219 char **child_argv;
220 struct FFStream *next;
221 unsigned bandwidth; /* bandwidth, in kbits/s */
222 /* RTSP options */
223 char *rtsp_option;
224 /* multicast specific */
225 int is_multicast;
226 struct in_addr multicast_ip;
227 int multicast_port; /* first port used for multicast */
228 int multicast_ttl;
229 int loop; /* if true, send the stream in loops (only meaningful if file) */
231 /* feed specific */
232 int feed_opened; /* true if someone is writing to the feed */
233 int is_feed; /* true if it is a feed */
234 int readonly; /* True if writing is prohibited to the file */
235 int conns_served;
236 int64_t bytes_served;
237 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
238 int64_t feed_write_index; /* current write position in feed (it wraps around) */
239 int64_t feed_size; /* current size of feed */
240 struct FFStream *next_feed;
241 } FFStream;
243 typedef struct FeedData {
244 long long data_count;
245 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
246 } FeedData;
248 static struct sockaddr_in my_http_addr;
249 static struct sockaddr_in my_rtsp_addr;
251 static char logfilename[1024];
252 static HTTPContext *first_http_ctx;
253 static FFStream *first_feed; /* contains only feeds */
254 static FFStream *first_stream; /* contains all streams, including feeds */
256 static void new_connection(int server_fd, int is_rtsp);
257 static void close_connection(HTTPContext *c);
259 /* HTTP handling */
260 static int handle_connection(HTTPContext *c);
261 static int http_parse_request(HTTPContext *c);
262 static int http_send_data(HTTPContext *c);
263 static void compute_status(HTTPContext *c);
264 static int open_input_stream(HTTPContext *c, const char *info);
265 static int http_start_receive_data(HTTPContext *c);
266 static int http_receive_data(HTTPContext *c);
268 /* RTSP handling */
269 static int rtsp_parse_request(HTTPContext *c);
270 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
271 static void rtsp_cmd_options(HTTPContext *c, const char *url);
272 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
273 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
274 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
275 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
277 /* SDP handling */
278 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
279 struct in_addr my_ip);
281 /* RTP handling */
282 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
283 FFStream *stream, const char *session_id,
284 enum RTSPProtocol rtp_protocol);
285 static int rtp_new_av_stream(HTTPContext *c,
286 int stream_index, struct sockaddr_in *dest_addr,
287 HTTPContext *rtsp_c);
289 static const char *my_program_name;
290 static const char *my_program_dir;
292 static const char *config_filename;
293 static int ffserver_debug;
294 static int ffserver_daemon;
295 static int no_launch;
296 static int need_to_start_children;
298 static int nb_max_connections = 5;
299 static int nb_connections;
301 static uint64_t max_bandwidth = 1000;
302 static uint64_t current_bandwidth;
304 static int64_t cur_time; // Making this global saves on passing it around everywhere
306 static AVRandomState random_state;
308 static FILE *logfile = NULL;
310 static char *ctime1(char *buf2)
312 time_t ti;
313 char *p;
315 ti = time(NULL);
316 p = ctime(&ti);
317 strcpy(buf2, p);
318 p = buf2 + strlen(p) - 1;
319 if (*p == '\n')
320 *p = '\0';
321 return buf2;
324 static void http_vlog(const char *fmt, va_list vargs)
326 static int print_prefix = 1;
327 if (logfile) {
328 if (print_prefix) {
329 char buf[32];
330 ctime1(buf);
331 fprintf(logfile, "%s ", buf);
333 print_prefix = strstr(fmt, "\n") != NULL;
334 vfprintf(logfile, fmt, vargs);
335 fflush(logfile);
339 void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
341 va_list vargs;
342 va_start(vargs, fmt);
343 http_vlog(fmt, vargs);
344 va_end(vargs);
347 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
349 static int print_prefix = 1;
350 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
351 if (level > av_log_level)
352 return;
353 if (print_prefix && avc)
354 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
355 print_prefix = strstr(fmt, "\n") != NULL;
356 http_vlog(fmt, vargs);
359 static void log_connection(HTTPContext *c)
361 if (c->suppress_log)
362 return;
364 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
365 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
366 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
369 static void update_datarate(DataRateData *drd, int64_t count)
371 if (!drd->time1 && !drd->count1) {
372 drd->time1 = drd->time2 = cur_time;
373 drd->count1 = drd->count2 = count;
374 } else if (cur_time - drd->time2 > 5000) {
375 drd->time1 = drd->time2;
376 drd->count1 = drd->count2;
377 drd->time2 = cur_time;
378 drd->count2 = count;
382 /* In bytes per second */
383 static int compute_datarate(DataRateData *drd, int64_t count)
385 if (cur_time == drd->time1)
386 return 0;
388 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
392 static void start_children(FFStream *feed)
394 if (no_launch)
395 return;
397 for (; feed; feed = feed->next) {
398 if (feed->child_argv && !feed->pid) {
399 feed->pid_start = time(0);
401 feed->pid = fork();
403 if (feed->pid < 0) {
404 http_log("Unable to create children\n");
405 exit(1);
407 if (!feed->pid) {
408 /* In child */
409 char pathname[1024];
410 char *slash;
411 int i;
413 av_strlcpy(pathname, my_program_name, sizeof(pathname));
415 slash = strrchr(pathname, '/');
416 if (!slash)
417 slash = pathname;
418 else
419 slash++;
420 strcpy(slash, "ffmpeg");
422 http_log("Launch commandline: ");
423 http_log("%s ", pathname);
424 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
425 http_log("%s ", feed->child_argv[i]);
426 http_log("\n");
428 for (i = 3; i < 256; i++)
429 close(i);
431 if (!ffserver_debug) {
432 i = open("/dev/null", O_RDWR);
433 if (i != -1) {
434 dup2(i, 0);
435 dup2(i, 1);
436 dup2(i, 2);
437 close(i);
441 /* This is needed to make relative pathnames work */
442 chdir(my_program_dir);
444 signal(SIGPIPE, SIG_DFL);
446 execvp(pathname, feed->child_argv);
448 _exit(1);
454 /* open a listening socket */
455 static int socket_open_listen(struct sockaddr_in *my_addr)
457 int server_fd, tmp;
459 server_fd = socket(AF_INET,SOCK_STREAM,0);
460 if (server_fd < 0) {
461 perror ("socket");
462 return -1;
465 tmp = 1;
466 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
468 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
469 char bindmsg[32];
470 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
471 perror (bindmsg);
472 closesocket(server_fd);
473 return -1;
476 if (listen (server_fd, 5) < 0) {
477 perror ("listen");
478 closesocket(server_fd);
479 return -1;
481 ff_socket_nonblock(server_fd, 1);
483 return server_fd;
486 /* start all multicast streams */
487 static void start_multicast(void)
489 FFStream *stream;
490 char session_id[32];
491 HTTPContext *rtp_c;
492 struct sockaddr_in dest_addr;
493 int default_port, stream_index;
495 default_port = 6000;
496 for(stream = first_stream; stream != NULL; stream = stream->next) {
497 if (stream->is_multicast) {
498 /* open the RTP connection */
499 snprintf(session_id, sizeof(session_id), "%08x%08x",
500 av_random(&random_state), av_random(&random_state));
502 /* choose a port if none given */
503 if (stream->multicast_port == 0) {
504 stream->multicast_port = default_port;
505 default_port += 100;
508 dest_addr.sin_family = AF_INET;
509 dest_addr.sin_addr = stream->multicast_ip;
510 dest_addr.sin_port = htons(stream->multicast_port);
512 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
513 RTSP_PROTOCOL_RTP_UDP_MULTICAST);
514 if (!rtp_c)
515 continue;
517 if (open_input_stream(rtp_c, "") < 0) {
518 http_log("Could not open input stream for stream '%s'\n",
519 stream->filename);
520 continue;
523 /* open each RTP stream */
524 for(stream_index = 0; stream_index < stream->nb_streams;
525 stream_index++) {
526 dest_addr.sin_port = htons(stream->multicast_port +
527 2 * stream_index);
528 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
529 http_log("Could not open output stream '%s/streamid=%d'\n",
530 stream->filename, stream_index);
531 exit(1);
535 /* change state to send data */
536 rtp_c->state = HTTPSTATE_SEND_DATA;
541 /* main loop of the http server */
542 static int http_server(void)
544 int server_fd = 0, rtsp_server_fd = 0;
545 int ret, delay, delay1;
546 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
547 HTTPContext *c, *c_next;
549 if (my_http_addr.sin_port) {
550 server_fd = socket_open_listen(&my_http_addr);
551 if (server_fd < 0)
552 return -1;
555 if (my_rtsp_addr.sin_port) {
556 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
557 if (rtsp_server_fd < 0)
558 return -1;
561 if (!rtsp_server_fd && !server_fd) {
562 http_log("HTTP and RTSP disabled.\n");
563 return -1;
566 http_log("ffserver started.\n");
568 start_children(first_feed);
570 start_multicast();
572 for(;;) {
573 poll_entry = poll_table;
574 if (server_fd) {
575 poll_entry->fd = server_fd;
576 poll_entry->events = POLLIN;
577 poll_entry++;
579 if (rtsp_server_fd) {
580 poll_entry->fd = rtsp_server_fd;
581 poll_entry->events = POLLIN;
582 poll_entry++;
585 /* wait for events on each HTTP handle */
586 c = first_http_ctx;
587 delay = 1000;
588 while (c != NULL) {
589 int fd;
590 fd = c->fd;
591 switch(c->state) {
592 case HTTPSTATE_SEND_HEADER:
593 case RTSPSTATE_SEND_REPLY:
594 case RTSPSTATE_SEND_PACKET:
595 c->poll_entry = poll_entry;
596 poll_entry->fd = fd;
597 poll_entry->events = POLLOUT;
598 poll_entry++;
599 break;
600 case HTTPSTATE_SEND_DATA_HEADER:
601 case HTTPSTATE_SEND_DATA:
602 case HTTPSTATE_SEND_DATA_TRAILER:
603 if (!c->is_packetized) {
604 /* for TCP, we output as much as we can (may need to put a limit) */
605 c->poll_entry = poll_entry;
606 poll_entry->fd = fd;
607 poll_entry->events = POLLOUT;
608 poll_entry++;
609 } else {
610 /* when ffserver is doing the timing, we work by
611 looking at which packet need to be sent every
612 10 ms */
613 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
614 if (delay1 < delay)
615 delay = delay1;
617 break;
618 case HTTPSTATE_WAIT_REQUEST:
619 case HTTPSTATE_RECEIVE_DATA:
620 case HTTPSTATE_WAIT_FEED:
621 case RTSPSTATE_WAIT_REQUEST:
622 /* need to catch errors */
623 c->poll_entry = poll_entry;
624 poll_entry->fd = fd;
625 poll_entry->events = POLLIN;/* Maybe this will work */
626 poll_entry++;
627 break;
628 default:
629 c->poll_entry = NULL;
630 break;
632 c = c->next;
635 /* wait for an event on one connection. We poll at least every
636 second to handle timeouts */
637 do {
638 ret = poll(poll_table, poll_entry - poll_table, delay);
639 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
640 ff_neterrno() != FF_NETERROR(EINTR))
641 return -1;
642 } while (ret < 0);
644 cur_time = av_gettime() / 1000;
646 if (need_to_start_children) {
647 need_to_start_children = 0;
648 start_children(first_feed);
651 /* now handle the events */
652 for(c = first_http_ctx; c != NULL; c = c_next) {
653 c_next = c->next;
654 if (handle_connection(c) < 0) {
655 /* close and free the connection */
656 log_connection(c);
657 close_connection(c);
661 poll_entry = poll_table;
662 if (server_fd) {
663 /* new HTTP connection request ? */
664 if (poll_entry->revents & POLLIN)
665 new_connection(server_fd, 0);
666 poll_entry++;
668 if (rtsp_server_fd) {
669 /* new RTSP connection request ? */
670 if (poll_entry->revents & POLLIN)
671 new_connection(rtsp_server_fd, 1);
676 /* start waiting for a new HTTP/RTSP request */
677 static void start_wait_request(HTTPContext *c, int is_rtsp)
679 c->buffer_ptr = c->buffer;
680 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
682 if (is_rtsp) {
683 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
684 c->state = RTSPSTATE_WAIT_REQUEST;
685 } else {
686 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
687 c->state = HTTPSTATE_WAIT_REQUEST;
691 static void new_connection(int server_fd, int is_rtsp)
693 struct sockaddr_in from_addr;
694 int fd, len;
695 HTTPContext *c = NULL;
697 len = sizeof(from_addr);
698 fd = accept(server_fd, (struct sockaddr *)&from_addr,
699 &len);
700 if (fd < 0) {
701 http_log("error during accept %s\n", strerror(errno));
702 return;
704 ff_socket_nonblock(fd, 1);
706 /* XXX: should output a warning page when coming
707 close to the connection limit */
708 if (nb_connections >= nb_max_connections)
709 goto fail;
711 /* add a new connection */
712 c = av_mallocz(sizeof(HTTPContext));
713 if (!c)
714 goto fail;
716 c->fd = fd;
717 c->poll_entry = NULL;
718 c->from_addr = from_addr;
719 c->buffer_size = IOBUFFER_INIT_SIZE;
720 c->buffer = av_malloc(c->buffer_size);
721 if (!c->buffer)
722 goto fail;
724 c->next = first_http_ctx;
725 first_http_ctx = c;
726 nb_connections++;
728 start_wait_request(c, is_rtsp);
730 return;
732 fail:
733 if (c) {
734 av_free(c->buffer);
735 av_free(c);
737 closesocket(fd);
740 static void close_connection(HTTPContext *c)
742 HTTPContext **cp, *c1;
743 int i, nb_streams;
744 AVFormatContext *ctx;
745 URLContext *h;
746 AVStream *st;
748 /* remove connection from list */
749 cp = &first_http_ctx;
750 while ((*cp) != NULL) {
751 c1 = *cp;
752 if (c1 == c)
753 *cp = c->next;
754 else
755 cp = &c1->next;
758 /* remove references, if any (XXX: do it faster) */
759 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
760 if (c1->rtsp_c == c)
761 c1->rtsp_c = NULL;
764 /* remove connection associated resources */
765 if (c->fd >= 0)
766 closesocket(c->fd);
767 if (c->fmt_in) {
768 /* close each frame parser */
769 for(i=0;i<c->fmt_in->nb_streams;i++) {
770 st = c->fmt_in->streams[i];
771 if (st->codec->codec)
772 avcodec_close(st->codec);
774 av_close_input_file(c->fmt_in);
777 /* free RTP output streams if any */
778 nb_streams = 0;
779 if (c->stream)
780 nb_streams = c->stream->nb_streams;
782 for(i=0;i<nb_streams;i++) {
783 ctx = c->rtp_ctx[i];
784 if (ctx) {
785 av_write_trailer(ctx);
786 av_free(ctx);
788 h = c->rtp_handles[i];
789 if (h)
790 url_close(h);
793 ctx = &c->fmt_ctx;
795 if (!c->last_packet_sent) {
796 if (ctx->oformat) {
797 /* prepare header */
798 if (url_open_dyn_buf(&ctx->pb) >= 0) {
799 av_write_trailer(ctx);
800 av_freep(&c->pb_buffer);
801 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
806 for(i=0; i<ctx->nb_streams; i++)
807 av_free(ctx->streams[i]);
809 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
810 current_bandwidth -= c->stream->bandwidth;
812 /* signal that there is no feed if we are the feeder socket */
813 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
814 c->stream->feed_opened = 0;
815 close(c->feed_fd);
818 av_freep(&c->pb_buffer);
819 av_freep(&c->packet_buffer);
820 av_free(c->buffer);
821 av_free(c);
822 nb_connections--;
825 static int handle_connection(HTTPContext *c)
827 int len, ret;
829 switch(c->state) {
830 case HTTPSTATE_WAIT_REQUEST:
831 case RTSPSTATE_WAIT_REQUEST:
832 /* timeout ? */
833 if ((c->timeout - cur_time) < 0)
834 return -1;
835 if (c->poll_entry->revents & (POLLERR | POLLHUP))
836 return -1;
838 /* no need to read if no events */
839 if (!(c->poll_entry->revents & POLLIN))
840 return 0;
841 /* read the data */
842 read_loop:
843 len = recv(c->fd, c->buffer_ptr, 1, 0);
844 if (len < 0) {
845 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
846 ff_neterrno() != FF_NETERROR(EINTR))
847 return -1;
848 } else if (len == 0) {
849 return -1;
850 } else {
851 /* search for end of request. */
852 uint8_t *ptr;
853 c->buffer_ptr += len;
854 ptr = c->buffer_ptr;
855 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
856 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
857 /* request found : parse it and reply */
858 if (c->state == HTTPSTATE_WAIT_REQUEST) {
859 ret = http_parse_request(c);
860 } else {
861 ret = rtsp_parse_request(c);
863 if (ret < 0)
864 return -1;
865 } else if (ptr >= c->buffer_end) {
866 /* request too long: cannot do anything */
867 return -1;
868 } else goto read_loop;
870 break;
872 case HTTPSTATE_SEND_HEADER:
873 if (c->poll_entry->revents & (POLLERR | POLLHUP))
874 return -1;
876 /* no need to write if no events */
877 if (!(c->poll_entry->revents & POLLOUT))
878 return 0;
879 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
880 if (len < 0) {
881 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
882 ff_neterrno() != FF_NETERROR(EINTR)) {
883 /* error : close connection */
884 av_freep(&c->pb_buffer);
885 return -1;
887 } else {
888 c->buffer_ptr += len;
889 if (c->stream)
890 c->stream->bytes_served += len;
891 c->data_count += len;
892 if (c->buffer_ptr >= c->buffer_end) {
893 av_freep(&c->pb_buffer);
894 /* if error, exit */
895 if (c->http_error)
896 return -1;
897 /* all the buffer was sent : synchronize to the incoming stream */
898 c->state = HTTPSTATE_SEND_DATA_HEADER;
899 c->buffer_ptr = c->buffer_end = c->buffer;
902 break;
904 case HTTPSTATE_SEND_DATA:
905 case HTTPSTATE_SEND_DATA_HEADER:
906 case HTTPSTATE_SEND_DATA_TRAILER:
907 /* for packetized output, we consider we can always write (the
908 input streams sets the speed). It may be better to verify
909 that we do not rely too much on the kernel queues */
910 if (!c->is_packetized) {
911 if (c->poll_entry->revents & (POLLERR | POLLHUP))
912 return -1;
914 /* no need to read if no events */
915 if (!(c->poll_entry->revents & POLLOUT))
916 return 0;
918 if (http_send_data(c) < 0)
919 return -1;
920 /* close connection if trailer sent */
921 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
922 return -1;
923 break;
924 case HTTPSTATE_RECEIVE_DATA:
925 /* no need to read if no events */
926 if (c->poll_entry->revents & (POLLERR | POLLHUP))
927 return -1;
928 if (!(c->poll_entry->revents & POLLIN))
929 return 0;
930 if (http_receive_data(c) < 0)
931 return -1;
932 break;
933 case HTTPSTATE_WAIT_FEED:
934 /* no need to read if no events */
935 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
936 return -1;
938 /* nothing to do, we'll be waken up by incoming feed packets */
939 break;
941 case RTSPSTATE_SEND_REPLY:
942 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
943 av_freep(&c->pb_buffer);
944 return -1;
946 /* no need to write if no events */
947 if (!(c->poll_entry->revents & POLLOUT))
948 return 0;
949 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
950 if (len < 0) {
951 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
952 ff_neterrno() != FF_NETERROR(EINTR)) {
953 /* error : close connection */
954 av_freep(&c->pb_buffer);
955 return -1;
957 } else {
958 c->buffer_ptr += len;
959 c->data_count += len;
960 if (c->buffer_ptr >= c->buffer_end) {
961 /* all the buffer was sent : wait for a new request */
962 av_freep(&c->pb_buffer);
963 start_wait_request(c, 1);
966 break;
967 case RTSPSTATE_SEND_PACKET:
968 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
969 av_freep(&c->packet_buffer);
970 return -1;
972 /* no need to write if no events */
973 if (!(c->poll_entry->revents & POLLOUT))
974 return 0;
975 len = send(c->fd, c->packet_buffer_ptr,
976 c->packet_buffer_end - c->packet_buffer_ptr, 0);
977 if (len < 0) {
978 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
979 ff_neterrno() != FF_NETERROR(EINTR)) {
980 /* error : close connection */
981 av_freep(&c->packet_buffer);
982 return -1;
984 } else {
985 c->packet_buffer_ptr += len;
986 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
987 /* all the buffer was sent : wait for a new request */
988 av_freep(&c->packet_buffer);
989 c->state = RTSPSTATE_WAIT_REQUEST;
992 break;
993 case HTTPSTATE_READY:
994 /* nothing to do */
995 break;
996 default:
997 return -1;
999 return 0;
1002 static int extract_rates(char *rates, int ratelen, const char *request)
1004 const char *p;
1006 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1007 if (strncasecmp(p, "Pragma:", 7) == 0) {
1008 const char *q = p + 7;
1010 while (*q && *q != '\n' && isspace(*q))
1011 q++;
1013 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1014 int stream_no;
1015 int rate_no;
1017 q += 20;
1019 memset(rates, 0xff, ratelen);
1021 while (1) {
1022 while (*q && *q != '\n' && *q != ':')
1023 q++;
1025 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1026 break;
1028 stream_no--;
1029 if (stream_no < ratelen && stream_no >= 0)
1030 rates[stream_no] = rate_no;
1032 while (*q && *q != '\n' && !isspace(*q))
1033 q++;
1036 return 1;
1039 p = strchr(p, '\n');
1040 if (!p)
1041 break;
1043 p++;
1046 return 0;
1049 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1051 int i;
1052 int best_bitrate = 100000000;
1053 int best = -1;
1055 for (i = 0; i < feed->nb_streams; i++) {
1056 AVCodecContext *feed_codec = feed->streams[i]->codec;
1058 if (feed_codec->codec_id != codec->codec_id ||
1059 feed_codec->sample_rate != codec->sample_rate ||
1060 feed_codec->width != codec->width ||
1061 feed_codec->height != codec->height)
1062 continue;
1064 /* Potential stream */
1066 /* We want the fastest stream less than bit_rate, or the slowest
1067 * faster than bit_rate
1070 if (feed_codec->bit_rate <= bit_rate) {
1071 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1072 best_bitrate = feed_codec->bit_rate;
1073 best = i;
1075 } else {
1076 if (feed_codec->bit_rate < best_bitrate) {
1077 best_bitrate = feed_codec->bit_rate;
1078 best = i;
1083 return best;
1086 static int modify_current_stream(HTTPContext *c, char *rates)
1088 int i;
1089 FFStream *req = c->stream;
1090 int action_required = 0;
1092 /* Not much we can do for a feed */
1093 if (!req->feed)
1094 return 0;
1096 for (i = 0; i < req->nb_streams; i++) {
1097 AVCodecContext *codec = req->streams[i]->codec;
1099 switch(rates[i]) {
1100 case 0:
1101 c->switch_feed_streams[i] = req->feed_streams[i];
1102 break;
1103 case 1:
1104 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1105 break;
1106 case 2:
1107 /* Wants off or slow */
1108 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1109 #ifdef WANTS_OFF
1110 /* This doesn't work well when it turns off the only stream! */
1111 c->switch_feed_streams[i] = -2;
1112 c->feed_streams[i] = -2;
1113 #endif
1114 break;
1117 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1118 action_required = 1;
1121 return action_required;
1125 static void do_switch_stream(HTTPContext *c, int i)
1127 if (c->switch_feed_streams[i] >= 0) {
1128 #ifdef PHILIP
1129 c->feed_streams[i] = c->switch_feed_streams[i];
1130 #endif
1132 /* Now update the stream */
1134 c->switch_feed_streams[i] = -1;
1137 /* XXX: factorize in utils.c ? */
1138 /* XXX: take care with different space meaning */
1139 static void skip_spaces(const char **pp)
1141 const char *p;
1142 p = *pp;
1143 while (*p == ' ' || *p == '\t')
1144 p++;
1145 *pp = p;
1148 static void get_word(char *buf, int buf_size, const char **pp)
1150 const char *p;
1151 char *q;
1153 p = *pp;
1154 skip_spaces(&p);
1155 q = buf;
1156 while (!isspace(*p) && *p != '\0') {
1157 if ((q - buf) < buf_size - 1)
1158 *q++ = *p;
1159 p++;
1161 if (buf_size > 0)
1162 *q = '\0';
1163 *pp = p;
1166 static int validate_acl(FFStream *stream, HTTPContext *c)
1168 enum IPAddressAction last_action = IP_DENY;
1169 IPAddressACL *acl;
1170 struct in_addr *src = &c->from_addr.sin_addr;
1171 unsigned long src_addr = src->s_addr;
1173 for (acl = stream->acl; acl; acl = acl->next) {
1174 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1175 return (acl->action == IP_ALLOW) ? 1 : 0;
1176 last_action = acl->action;
1179 /* Nothing matched, so return not the last action */
1180 return (last_action == IP_DENY) ? 1 : 0;
1183 /* compute the real filename of a file by matching it without its
1184 extensions to all the stream filenames */
1185 static void compute_real_filename(char *filename, int max_size)
1187 char file1[1024];
1188 char file2[1024];
1189 char *p;
1190 FFStream *stream;
1192 /* compute filename by matching without the file extensions */
1193 av_strlcpy(file1, filename, sizeof(file1));
1194 p = strrchr(file1, '.');
1195 if (p)
1196 *p = '\0';
1197 for(stream = first_stream; stream != NULL; stream = stream->next) {
1198 av_strlcpy(file2, stream->filename, sizeof(file2));
1199 p = strrchr(file2, '.');
1200 if (p)
1201 *p = '\0';
1202 if (!strcmp(file1, file2)) {
1203 av_strlcpy(filename, stream->filename, max_size);
1204 break;
1209 enum RedirType {
1210 REDIR_NONE,
1211 REDIR_ASX,
1212 REDIR_RAM,
1213 REDIR_ASF,
1214 REDIR_RTSP,
1215 REDIR_SDP,
1218 /* parse http request and prepare header */
1219 static int http_parse_request(HTTPContext *c)
1221 char *p;
1222 enum RedirType redir_type;
1223 char cmd[32];
1224 char info[1024], filename[1024];
1225 char url[1024], *q;
1226 char protocol[32];
1227 char msg[1024];
1228 const char *mime_type;
1229 FFStream *stream;
1230 int i;
1231 char ratebuf[32];
1232 char *useragent = 0;
1234 p = c->buffer;
1235 get_word(cmd, sizeof(cmd), (const char **)&p);
1236 av_strlcpy(c->method, cmd, sizeof(c->method));
1238 if (!strcmp(cmd, "GET"))
1239 c->post = 0;
1240 else if (!strcmp(cmd, "POST"))
1241 c->post = 1;
1242 else
1243 return -1;
1245 get_word(url, sizeof(url), (const char **)&p);
1246 av_strlcpy(c->url, url, sizeof(c->url));
1248 get_word(protocol, sizeof(protocol), (const char **)&p);
1249 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1250 return -1;
1252 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1254 if (ffserver_debug)
1255 http_log("New connection: %s %s\n", cmd, url);
1257 /* find the filename and the optional info string in the request */
1258 p = strchr(url, '?');
1259 if (p) {
1260 av_strlcpy(info, p, sizeof(info));
1261 *p = '\0';
1262 } else
1263 info[0] = '\0';
1265 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1267 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1268 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1269 useragent = p + 11;
1270 if (*useragent && *useragent != '\n' && isspace(*useragent))
1271 useragent++;
1272 break;
1274 p = strchr(p, '\n');
1275 if (!p)
1276 break;
1278 p++;
1281 redir_type = REDIR_NONE;
1282 if (match_ext(filename, "asx")) {
1283 redir_type = REDIR_ASX;
1284 filename[strlen(filename)-1] = 'f';
1285 } else if (match_ext(filename, "asf") &&
1286 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1287 /* if this isn't WMP or lookalike, return the redirector file */
1288 redir_type = REDIR_ASF;
1289 } else if (match_ext(filename, "rpm,ram")) {
1290 redir_type = REDIR_RAM;
1291 strcpy(filename + strlen(filename)-2, "m");
1292 } else if (match_ext(filename, "rtsp")) {
1293 redir_type = REDIR_RTSP;
1294 compute_real_filename(filename, sizeof(filename) - 1);
1295 } else if (match_ext(filename, "sdp")) {
1296 redir_type = REDIR_SDP;
1297 compute_real_filename(filename, sizeof(filename) - 1);
1300 // "redirect" / request to index.html
1301 if (!strlen(filename))
1302 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1304 stream = first_stream;
1305 while (stream != NULL) {
1306 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1307 break;
1308 stream = stream->next;
1310 if (stream == NULL) {
1311 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1312 goto send_error;
1315 c->stream = stream;
1316 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1317 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1319 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1320 c->http_error = 301;
1321 q = c->buffer;
1322 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 301 Moved\r\n");
1323 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Location: %s\r\n", stream->feed_filename);
1324 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1325 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1326 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Moved</title></head><body>\r\n");
1327 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1328 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1330 /* prepare output buffer */
1331 c->buffer_ptr = c->buffer;
1332 c->buffer_end = q;
1333 c->state = HTTPSTATE_SEND_HEADER;
1334 return 0;
1337 /* If this is WMP, get the rate information */
1338 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1339 if (modify_current_stream(c, ratebuf)) {
1340 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1341 if (c->switch_feed_streams[i] >= 0)
1342 do_switch_stream(c, i);
1347 /* If already streaming this feed, do not let start another feeder. */
1348 if (stream->feed_opened) {
1349 snprintf(msg, sizeof(msg), "This feed is already being received.");
1350 http_log("feed %s already being received\n", stream->feed_filename);
1351 goto send_error;
1354 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1355 current_bandwidth += stream->bandwidth;
1357 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1358 c->http_error = 200;
1359 q = c->buffer;
1360 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 Server too busy\r\n");
1361 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: text/html\r\n");
1362 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1363 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<html><head><title>Too busy</title></head><body>\r\n");
1364 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");
1365 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",
1366 current_bandwidth, max_bandwidth);
1367 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</body></html>\r\n");
1369 /* prepare output buffer */
1370 c->buffer_ptr = c->buffer;
1371 c->buffer_end = q;
1372 c->state = HTTPSTATE_SEND_HEADER;
1373 return 0;
1376 if (redir_type != REDIR_NONE) {
1377 char *hostinfo = 0;
1379 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1380 if (strncasecmp(p, "Host:", 5) == 0) {
1381 hostinfo = p + 5;
1382 break;
1384 p = strchr(p, '\n');
1385 if (!p)
1386 break;
1388 p++;
1391 if (hostinfo) {
1392 char *eoh;
1393 char hostbuf[260];
1395 while (isspace(*hostinfo))
1396 hostinfo++;
1398 eoh = strchr(hostinfo, '\n');
1399 if (eoh) {
1400 if (eoh[-1] == '\r')
1401 eoh--;
1403 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1404 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1405 hostbuf[eoh - hostinfo] = 0;
1407 c->http_error = 200;
1408 q = c->buffer;
1409 switch(redir_type) {
1410 case REDIR_ASX:
1411 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASX Follows\r\n");
1412 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1413 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1414 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ASX Version=\"3\">\r\n");
1415 //q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<!-- Autogenerated by ffserver -->\r\n");
1416 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1417 hostbuf, filename, info);
1418 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</ASX>\r\n");
1419 break;
1420 case REDIR_RAM:
1421 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RAM Follows\r\n");
1422 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: audio/x-pn-realaudio\r\n");
1423 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1424 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "# Autogenerated by ffserver\r\n");
1425 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "http://%s/%s%s\r\n",
1426 hostbuf, filename, info);
1427 break;
1428 case REDIR_ASF:
1429 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 ASF Redirect follows\r\n");
1430 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: video/x-ms-asf\r\n");
1431 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1432 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "[Reference]\r\n");
1433 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Ref1=http://%s/%s%s\r\n",
1434 hostbuf, filename, info);
1435 break;
1436 case REDIR_RTSP:
1438 char hostname[256], *p;
1439 /* extract only hostname */
1440 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1441 p = strrchr(hostname, ':');
1442 if (p)
1443 *p = '\0';
1444 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1445 /* XXX: incorrect mime type ? */
1446 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/x-rtsp\r\n");
1447 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1448 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "rtsp://%s:%d/%s\r\n",
1449 hostname, ntohs(my_rtsp_addr.sin_port),
1450 filename);
1452 break;
1453 case REDIR_SDP:
1455 uint8_t *sdp_data;
1456 int sdp_data_size, len;
1457 struct sockaddr_in my_addr;
1459 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1460 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: application/sdp\r\n");
1461 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1463 len = sizeof(my_addr);
1464 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1466 /* XXX: should use a dynamic buffer */
1467 sdp_data_size = prepare_sdp_description(stream,
1468 &sdp_data,
1469 my_addr.sin_addr);
1470 if (sdp_data_size > 0) {
1471 memcpy(q, sdp_data, sdp_data_size);
1472 q += sdp_data_size;
1473 *q = '\0';
1474 av_free(sdp_data);
1477 break;
1478 default:
1479 abort();
1480 break;
1483 /* prepare output buffer */
1484 c->buffer_ptr = c->buffer;
1485 c->buffer_end = q;
1486 c->state = HTTPSTATE_SEND_HEADER;
1487 return 0;
1492 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1493 goto send_error;
1496 stream->conns_served++;
1498 /* XXX: add there authenticate and IP match */
1500 if (c->post) {
1501 /* if post, it means a feed is being sent */
1502 if (!stream->is_feed) {
1503 /* However it might be a status report from WMP! Let us log the
1504 * data as it might come in handy one day. */
1505 char *logline = 0;
1506 int client_id = 0;
1508 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1509 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1510 logline = p;
1511 break;
1513 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1514 client_id = strtol(p + 18, 0, 10);
1515 p = strchr(p, '\n');
1516 if (!p)
1517 break;
1519 p++;
1522 if (logline) {
1523 char *eol = strchr(logline, '\n');
1525 logline += 17;
1527 if (eol) {
1528 if (eol[-1] == '\r')
1529 eol--;
1530 http_log("%.*s\n", (int) (eol - logline), logline);
1531 c->suppress_log = 1;
1535 #ifdef DEBUG_WMP
1536 http_log("\nGot request:\n%s\n", c->buffer);
1537 #endif
1539 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1540 HTTPContext *wmpc;
1542 /* Now we have to find the client_id */
1543 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1544 if (wmpc->wmp_client_id == client_id)
1545 break;
1548 if (wmpc && modify_current_stream(wmpc, ratebuf))
1549 wmpc->switch_pending = 1;
1552 snprintf(msg, sizeof(msg), "POST command not handled");
1553 c->stream = 0;
1554 goto send_error;
1556 if (http_start_receive_data(c) < 0) {
1557 snprintf(msg, sizeof(msg), "could not open feed");
1558 goto send_error;
1560 c->http_error = 0;
1561 c->state = HTTPSTATE_RECEIVE_DATA;
1562 return 0;
1565 #ifdef DEBUG_WMP
1566 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1567 http_log("\nGot request:\n%s\n", c->buffer);
1568 #endif
1570 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1571 goto send_status;
1573 /* open input stream */
1574 if (open_input_stream(c, info) < 0) {
1575 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1576 goto send_error;
1579 /* prepare http header */
1580 q = c->buffer;
1581 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1582 mime_type = c->stream->fmt->mime_type;
1583 if (!mime_type)
1584 mime_type = "application/x-octet-stream";
1585 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1587 /* for asf, we need extra headers */
1588 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1589 /* Need to allocate a client id */
1591 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1593 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);
1595 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1596 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1598 /* prepare output buffer */
1599 c->http_error = 0;
1600 c->buffer_ptr = c->buffer;
1601 c->buffer_end = q;
1602 c->state = HTTPSTATE_SEND_HEADER;
1603 return 0;
1604 send_error:
1605 c->http_error = 404;
1606 q = c->buffer;
1607 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 404 Not Found\r\n");
1608 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-type: %s\r\n", "text/html");
1609 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1610 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HTML>\n");
1611 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1612 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "<BODY>%s</BODY>\n", msg);
1613 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "</HTML>\n");
1615 /* prepare output buffer */
1616 c->buffer_ptr = c->buffer;
1617 c->buffer_end = q;
1618 c->state = HTTPSTATE_SEND_HEADER;
1619 return 0;
1620 send_status:
1621 compute_status(c);
1622 c->http_error = 200; /* horrible : we use this value to avoid
1623 going to the send data state */
1624 c->state = HTTPSTATE_SEND_HEADER;
1625 return 0;
1628 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1630 static const char *suffix = " kMGTP";
1631 const char *s;
1633 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1635 url_fprintf(pb, "%"PRId64"%c", count, *s);
1638 static void compute_status(HTTPContext *c)
1640 HTTPContext *c1;
1641 FFStream *stream;
1642 char *p;
1643 time_t ti;
1644 int i, len;
1645 ByteIOContext *pb;
1647 if (url_open_dyn_buf(&pb) < 0) {
1648 /* XXX: return an error ? */
1649 c->buffer_ptr = c->buffer;
1650 c->buffer_end = c->buffer;
1651 return;
1654 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1655 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1656 url_fprintf(pb, "Pragma: no-cache\r\n");
1657 url_fprintf(pb, "\r\n");
1659 url_fprintf(pb, "<HEAD><TITLE>%s Status</TITLE>\n", program_name);
1660 if (c->stream->feed_filename[0])
1661 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1662 url_fprintf(pb, "</HEAD>\n<BODY>");
1663 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1664 /* format status */
1665 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1666 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1667 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");
1668 stream = first_stream;
1669 while (stream != NULL) {
1670 char sfilename[1024];
1671 char *eosf;
1673 if (stream->feed != stream) {
1674 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1675 eosf = sfilename + strlen(sfilename);
1676 if (eosf - sfilename >= 4) {
1677 if (strcmp(eosf - 4, ".asf") == 0)
1678 strcpy(eosf - 4, ".asx");
1679 else if (strcmp(eosf - 3, ".rm") == 0)
1680 strcpy(eosf - 3, ".ram");
1681 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1682 /* generate a sample RTSP director if
1683 unicast. Generate an SDP redirector if
1684 multicast */
1685 eosf = strrchr(sfilename, '.');
1686 if (!eosf)
1687 eosf = sfilename + strlen(sfilename);
1688 if (stream->is_multicast)
1689 strcpy(eosf, ".sdp");
1690 else
1691 strcpy(eosf, ".rtsp");
1695 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1696 sfilename, stream->filename);
1697 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1698 stream->conns_served);
1699 fmt_bytecount(pb, stream->bytes_served);
1700 switch(stream->stream_type) {
1701 case STREAM_TYPE_LIVE: {
1702 int audio_bit_rate = 0;
1703 int video_bit_rate = 0;
1704 const char *audio_codec_name = "";
1705 const char *video_codec_name = "";
1706 const char *audio_codec_name_extra = "";
1707 const char *video_codec_name_extra = "";
1709 for(i=0;i<stream->nb_streams;i++) {
1710 AVStream *st = stream->streams[i];
1711 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1712 switch(st->codec->codec_type) {
1713 case CODEC_TYPE_AUDIO:
1714 audio_bit_rate += st->codec->bit_rate;
1715 if (codec) {
1716 if (*audio_codec_name)
1717 audio_codec_name_extra = "...";
1718 audio_codec_name = codec->name;
1720 break;
1721 case CODEC_TYPE_VIDEO:
1722 video_bit_rate += st->codec->bit_rate;
1723 if (codec) {
1724 if (*video_codec_name)
1725 video_codec_name_extra = "...";
1726 video_codec_name = codec->name;
1728 break;
1729 case CODEC_TYPE_DATA:
1730 video_bit_rate += st->codec->bit_rate;
1731 break;
1732 default:
1733 abort();
1736 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",
1737 stream->fmt->name,
1738 stream->bandwidth,
1739 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1740 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1741 if (stream->feed)
1742 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1743 else
1744 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1745 url_fprintf(pb, "\n");
1747 break;
1748 default:
1749 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1750 break;
1753 stream = stream->next;
1755 url_fprintf(pb, "</TABLE>\n");
1757 stream = first_stream;
1758 while (stream != NULL) {
1759 if (stream->feed == stream) {
1760 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1761 if (stream->pid) {
1762 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1764 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1766 FILE *pid_stat;
1767 char ps_cmd[64];
1769 /* This is somewhat linux specific I guess */
1770 snprintf(ps_cmd, sizeof(ps_cmd),
1771 "ps -o \"%%cpu,cputime\" --no-headers %d",
1772 stream->pid);
1774 pid_stat = popen(ps_cmd, "r");
1775 if (pid_stat) {
1776 char cpuperc[10];
1777 char cpuused[64];
1779 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1780 cpuused) == 2) {
1781 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1782 cpuperc, cpuused);
1784 fclose(pid_stat);
1787 #endif
1789 url_fprintf(pb, "<p>");
1791 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");
1793 for (i = 0; i < stream->nb_streams; i++) {
1794 AVStream *st = stream->streams[i];
1795 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1796 const char *type = "unknown";
1797 char parameters[64];
1799 parameters[0] = 0;
1801 switch(st->codec->codec_type) {
1802 case CODEC_TYPE_AUDIO:
1803 type = "audio";
1804 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1805 break;
1806 case CODEC_TYPE_VIDEO:
1807 type = "video";
1808 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1809 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1810 break;
1811 default:
1812 abort();
1814 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1815 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1817 url_fprintf(pb, "</table>\n");
1820 stream = stream->next;
1823 #if 0
1825 float avg;
1826 AVCodecContext *enc;
1827 char buf[1024];
1829 /* feed status */
1830 stream = first_feed;
1831 while (stream != NULL) {
1832 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1833 url_fprintf(pb, "<TABLE>\n");
1834 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1835 for(i=0;i<stream->nb_streams;i++) {
1836 AVStream *st = stream->streams[i];
1837 FeedData *fdata = st->priv_data;
1838 enc = st->codec;
1840 avcodec_string(buf, sizeof(buf), enc);
1841 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1842 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1843 avg /= enc->frame_size;
1844 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1845 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1847 url_fprintf(pb, "</TABLE>\n");
1848 stream = stream->next_feed;
1851 #endif
1853 /* connection status */
1854 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1856 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1857 nb_connections, nb_max_connections);
1859 url_fprintf(pb, "Bandwidth in use: %lldk / %lldk<BR>\n",
1860 current_bandwidth, max_bandwidth);
1862 url_fprintf(pb, "<TABLE>\n");
1863 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");
1864 c1 = first_http_ctx;
1865 i = 0;
1866 while (c1 != NULL) {
1867 int bitrate;
1868 int j;
1870 bitrate = 0;
1871 if (c1->stream) {
1872 for (j = 0; j < c1->stream->nb_streams; j++) {
1873 if (!c1->stream->feed)
1874 bitrate += c1->stream->streams[j]->codec->bit_rate;
1875 else if (c1->feed_streams[j] >= 0)
1876 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1880 i++;
1881 p = inet_ntoa(c1->from_addr.sin_addr);
1882 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1884 c1->stream ? c1->stream->filename : "",
1885 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1887 c1->protocol,
1888 http_state[c1->state]);
1889 fmt_bytecount(pb, bitrate);
1890 url_fprintf(pb, "<td align=right>");
1891 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1892 url_fprintf(pb, "<td align=right>");
1893 fmt_bytecount(pb, c1->data_count);
1894 url_fprintf(pb, "\n");
1895 c1 = c1->next;
1897 url_fprintf(pb, "</TABLE>\n");
1899 /* date */
1900 ti = time(NULL);
1901 p = ctime(&ti);
1902 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1903 url_fprintf(pb, "</BODY>\n</HTML>\n");
1905 len = url_close_dyn_buf(pb, &c->pb_buffer);
1906 c->buffer_ptr = c->pb_buffer;
1907 c->buffer_end = c->pb_buffer + len;
1910 /* check if the parser needs to be opened for stream i */
1911 static void open_parser(AVFormatContext *s, int i)
1913 AVStream *st = s->streams[i];
1914 AVCodec *codec;
1916 if (!st->codec->codec) {
1917 codec = avcodec_find_decoder(st->codec->codec_id);
1918 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1919 st->codec->parse_only = 1;
1920 if (avcodec_open(st->codec, codec) < 0)
1921 st->codec->parse_only = 0;
1926 static int open_input_stream(HTTPContext *c, const char *info)
1928 char buf[128];
1929 char input_filename[1024];
1930 AVFormatContext *s;
1931 int buf_size, i, ret;
1932 int64_t stream_pos;
1934 /* find file name */
1935 if (c->stream->feed) {
1936 strcpy(input_filename, c->stream->feed->feed_filename);
1937 buf_size = FFM_PACKET_SIZE;
1938 /* compute position (absolute time) */
1939 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1940 stream_pos = parse_date(buf, 0);
1941 if (stream_pos == INT64_MIN)
1942 return -1;
1943 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1944 int prebuffer = strtol(buf, 0, 10);
1945 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1946 } else
1947 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1948 } else {
1949 strcpy(input_filename, c->stream->feed_filename);
1950 buf_size = 0;
1951 /* compute position (relative time) */
1952 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1953 stream_pos = parse_date(buf, 1);
1954 if (stream_pos == INT64_MIN)
1955 return -1;
1956 } else
1957 stream_pos = 0;
1959 if (input_filename[0] == '\0')
1960 return -1;
1962 #if 0
1963 { time_t when = stream_pos / 1000000;
1964 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1966 #endif
1968 /* open stream */
1969 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1970 buf_size, c->stream->ap_in)) < 0) {
1971 http_log("could not open %s: %d\n", input_filename, ret);
1972 return -1;
1974 s->flags |= AVFMT_FLAG_GENPTS;
1975 c->fmt_in = s;
1976 av_find_stream_info(c->fmt_in);
1978 /* open each parser */
1979 for(i=0;i<s->nb_streams;i++)
1980 open_parser(s, i);
1982 /* choose stream as clock source (we favorize video stream if
1983 present) for packet sending */
1984 c->pts_stream_index = 0;
1985 for(i=0;i<c->stream->nb_streams;i++) {
1986 if (c->pts_stream_index == 0 &&
1987 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1988 c->pts_stream_index = i;
1992 #if 1
1993 if (c->fmt_in->iformat->read_seek)
1994 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
1995 #endif
1996 /* set the start time (needed for maxtime and RTP packet timing) */
1997 c->start_time = cur_time;
1998 c->first_pts = AV_NOPTS_VALUE;
1999 return 0;
2002 /* return the server clock (in us) */
2003 static int64_t get_server_clock(HTTPContext *c)
2005 /* compute current pts value from system time */
2006 return (cur_time - c->start_time) * 1000;
2009 /* return the estimated time at which the current packet must be sent
2010 (in us) */
2011 static int64_t get_packet_send_clock(HTTPContext *c)
2013 int bytes_left, bytes_sent, frame_bytes;
2015 frame_bytes = c->cur_frame_bytes;
2016 if (frame_bytes <= 0)
2017 return c->cur_pts;
2018 else {
2019 bytes_left = c->buffer_end - c->buffer_ptr;
2020 bytes_sent = frame_bytes - bytes_left;
2021 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2026 static int http_prepare_data(HTTPContext *c)
2028 int i, len, ret;
2029 AVFormatContext *ctx;
2031 av_freep(&c->pb_buffer);
2032 switch(c->state) {
2033 case HTTPSTATE_SEND_DATA_HEADER:
2034 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2035 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2036 sizeof(c->fmt_ctx.author));
2037 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2038 sizeof(c->fmt_ctx.comment));
2039 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2040 sizeof(c->fmt_ctx.copyright));
2041 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2042 sizeof(c->fmt_ctx.title));
2044 for(i=0;i<c->stream->nb_streams;i++) {
2045 AVStream *st;
2046 AVStream *src;
2047 st = av_mallocz(sizeof(AVStream));
2048 c->fmt_ctx.streams[i] = st;
2049 /* if file or feed, then just take streams from FFStream struct */
2050 if (!c->stream->feed ||
2051 c->stream->feed == c->stream)
2052 src = c->stream->streams[i];
2053 else
2054 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2056 *st = *src;
2057 st->priv_data = 0;
2058 st->codec->frame_number = 0; /* XXX: should be done in
2059 AVStream, not in codec */
2061 /* set output format parameters */
2062 c->fmt_ctx.oformat = c->stream->fmt;
2063 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2065 c->got_key_frame = 0;
2067 /* prepare header and save header data in a stream */
2068 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2069 /* XXX: potential leak */
2070 return -1;
2072 c->fmt_ctx.pb->is_streamed = 1;
2075 * HACK to avoid mpeg ps muxer to spit many underflow errors
2076 * Default value from FFmpeg
2077 * Try to set it use configuration option
2079 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2080 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2082 av_set_parameters(&c->fmt_ctx, NULL);
2083 if (av_write_header(&c->fmt_ctx) < 0) {
2084 http_log("Error writing output header\n");
2085 return -1;
2088 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2089 c->buffer_ptr = c->pb_buffer;
2090 c->buffer_end = c->pb_buffer + len;
2092 c->state = HTTPSTATE_SEND_DATA;
2093 c->last_packet_sent = 0;
2094 break;
2095 case HTTPSTATE_SEND_DATA:
2096 /* find a new packet */
2097 /* read a packet from the input stream */
2098 if (c->stream->feed)
2099 ffm_set_write_index(c->fmt_in,
2100 c->stream->feed->feed_write_index,
2101 c->stream->feed->feed_size);
2103 if (c->stream->max_time &&
2104 c->stream->max_time + c->start_time - cur_time < 0)
2105 /* We have timed out */
2106 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2107 else {
2108 AVPacket pkt;
2109 redo:
2110 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2111 if (c->stream->feed && c->stream->feed->feed_opened) {
2112 /* if coming from feed, it means we reached the end of the
2113 ffm file, so must wait for more data */
2114 c->state = HTTPSTATE_WAIT_FEED;
2115 return 1; /* state changed */
2116 } else {
2117 if (c->stream->loop) {
2118 av_close_input_file(c->fmt_in);
2119 c->fmt_in = NULL;
2120 if (open_input_stream(c, "") < 0)
2121 goto no_loop;
2122 goto redo;
2123 } else {
2124 no_loop:
2125 /* must send trailer now because eof or error */
2126 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2129 } else {
2130 int source_index = pkt.stream_index;
2131 /* update first pts if needed */
2132 if (c->first_pts == AV_NOPTS_VALUE) {
2133 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2134 c->start_time = cur_time;
2136 /* send it to the appropriate stream */
2137 if (c->stream->feed) {
2138 /* if coming from a feed, select the right stream */
2139 if (c->switch_pending) {
2140 c->switch_pending = 0;
2141 for(i=0;i<c->stream->nb_streams;i++) {
2142 if (c->switch_feed_streams[i] == pkt.stream_index)
2143 if (pkt.flags & PKT_FLAG_KEY)
2144 do_switch_stream(c, i);
2145 if (c->switch_feed_streams[i] >= 0)
2146 c->switch_pending = 1;
2149 for(i=0;i<c->stream->nb_streams;i++) {
2150 if (c->feed_streams[i] == pkt.stream_index) {
2151 AVStream *st = c->fmt_in->streams[source_index];
2152 pkt.stream_index = i;
2153 if (pkt.flags & PKT_FLAG_KEY &&
2154 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2155 c->stream->nb_streams == 1))
2156 c->got_key_frame = 1;
2157 if (!c->stream->send_on_key || c->got_key_frame)
2158 goto send_it;
2161 } else {
2162 AVCodecContext *codec;
2163 AVStream *ist, *ost;
2164 send_it:
2165 ist = c->fmt_in->streams[source_index];
2166 /* specific handling for RTP: we use several
2167 output stream (one for each RTP
2168 connection). XXX: need more abstract handling */
2169 if (c->is_packetized) {
2170 /* compute send time and duration */
2171 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2172 if (ist->start_time != AV_NOPTS_VALUE)
2173 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
2174 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2175 #if 0
2176 printf("index=%d pts=%0.3f duration=%0.6f\n",
2177 pkt.stream_index,
2178 (double)c->cur_pts /
2179 AV_TIME_BASE,
2180 (double)c->cur_frame_duration /
2181 AV_TIME_BASE);
2182 #endif
2183 /* find RTP context */
2184 c->packet_stream_index = pkt.stream_index;
2185 ctx = c->rtp_ctx[c->packet_stream_index];
2186 if(!ctx) {
2187 av_free_packet(&pkt);
2188 break;
2190 codec = ctx->streams[0]->codec;
2191 /* only one stream per RTP connection */
2192 pkt.stream_index = 0;
2193 } else {
2194 ctx = &c->fmt_ctx;
2195 /* Fudge here */
2196 codec = ctx->streams[pkt.stream_index]->codec;
2199 if (c->is_packetized) {
2200 int max_packet_size;
2201 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP)
2202 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2203 else
2204 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2205 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2206 } else {
2207 ret = url_open_dyn_buf(&ctx->pb);
2209 if (ret < 0) {
2210 /* XXX: potential leak */
2211 return -1;
2213 ost = ctx->streams[pkt.stream_index];
2215 ctx->pb->is_streamed = 1;
2216 if (pkt.dts != AV_NOPTS_VALUE)
2217 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2218 if (pkt.pts != AV_NOPTS_VALUE)
2219 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2220 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2221 if (av_write_frame(ctx, &pkt) < 0) {
2222 http_log("Error writing frame to output\n");
2223 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2226 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2227 c->cur_frame_bytes = len;
2228 c->buffer_ptr = c->pb_buffer;
2229 c->buffer_end = c->pb_buffer + len;
2231 codec->frame_number++;
2232 if (len == 0) {
2233 av_free_packet(&pkt);
2234 goto redo;
2237 av_free_packet(&pkt);
2240 break;
2241 default:
2242 case HTTPSTATE_SEND_DATA_TRAILER:
2243 /* last packet test ? */
2244 if (c->last_packet_sent || c->is_packetized)
2245 return -1;
2246 ctx = &c->fmt_ctx;
2247 /* prepare header */
2248 if (url_open_dyn_buf(&ctx->pb) < 0) {
2249 /* XXX: potential leak */
2250 return -1;
2252 c->fmt_ctx.pb->is_streamed = 1;
2253 av_write_trailer(ctx);
2254 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2255 c->buffer_ptr = c->pb_buffer;
2256 c->buffer_end = c->pb_buffer + len;
2258 c->last_packet_sent = 1;
2259 break;
2261 return 0;
2264 /* should convert the format at the same time */
2265 /* send data starting at c->buffer_ptr to the output connection
2266 (either UDP or TCP connection) */
2267 static int http_send_data(HTTPContext *c)
2269 int len, ret;
2271 for(;;) {
2272 if (c->buffer_ptr >= c->buffer_end) {
2273 ret = http_prepare_data(c);
2274 if (ret < 0)
2275 return -1;
2276 else if (ret != 0)
2277 /* state change requested */
2278 break;
2279 } else {
2280 if (c->is_packetized) {
2281 /* RTP data output */
2282 len = c->buffer_end - c->buffer_ptr;
2283 if (len < 4) {
2284 /* fail safe - should never happen */
2285 fail1:
2286 c->buffer_ptr = c->buffer_end;
2287 return 0;
2289 len = (c->buffer_ptr[0] << 24) |
2290 (c->buffer_ptr[1] << 16) |
2291 (c->buffer_ptr[2] << 8) |
2292 (c->buffer_ptr[3]);
2293 if (len > (c->buffer_end - c->buffer_ptr))
2294 goto fail1;
2295 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2296 /* nothing to send yet: we can wait */
2297 return 0;
2300 c->data_count += len;
2301 update_datarate(&c->datarate, c->data_count);
2302 if (c->stream)
2303 c->stream->bytes_served += len;
2305 if (c->rtp_protocol == RTSP_PROTOCOL_RTP_TCP) {
2306 /* RTP packets are sent inside the RTSP TCP connection */
2307 ByteIOContext *pb;
2308 int interleaved_index, size;
2309 uint8_t header[4];
2310 HTTPContext *rtsp_c;
2312 rtsp_c = c->rtsp_c;
2313 /* if no RTSP connection left, error */
2314 if (!rtsp_c)
2315 return -1;
2316 /* if already sending something, then wait. */
2317 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2318 break;
2319 if (url_open_dyn_buf(&pb) < 0)
2320 goto fail1;
2321 interleaved_index = c->packet_stream_index * 2;
2322 /* RTCP packets are sent at odd indexes */
2323 if (c->buffer_ptr[1] == 200)
2324 interleaved_index++;
2325 /* write RTSP TCP header */
2326 header[0] = '$';
2327 header[1] = interleaved_index;
2328 header[2] = len >> 8;
2329 header[3] = len;
2330 put_buffer(pb, header, 4);
2331 /* write RTP packet data */
2332 c->buffer_ptr += 4;
2333 put_buffer(pb, c->buffer_ptr, len);
2334 size = url_close_dyn_buf(pb, &c->packet_buffer);
2335 /* prepare asynchronous TCP sending */
2336 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2337 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2338 c->buffer_ptr += len;
2340 /* send everything we can NOW */
2341 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2342 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2343 if (len > 0)
2344 rtsp_c->packet_buffer_ptr += len;
2345 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2346 /* if we could not send all the data, we will
2347 send it later, so a new state is needed to
2348 "lock" the RTSP TCP connection */
2349 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2350 break;
2351 } else
2352 /* all data has been sent */
2353 av_freep(&c->packet_buffer);
2354 } else {
2355 /* send RTP packet directly in UDP */
2356 c->buffer_ptr += 4;
2357 url_write(c->rtp_handles[c->packet_stream_index],
2358 c->buffer_ptr, len);
2359 c->buffer_ptr += len;
2360 /* here we continue as we can send several packets per 10 ms slot */
2362 } else {
2363 /* TCP data output */
2364 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2365 if (len < 0) {
2366 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2367 ff_neterrno() != FF_NETERROR(EINTR))
2368 /* error : close connection */
2369 return -1;
2370 else
2371 return 0;
2372 } else
2373 c->buffer_ptr += len;
2375 c->data_count += len;
2376 update_datarate(&c->datarate, c->data_count);
2377 if (c->stream)
2378 c->stream->bytes_served += len;
2379 break;
2382 } /* for(;;) */
2383 return 0;
2386 static int http_start_receive_data(HTTPContext *c)
2388 int fd;
2390 if (c->stream->feed_opened)
2391 return -1;
2393 /* Don't permit writing to this one */
2394 if (c->stream->readonly)
2395 return -1;
2397 /* open feed */
2398 fd = open(c->stream->feed_filename, O_RDWR);
2399 if (fd < 0) {
2400 http_log("Error opening feeder file: %s\n", strerror(errno));
2401 return -1;
2403 c->feed_fd = fd;
2405 c->stream->feed_write_index = ffm_read_write_index(fd);
2406 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2407 lseek(fd, 0, SEEK_SET);
2409 /* init buffer input */
2410 c->buffer_ptr = c->buffer;
2411 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2412 c->stream->feed_opened = 1;
2413 return 0;
2416 static int http_receive_data(HTTPContext *c)
2418 HTTPContext *c1;
2420 if (c->buffer_end > c->buffer_ptr) {
2421 int len;
2423 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2424 if (len < 0) {
2425 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2426 ff_neterrno() != FF_NETERROR(EINTR))
2427 /* error : close connection */
2428 goto fail;
2429 } else if (len == 0)
2430 /* end of connection : close it */
2431 goto fail;
2432 else {
2433 c->buffer_ptr += len;
2434 c->data_count += len;
2435 update_datarate(&c->datarate, c->data_count);
2439 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2440 if (c->buffer[0] != 'f' ||
2441 c->buffer[1] != 'm') {
2442 http_log("Feed stream has become desynchronized -- disconnecting\n");
2443 goto fail;
2447 if (c->buffer_ptr >= c->buffer_end) {
2448 FFStream *feed = c->stream;
2449 /* a packet has been received : write it in the store, except
2450 if header */
2451 if (c->data_count > FFM_PACKET_SIZE) {
2453 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2454 /* XXX: use llseek or url_seek */
2455 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2456 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2457 http_log("Error writing to feed file: %s\n", strerror(errno));
2458 goto fail;
2461 feed->feed_write_index += FFM_PACKET_SIZE;
2462 /* update file size */
2463 if (feed->feed_write_index > c->stream->feed_size)
2464 feed->feed_size = feed->feed_write_index;
2466 /* handle wrap around if max file size reached */
2467 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2468 feed->feed_write_index = FFM_PACKET_SIZE;
2470 /* write index */
2471 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2473 /* wake up any waiting connections */
2474 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2475 if (c1->state == HTTPSTATE_WAIT_FEED &&
2476 c1->stream->feed == c->stream->feed)
2477 c1->state = HTTPSTATE_SEND_DATA;
2479 } else {
2480 /* We have a header in our hands that contains useful data */
2481 AVFormatContext *s = NULL;
2482 ByteIOContext *pb;
2483 AVInputFormat *fmt_in;
2484 int i;
2486 /* use feed output format name to find corresponding input format */
2487 fmt_in = av_find_input_format(feed->fmt->name);
2488 if (!fmt_in)
2489 goto fail;
2491 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2492 pb->is_streamed = 1;
2494 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2495 av_free(pb);
2496 goto fail;
2499 /* Now we have the actual streams */
2500 if (s->nb_streams != feed->nb_streams) {
2501 av_close_input_stream(s);
2502 av_free(pb);
2503 goto fail;
2506 for (i = 0; i < s->nb_streams; i++) {
2507 AVStream *fst = feed->streams[i];
2508 AVStream *st = s->streams[i];
2509 memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
2510 if (fst->codec->extradata_size) {
2511 fst->codec->extradata = av_malloc(fst->codec->extradata_size);
2512 if (!fst->codec->extradata)
2513 goto fail;
2514 memcpy(fst->codec->extradata, st->codec->extradata,
2515 fst->codec->extradata_size);
2519 av_close_input_stream(s);
2520 av_free(pb);
2522 c->buffer_ptr = c->buffer;
2525 return 0;
2526 fail:
2527 c->stream->feed_opened = 0;
2528 close(c->feed_fd);
2529 /* wake up any waiting connections to stop waiting for feed */
2530 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2531 if (c1->state == HTTPSTATE_WAIT_FEED &&
2532 c1->stream->feed == c->stream->feed)
2533 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2535 return -1;
2538 /********************************************************************/
2539 /* RTSP handling */
2541 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2543 const char *str;
2544 time_t ti;
2545 char *p;
2546 char buf2[32];
2548 switch(error_number) {
2549 case RTSP_STATUS_OK:
2550 str = "OK";
2551 break;
2552 case RTSP_STATUS_METHOD:
2553 str = "Method Not Allowed";
2554 break;
2555 case RTSP_STATUS_BANDWIDTH:
2556 str = "Not Enough Bandwidth";
2557 break;
2558 case RTSP_STATUS_SESSION:
2559 str = "Session Not Found";
2560 break;
2561 case RTSP_STATUS_STATE:
2562 str = "Method Not Valid in This State";
2563 break;
2564 case RTSP_STATUS_AGGREGATE:
2565 str = "Aggregate operation not allowed";
2566 break;
2567 case RTSP_STATUS_ONLY_AGGREGATE:
2568 str = "Only aggregate operation allowed";
2569 break;
2570 case RTSP_STATUS_TRANSPORT:
2571 str = "Unsupported transport";
2572 break;
2573 case RTSP_STATUS_INTERNAL:
2574 str = "Internal Server Error";
2575 break;
2576 case RTSP_STATUS_SERVICE:
2577 str = "Service Unavailable";
2578 break;
2579 case RTSP_STATUS_VERSION:
2580 str = "RTSP Version not supported";
2581 break;
2582 default:
2583 str = "Unknown Error";
2584 break;
2587 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2588 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2590 /* output GMT time */
2591 ti = time(NULL);
2592 p = ctime(&ti);
2593 strcpy(buf2, p);
2594 p = buf2 + strlen(p) - 1;
2595 if (*p == '\n')
2596 *p = '\0';
2597 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2600 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2602 rtsp_reply_header(c, error_number);
2603 url_fprintf(c->pb, "\r\n");
2606 static int rtsp_parse_request(HTTPContext *c)
2608 const char *p, *p1, *p2;
2609 char cmd[32];
2610 char url[1024];
2611 char protocol[32];
2612 char line[1024];
2613 int len;
2614 RTSPHeader header1, *header = &header1;
2616 c->buffer_ptr[0] = '\0';
2617 p = c->buffer;
2619 get_word(cmd, sizeof(cmd), &p);
2620 get_word(url, sizeof(url), &p);
2621 get_word(protocol, sizeof(protocol), &p);
2623 av_strlcpy(c->method, cmd, sizeof(c->method));
2624 av_strlcpy(c->url, url, sizeof(c->url));
2625 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2627 if (url_open_dyn_buf(&c->pb) < 0) {
2628 /* XXX: cannot do more */
2629 c->pb = NULL; /* safety */
2630 return -1;
2633 /* check version name */
2634 if (strcmp(protocol, "RTSP/1.0") != 0) {
2635 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2636 goto the_end;
2639 /* parse each header line */
2640 memset(header, 0, sizeof(RTSPHeader));
2641 /* skip to next line */
2642 while (*p != '\n' && *p != '\0')
2643 p++;
2644 if (*p == '\n')
2645 p++;
2646 while (*p != '\0') {
2647 p1 = strchr(p, '\n');
2648 if (!p1)
2649 break;
2650 p2 = p1;
2651 if (p2 > p && p2[-1] == '\r')
2652 p2--;
2653 /* skip empty line */
2654 if (p2 == p)
2655 break;
2656 len = p2 - p;
2657 if (len > sizeof(line) - 1)
2658 len = sizeof(line) - 1;
2659 memcpy(line, p, len);
2660 line[len] = '\0';
2661 rtsp_parse_line(header, line);
2662 p = p1 + 1;
2665 /* handle sequence number */
2666 c->seq = header->seq;
2668 if (!strcmp(cmd, "DESCRIBE"))
2669 rtsp_cmd_describe(c, url);
2670 else if (!strcmp(cmd, "OPTIONS"))
2671 rtsp_cmd_options(c, url);
2672 else if (!strcmp(cmd, "SETUP"))
2673 rtsp_cmd_setup(c, url, header);
2674 else if (!strcmp(cmd, "PLAY"))
2675 rtsp_cmd_play(c, url, header);
2676 else if (!strcmp(cmd, "PAUSE"))
2677 rtsp_cmd_pause(c, url, header);
2678 else if (!strcmp(cmd, "TEARDOWN"))
2679 rtsp_cmd_teardown(c, url, header);
2680 else
2681 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2683 the_end:
2684 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2685 c->pb = NULL; /* safety */
2686 if (len < 0) {
2687 /* XXX: cannot do more */
2688 return -1;
2690 c->buffer_ptr = c->pb_buffer;
2691 c->buffer_end = c->pb_buffer + len;
2692 c->state = RTSPSTATE_SEND_REPLY;
2693 return 0;
2696 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2697 struct in_addr my_ip)
2699 AVFormatContext *avc;
2700 AVStream avs[MAX_STREAMS];
2701 int i;
2703 avc = av_alloc_format_context();
2704 if (avc == NULL) {
2705 return -1;
2707 if (stream->title[0] != 0) {
2708 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2709 } else {
2710 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2712 avc->nb_streams = stream->nb_streams;
2713 if (stream->is_multicast) {
2714 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2715 inet_ntoa(stream->multicast_ip),
2716 stream->multicast_port, stream->multicast_ttl);
2719 for(i = 0; i < stream->nb_streams; i++) {
2720 avc->streams[i] = &avs[i];
2721 avc->streams[i]->codec = stream->streams[i]->codec;
2723 *pbuffer = av_mallocz(2048);
2724 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2725 av_free(avc);
2727 return strlen(*pbuffer);
2730 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2732 // rtsp_reply_header(c, RTSP_STATUS_OK);
2733 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2734 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2735 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2736 url_fprintf(c->pb, "\r\n");
2739 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2741 FFStream *stream;
2742 char path1[1024];
2743 const char *path;
2744 uint8_t *content;
2745 int content_length, len;
2746 struct sockaddr_in my_addr;
2748 /* find which url is asked */
2749 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2750 path = path1;
2751 if (*path == '/')
2752 path++;
2754 for(stream = first_stream; stream != NULL; stream = stream->next) {
2755 if (!stream->is_feed &&
2756 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2757 !strcmp(path, stream->filename)) {
2758 goto found;
2761 /* no stream found */
2762 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2763 return;
2765 found:
2766 /* prepare the media description in sdp format */
2768 /* get the host IP */
2769 len = sizeof(my_addr);
2770 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2771 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2772 if (content_length < 0) {
2773 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2774 return;
2776 rtsp_reply_header(c, RTSP_STATUS_OK);
2777 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2778 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2779 url_fprintf(c->pb, "\r\n");
2780 put_buffer(c->pb, content, content_length);
2783 static HTTPContext *find_rtp_session(const char *session_id)
2785 HTTPContext *c;
2787 if (session_id[0] == '\0')
2788 return NULL;
2790 for(c = first_http_ctx; c != NULL; c = c->next) {
2791 if (!strcmp(c->session_id, session_id))
2792 return c;
2794 return NULL;
2797 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2799 RTSPTransportField *th;
2800 int i;
2802 for(i=0;i<h->nb_transports;i++) {
2803 th = &h->transports[i];
2804 if (th->protocol == protocol)
2805 return th;
2807 return NULL;
2810 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2811 RTSPHeader *h)
2813 FFStream *stream;
2814 int stream_index, port;
2815 char buf[1024];
2816 char path1[1024];
2817 const char *path;
2818 HTTPContext *rtp_c;
2819 RTSPTransportField *th;
2820 struct sockaddr_in dest_addr;
2821 RTSPActionServerSetup setup;
2823 /* find which url is asked */
2824 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2825 path = path1;
2826 if (*path == '/')
2827 path++;
2829 /* now check each stream */
2830 for(stream = first_stream; stream != NULL; stream = stream->next) {
2831 if (!stream->is_feed &&
2832 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2833 /* accept aggregate filenames only if single stream */
2834 if (!strcmp(path, stream->filename)) {
2835 if (stream->nb_streams != 1) {
2836 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2837 return;
2839 stream_index = 0;
2840 goto found;
2843 for(stream_index = 0; stream_index < stream->nb_streams;
2844 stream_index++) {
2845 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2846 stream->filename, stream_index);
2847 if (!strcmp(path, buf))
2848 goto found;
2852 /* no stream found */
2853 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2854 return;
2855 found:
2857 /* generate session id if needed */
2858 if (h->session_id[0] == '\0')
2859 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2860 av_random(&random_state), av_random(&random_state));
2862 /* find rtp session, and create it if none found */
2863 rtp_c = find_rtp_session(h->session_id);
2864 if (!rtp_c) {
2865 /* always prefer UDP */
2866 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2867 if (!th) {
2868 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2869 if (!th) {
2870 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2871 return;
2875 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2876 th->protocol);
2877 if (!rtp_c) {
2878 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2879 return;
2882 /* open input stream */
2883 if (open_input_stream(rtp_c, "") < 0) {
2884 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2885 return;
2889 /* test if stream is OK (test needed because several SETUP needs
2890 to be done for a given file) */
2891 if (rtp_c->stream != stream) {
2892 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2893 return;
2896 /* test if stream is already set up */
2897 if (rtp_c->rtp_ctx[stream_index]) {
2898 rtsp_reply_error(c, RTSP_STATUS_STATE);
2899 return;
2902 /* check transport */
2903 th = find_transport(h, rtp_c->rtp_protocol);
2904 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2905 th->client_port_min <= 0)) {
2906 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2907 return;
2910 /* setup default options */
2911 setup.transport_option[0] = '\0';
2912 dest_addr = rtp_c->from_addr;
2913 dest_addr.sin_port = htons(th->client_port_min);
2915 /* setup stream */
2916 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2917 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2918 return;
2921 /* now everything is OK, so we can send the connection parameters */
2922 rtsp_reply_header(c, RTSP_STATUS_OK);
2923 /* session ID */
2924 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2926 switch(rtp_c->rtp_protocol) {
2927 case RTSP_PROTOCOL_RTP_UDP:
2928 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2929 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2930 "client_port=%d-%d;server_port=%d-%d",
2931 th->client_port_min, th->client_port_min + 1,
2932 port, port + 1);
2933 break;
2934 case RTSP_PROTOCOL_RTP_TCP:
2935 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2936 stream_index * 2, stream_index * 2 + 1);
2937 break;
2938 default:
2939 break;
2941 if (setup.transport_option[0] != '\0')
2942 url_fprintf(c->pb, ";%s", setup.transport_option);
2943 url_fprintf(c->pb, "\r\n");
2946 url_fprintf(c->pb, "\r\n");
2950 /* find an rtp connection by using the session ID. Check consistency
2951 with filename */
2952 static HTTPContext *find_rtp_session_with_url(const char *url,
2953 const char *session_id)
2955 HTTPContext *rtp_c;
2956 char path1[1024];
2957 const char *path;
2958 char buf[1024];
2959 int s;
2961 rtp_c = find_rtp_session(session_id);
2962 if (!rtp_c)
2963 return NULL;
2965 /* find which url is asked */
2966 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2967 path = path1;
2968 if (*path == '/')
2969 path++;
2970 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2971 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2972 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2973 rtp_c->stream->filename, s);
2974 if(!strncmp(path, buf, sizeof(buf))) {
2975 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2976 return rtp_c;
2979 return NULL;
2982 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2984 HTTPContext *rtp_c;
2986 rtp_c = find_rtp_session_with_url(url, h->session_id);
2987 if (!rtp_c) {
2988 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2989 return;
2992 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2993 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2994 rtp_c->state != HTTPSTATE_READY) {
2995 rtsp_reply_error(c, RTSP_STATUS_STATE);
2996 return;
2999 #if 0
3000 /* XXX: seek in stream */
3001 if (h->range_start != AV_NOPTS_VALUE) {
3002 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3003 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3005 #endif
3007 rtp_c->state = HTTPSTATE_SEND_DATA;
3009 /* now everything is OK, so we can send the connection parameters */
3010 rtsp_reply_header(c, RTSP_STATUS_OK);
3011 /* session ID */
3012 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3013 url_fprintf(c->pb, "\r\n");
3016 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3018 HTTPContext *rtp_c;
3020 rtp_c = find_rtp_session_with_url(url, h->session_id);
3021 if (!rtp_c) {
3022 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3023 return;
3026 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3027 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3028 rtsp_reply_error(c, RTSP_STATUS_STATE);
3029 return;
3032 rtp_c->state = HTTPSTATE_READY;
3033 rtp_c->first_pts = AV_NOPTS_VALUE;
3034 /* now everything is OK, so we can send the connection parameters */
3035 rtsp_reply_header(c, RTSP_STATUS_OK);
3036 /* session ID */
3037 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3038 url_fprintf(c->pb, "\r\n");
3041 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3043 HTTPContext *rtp_c;
3044 char session_id[32];
3046 rtp_c = find_rtp_session_with_url(url, h->session_id);
3047 if (!rtp_c) {
3048 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3049 return;
3052 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3054 /* abort the session */
3055 close_connection(rtp_c);
3057 /* now everything is OK, so we can send the connection parameters */
3058 rtsp_reply_header(c, RTSP_STATUS_OK);
3059 /* session ID */
3060 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3061 url_fprintf(c->pb, "\r\n");
3065 /********************************************************************/
3066 /* RTP handling */
3068 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3069 FFStream *stream, const char *session_id,
3070 enum RTSPProtocol rtp_protocol)
3072 HTTPContext *c = NULL;
3073 const char *proto_str;
3075 /* XXX: should output a warning page when coming
3076 close to the connection limit */
3077 if (nb_connections >= nb_max_connections)
3078 goto fail;
3080 /* add a new connection */
3081 c = av_mallocz(sizeof(HTTPContext));
3082 if (!c)
3083 goto fail;
3085 c->fd = -1;
3086 c->poll_entry = NULL;
3087 c->from_addr = *from_addr;
3088 c->buffer_size = IOBUFFER_INIT_SIZE;
3089 c->buffer = av_malloc(c->buffer_size);
3090 if (!c->buffer)
3091 goto fail;
3092 nb_connections++;
3093 c->stream = stream;
3094 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3095 c->state = HTTPSTATE_READY;
3096 c->is_packetized = 1;
3097 c->rtp_protocol = rtp_protocol;
3099 /* protocol is shown in statistics */
3100 switch(c->rtp_protocol) {
3101 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3102 proto_str = "MCAST";
3103 break;
3104 case RTSP_PROTOCOL_RTP_UDP:
3105 proto_str = "UDP";
3106 break;
3107 case RTSP_PROTOCOL_RTP_TCP:
3108 proto_str = "TCP";
3109 break;
3110 default:
3111 proto_str = "???";
3112 break;
3114 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3115 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3117 current_bandwidth += stream->bandwidth;
3119 c->next = first_http_ctx;
3120 first_http_ctx = c;
3121 return c;
3123 fail:
3124 if (c) {
3125 av_free(c->buffer);
3126 av_free(c);
3128 return NULL;
3131 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3132 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3133 used. */
3134 static int rtp_new_av_stream(HTTPContext *c,
3135 int stream_index, struct sockaddr_in *dest_addr,
3136 HTTPContext *rtsp_c)
3138 AVFormatContext *ctx;
3139 AVStream *st;
3140 char *ipaddr;
3141 URLContext *h = NULL;
3142 uint8_t *dummy_buf;
3143 int max_packet_size;
3145 /* now we can open the relevant output stream */
3146 ctx = av_alloc_format_context();
3147 if (!ctx)
3148 return -1;
3149 ctx->oformat = guess_format("rtp", NULL, NULL);
3151 st = av_mallocz(sizeof(AVStream));
3152 if (!st)
3153 goto fail;
3154 st->codec= avcodec_alloc_context();
3155 ctx->nb_streams = 1;
3156 ctx->streams[0] = st;
3158 if (!c->stream->feed ||
3159 c->stream->feed == c->stream)
3160 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3161 else
3162 memcpy(st,
3163 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3164 sizeof(AVStream));
3165 st->priv_data = NULL;
3167 /* build destination RTP address */
3168 ipaddr = inet_ntoa(dest_addr->sin_addr);
3170 switch(c->rtp_protocol) {
3171 case RTSP_PROTOCOL_RTP_UDP:
3172 case RTSP_PROTOCOL_RTP_UDP_MULTICAST:
3173 /* RTP/UDP case */
3175 /* XXX: also pass as parameter to function ? */
3176 if (c->stream->is_multicast) {
3177 int ttl;
3178 ttl = c->stream->multicast_ttl;
3179 if (!ttl)
3180 ttl = 16;
3181 snprintf(ctx->filename, sizeof(ctx->filename),
3182 "rtp://%s:%d?multicast=1&ttl=%d",
3183 ipaddr, ntohs(dest_addr->sin_port), ttl);
3184 } else {
3185 snprintf(ctx->filename, sizeof(ctx->filename),
3186 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3189 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3190 goto fail;
3191 c->rtp_handles[stream_index] = h;
3192 max_packet_size = url_get_max_packet_size(h);
3193 break;
3194 case RTSP_PROTOCOL_RTP_TCP:
3195 /* RTP/TCP case */
3196 c->rtsp_c = rtsp_c;
3197 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3198 break;
3199 default:
3200 goto fail;
3203 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3204 ipaddr, ntohs(dest_addr->sin_port),
3205 c->stream->filename, stream_index, c->protocol);
3207 /* normally, no packets should be output here, but the packet size may be checked */
3208 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3209 /* XXX: close stream */
3210 goto fail;
3212 av_set_parameters(ctx, NULL);
3213 if (av_write_header(ctx) < 0) {
3214 fail:
3215 if (h)
3216 url_close(h);
3217 av_free(ctx);
3218 return -1;
3220 url_close_dyn_buf(ctx->pb, &dummy_buf);
3221 av_free(dummy_buf);
3223 c->rtp_ctx[stream_index] = ctx;
3224 return 0;
3227 /********************************************************************/
3228 /* ffserver initialization */
3230 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3232 AVStream *fst;
3234 fst = av_mallocz(sizeof(AVStream));
3235 if (!fst)
3236 return NULL;
3237 fst->codec= avcodec_alloc_context();
3238 fst->priv_data = av_mallocz(sizeof(FeedData));
3239 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3240 fst->index = stream->nb_streams;
3241 av_set_pts_info(fst, 33, 1, 90000);
3242 stream->streams[stream->nb_streams++] = fst;
3243 return fst;
3246 /* return the stream number in the feed */
3247 static int add_av_stream(FFStream *feed, AVStream *st)
3249 AVStream *fst;
3250 AVCodecContext *av, *av1;
3251 int i;
3253 av = st->codec;
3254 for(i=0;i<feed->nb_streams;i++) {
3255 st = feed->streams[i];
3256 av1 = st->codec;
3257 if (av1->codec_id == av->codec_id &&
3258 av1->codec_type == av->codec_type &&
3259 av1->bit_rate == av->bit_rate) {
3261 switch(av->codec_type) {
3262 case CODEC_TYPE_AUDIO:
3263 if (av1->channels == av->channels &&
3264 av1->sample_rate == av->sample_rate)
3265 goto found;
3266 break;
3267 case CODEC_TYPE_VIDEO:
3268 if (av1->width == av->width &&
3269 av1->height == av->height &&
3270 av1->time_base.den == av->time_base.den &&
3271 av1->time_base.num == av->time_base.num &&
3272 av1->gop_size == av->gop_size)
3273 goto found;
3274 break;
3275 default:
3276 abort();
3281 fst = add_av_stream1(feed, av);
3282 if (!fst)
3283 return -1;
3284 return feed->nb_streams - 1;
3285 found:
3286 return i;
3289 static void remove_stream(FFStream *stream)
3291 FFStream **ps;
3292 ps = &first_stream;
3293 while (*ps != NULL) {
3294 if (*ps == stream)
3295 *ps = (*ps)->next;
3296 else
3297 ps = &(*ps)->next;
3301 /* specific mpeg4 handling : we extract the raw parameters */
3302 static void extract_mpeg4_header(AVFormatContext *infile)
3304 int mpeg4_count, i, size;
3305 AVPacket pkt;
3306 AVStream *st;
3307 const uint8_t *p;
3309 mpeg4_count = 0;
3310 for(i=0;i<infile->nb_streams;i++) {
3311 st = infile->streams[i];
3312 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3313 st->codec->extradata_size == 0) {
3314 mpeg4_count++;
3317 if (!mpeg4_count)
3318 return;
3320 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3321 while (mpeg4_count > 0) {
3322 if (av_read_packet(infile, &pkt) < 0)
3323 break;
3324 st = infile->streams[pkt.stream_index];
3325 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3326 st->codec->extradata_size == 0) {
3327 av_freep(&st->codec->extradata);
3328 /* fill extradata with the header */
3329 /* XXX: we make hard suppositions here ! */
3330 p = pkt.data;
3331 while (p < pkt.data + pkt.size - 4) {
3332 /* stop when vop header is found */
3333 if (p[0] == 0x00 && p[1] == 0x00 &&
3334 p[2] == 0x01 && p[3] == 0xb6) {
3335 size = p - pkt.data;
3336 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3337 st->codec->extradata = av_malloc(size);
3338 st->codec->extradata_size = size;
3339 memcpy(st->codec->extradata, pkt.data, size);
3340 break;
3342 p++;
3344 mpeg4_count--;
3346 av_free_packet(&pkt);
3350 /* compute the needed AVStream for each file */
3351 static void build_file_streams(void)
3353 FFStream *stream, *stream_next;
3354 AVFormatContext *infile;
3355 int i, ret;
3357 /* gather all streams */
3358 for(stream = first_stream; stream != NULL; stream = stream_next) {
3359 stream_next = stream->next;
3360 if (stream->stream_type == STREAM_TYPE_LIVE &&
3361 !stream->feed) {
3362 /* the stream comes from a file */
3363 /* try to open the file */
3364 /* open stream */
3365 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3366 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3367 /* specific case : if transport stream output to RTP,
3368 we use a raw transport stream reader */
3369 stream->ap_in->mpeg2ts_raw = 1;
3370 stream->ap_in->mpeg2ts_compute_pcr = 1;
3373 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3374 stream->ifmt, 0, stream->ap_in)) < 0) {
3375 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3376 /* remove stream (no need to spend more time on it) */
3377 fail:
3378 remove_stream(stream);
3379 } else {
3380 /* find all the AVStreams inside and reference them in
3381 'stream' */
3382 if (av_find_stream_info(infile) < 0) {
3383 http_log("Could not find codec parameters from '%s'\n",
3384 stream->feed_filename);
3385 av_close_input_file(infile);
3386 goto fail;
3388 extract_mpeg4_header(infile);
3390 for(i=0;i<infile->nb_streams;i++)
3391 add_av_stream1(stream, infile->streams[i]->codec);
3393 av_close_input_file(infile);
3399 /* compute the needed AVStream for each feed */
3400 static void build_feed_streams(void)
3402 FFStream *stream, *feed;
3403 int i;
3405 /* gather all streams */
3406 for(stream = first_stream; stream != NULL; stream = stream->next) {
3407 feed = stream->feed;
3408 if (feed) {
3409 if (!stream->is_feed) {
3410 /* we handle a stream coming from a feed */
3411 for(i=0;i<stream->nb_streams;i++)
3412 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3417 /* gather all streams */
3418 for(stream = first_stream; stream != NULL; stream = stream->next) {
3419 feed = stream->feed;
3420 if (feed) {
3421 if (stream->is_feed) {
3422 for(i=0;i<stream->nb_streams;i++)
3423 stream->feed_streams[i] = i;
3428 /* create feed files if needed */
3429 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3430 int fd;
3432 if (url_exist(feed->feed_filename)) {
3433 /* See if it matches */
3434 AVFormatContext *s;
3435 int matches = 0;
3437 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3438 /* Now see if it matches */
3439 if (s->nb_streams == feed->nb_streams) {
3440 matches = 1;
3441 for(i=0;i<s->nb_streams;i++) {
3442 AVStream *sf, *ss;
3443 sf = feed->streams[i];
3444 ss = s->streams[i];
3446 if (sf->index != ss->index ||
3447 sf->id != ss->id) {
3448 http_log("Index & Id do not match for stream %d (%s)\n",
3449 i, feed->feed_filename);
3450 matches = 0;
3451 } else {
3452 AVCodecContext *ccf, *ccs;
3454 ccf = sf->codec;
3455 ccs = ss->codec;
3456 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3458 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3459 http_log("Codecs do not match for stream %d\n", i);
3460 matches = 0;
3461 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3462 http_log("Codec bitrates do not match for stream %d\n", i);
3463 matches = 0;
3464 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3465 if (CHECK_CODEC(time_base.den) ||
3466 CHECK_CODEC(time_base.num) ||
3467 CHECK_CODEC(width) ||
3468 CHECK_CODEC(height)) {
3469 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3470 matches = 0;
3472 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3473 if (CHECK_CODEC(sample_rate) ||
3474 CHECK_CODEC(channels) ||
3475 CHECK_CODEC(frame_size)) {
3476 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3477 matches = 0;
3479 } else {
3480 http_log("Unknown codec type\n");
3481 matches = 0;
3484 if (!matches)
3485 break;
3487 } else
3488 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3489 feed->feed_filename, s->nb_streams, feed->nb_streams);
3491 av_close_input_file(s);
3492 } else
3493 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3494 feed->feed_filename);
3496 if (!matches) {
3497 if (feed->readonly) {
3498 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3499 feed->feed_filename);
3500 exit(1);
3502 unlink(feed->feed_filename);
3505 if (!url_exist(feed->feed_filename)) {
3506 AVFormatContext s1, *s = &s1;
3508 if (feed->readonly) {
3509 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3510 feed->feed_filename);
3511 exit(1);
3514 /* only write the header of the ffm file */
3515 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3516 http_log("Could not open output feed file '%s'\n",
3517 feed->feed_filename);
3518 exit(1);
3520 s->oformat = feed->fmt;
3521 s->nb_streams = feed->nb_streams;
3522 for(i=0;i<s->nb_streams;i++) {
3523 AVStream *st;
3524 st = feed->streams[i];
3525 s->streams[i] = st;
3527 av_set_parameters(s, NULL);
3528 if (av_write_header(s) < 0) {
3529 http_log("Container doesn't supports the required parameters\n");
3530 exit(1);
3532 /* XXX: need better api */
3533 av_freep(&s->priv_data);
3534 url_fclose(s->pb);
3536 /* get feed size and write index */
3537 fd = open(feed->feed_filename, O_RDONLY);
3538 if (fd < 0) {
3539 http_log("Could not open output feed file '%s'\n",
3540 feed->feed_filename);
3541 exit(1);
3544 feed->feed_write_index = ffm_read_write_index(fd);
3545 feed->feed_size = lseek(fd, 0, SEEK_END);
3546 /* ensure that we do not wrap before the end of file */
3547 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3548 feed->feed_max_size = feed->feed_size;
3550 close(fd);
3554 /* compute the bandwidth used by each stream */
3555 static void compute_bandwidth(void)
3557 unsigned bandwidth;
3558 int i;
3559 FFStream *stream;
3561 for(stream = first_stream; stream != NULL; stream = stream->next) {
3562 bandwidth = 0;
3563 for(i=0;i<stream->nb_streams;i++) {
3564 AVStream *st = stream->streams[i];
3565 switch(st->codec->codec_type) {
3566 case CODEC_TYPE_AUDIO:
3567 case CODEC_TYPE_VIDEO:
3568 bandwidth += st->codec->bit_rate;
3569 break;
3570 default:
3571 break;
3574 stream->bandwidth = (bandwidth + 999) / 1000;
3578 static void get_arg(char *buf, int buf_size, const char **pp)
3580 const char *p;
3581 char *q;
3582 int quote;
3584 p = *pp;
3585 while (isspace(*p)) p++;
3586 q = buf;
3587 quote = 0;
3588 if (*p == '\"' || *p == '\'')
3589 quote = *p++;
3590 for(;;) {
3591 if (quote) {
3592 if (*p == quote)
3593 break;
3594 } else {
3595 if (isspace(*p))
3596 break;
3598 if (*p == '\0')
3599 break;
3600 if ((q - buf) < buf_size - 1)
3601 *q++ = *p;
3602 p++;
3604 *q = '\0';
3605 if (quote && *p == quote)
3606 p++;
3607 *pp = p;
3610 /* add a codec and set the default parameters */
3611 static void add_codec(FFStream *stream, AVCodecContext *av)
3613 AVStream *st;
3615 /* compute default parameters */
3616 switch(av->codec_type) {
3617 case CODEC_TYPE_AUDIO:
3618 if (av->bit_rate == 0)
3619 av->bit_rate = 64000;
3620 if (av->sample_rate == 0)
3621 av->sample_rate = 22050;
3622 if (av->channels == 0)
3623 av->channels = 1;
3624 break;
3625 case CODEC_TYPE_VIDEO:
3626 if (av->bit_rate == 0)
3627 av->bit_rate = 64000;
3628 if (av->time_base.num == 0){
3629 av->time_base.den = 5;
3630 av->time_base.num = 1;
3632 if (av->width == 0 || av->height == 0) {
3633 av->width = 160;
3634 av->height = 128;
3636 /* Bitrate tolerance is less for streaming */
3637 if (av->bit_rate_tolerance == 0)
3638 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3639 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3640 if (av->qmin == 0)
3641 av->qmin = 3;
3642 if (av->qmax == 0)
3643 av->qmax = 31;
3644 if (av->max_qdiff == 0)
3645 av->max_qdiff = 3;
3646 av->qcompress = 0.5;
3647 av->qblur = 0.5;
3649 if (!av->nsse_weight)
3650 av->nsse_weight = 8;
3652 av->frame_skip_cmp = FF_CMP_DCTMAX;
3653 av->me_method = ME_EPZS;
3654 av->rc_buffer_aggressivity = 1.0;
3656 if (!av->rc_eq)
3657 av->rc_eq = "tex^qComp";
3658 if (!av->i_quant_factor)
3659 av->i_quant_factor = -0.8;
3660 if (!av->b_quant_factor)
3661 av->b_quant_factor = 1.25;
3662 if (!av->b_quant_offset)
3663 av->b_quant_offset = 1.25;
3664 if (!av->rc_max_rate)
3665 av->rc_max_rate = av->bit_rate * 2;
3667 if (av->rc_max_rate && !av->rc_buffer_size) {
3668 av->rc_buffer_size = av->rc_max_rate;
3672 break;
3673 default:
3674 abort();
3677 st = av_mallocz(sizeof(AVStream));
3678 if (!st)
3679 return;
3680 st->codec = avcodec_alloc_context();
3681 stream->streams[stream->nb_streams++] = st;
3682 memcpy(st->codec, av, sizeof(AVCodecContext));
3685 static int opt_audio_codec(const char *arg)
3687 AVCodec *p= avcodec_find_encoder_by_name(arg);
3689 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3690 return CODEC_ID_NONE;
3692 return p->id;
3695 static int opt_video_codec(const char *arg)
3697 AVCodec *p= avcodec_find_encoder_by_name(arg);
3699 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3700 return CODEC_ID_NONE;
3702 return p->id;
3705 /* simplistic plugin support */
3707 #ifdef HAVE_DLOPEN
3708 static void load_module(const char *filename)
3710 void *dll;
3711 void (*init_func)(void);
3712 dll = dlopen(filename, RTLD_NOW);
3713 if (!dll) {
3714 fprintf(stderr, "Could not load module '%s' - %s\n",
3715 filename, dlerror());
3716 return;
3719 init_func = dlsym(dll, "ffserver_module_init");
3720 if (!init_func) {
3721 fprintf(stderr,
3722 "%s: init function 'ffserver_module_init()' not found\n",
3723 filename);
3724 dlclose(dll);
3727 init_func();
3729 #endif
3731 static int opt_default(const char *opt, const char *arg,
3732 AVCodecContext *avctx, int type)
3734 const AVOption *o = NULL;
3735 const AVOption *o2 = av_find_opt(avctx, opt, NULL, type, type);
3736 if(o2)
3737 o = av_set_string2(avctx, opt, arg, 1);
3738 if(!o)
3739 return -1;
3740 return 0;
3743 static int parse_ffconfig(const char *filename)
3745 FILE *f;
3746 char line[1024];
3747 char cmd[64];
3748 char arg[1024];
3749 const char *p;
3750 int val, errors, line_num;
3751 FFStream **last_stream, *stream, *redirect;
3752 FFStream **last_feed, *feed;
3753 AVCodecContext audio_enc, video_enc;
3754 int audio_id, video_id;
3756 f = fopen(filename, "r");
3757 if (!f) {
3758 perror(filename);
3759 return -1;
3762 errors = 0;
3763 line_num = 0;
3764 first_stream = NULL;
3765 last_stream = &first_stream;
3766 first_feed = NULL;
3767 last_feed = &first_feed;
3768 stream = NULL;
3769 feed = NULL;
3770 redirect = NULL;
3771 audio_id = CODEC_ID_NONE;
3772 video_id = CODEC_ID_NONE;
3773 for(;;) {
3774 if (fgets(line, sizeof(line), f) == NULL)
3775 break;
3776 line_num++;
3777 p = line;
3778 while (isspace(*p))
3779 p++;
3780 if (*p == '\0' || *p == '#')
3781 continue;
3783 get_arg(cmd, sizeof(cmd), &p);
3785 if (!strcasecmp(cmd, "Port")) {
3786 get_arg(arg, sizeof(arg), &p);
3787 val = atoi(arg);
3788 if (val < 1 || val > 65536) {
3789 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3790 filename, line_num, arg);
3791 errors++;
3793 my_http_addr.sin_port = htons(val);
3794 } else if (!strcasecmp(cmd, "BindAddress")) {
3795 get_arg(arg, sizeof(arg), &p);
3796 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3797 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3798 filename, line_num, arg);
3799 errors++;
3801 } else if (!strcasecmp(cmd, "NoDaemon")) {
3802 ffserver_daemon = 0;
3803 } else if (!strcasecmp(cmd, "RTSPPort")) {
3804 get_arg(arg, sizeof(arg), &p);
3805 val = atoi(arg);
3806 if (val < 1 || val > 65536) {
3807 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3808 filename, line_num, arg);
3809 errors++;
3811 my_rtsp_addr.sin_port = htons(atoi(arg));
3812 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3813 get_arg(arg, sizeof(arg), &p);
3814 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3815 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3816 filename, line_num, arg);
3817 errors++;
3819 } else if (!strcasecmp(cmd, "MaxClients")) {
3820 get_arg(arg, sizeof(arg), &p);
3821 val = atoi(arg);
3822 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3823 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3824 filename, line_num, arg);
3825 errors++;
3826 } else {
3827 nb_max_connections = val;
3829 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3830 int64_t llval;
3831 get_arg(arg, sizeof(arg), &p);
3832 llval = atoll(arg);
3833 if (llval < 10 || llval > 10000000) {
3834 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3835 filename, line_num, arg);
3836 errors++;
3837 } else
3838 max_bandwidth = llval;
3839 } else if (!strcasecmp(cmd, "CustomLog")) {
3840 if (!ffserver_debug)
3841 get_arg(logfilename, sizeof(logfilename), &p);
3842 } else if (!strcasecmp(cmd, "<Feed")) {
3843 /*********************************************/
3844 /* Feed related options */
3845 char *q;
3846 if (stream || feed) {
3847 fprintf(stderr, "%s:%d: Already in a tag\n",
3848 filename, line_num);
3849 } else {
3850 feed = av_mallocz(sizeof(FFStream));
3851 /* add in stream list */
3852 *last_stream = feed;
3853 last_stream = &feed->next;
3854 /* add in feed list */
3855 *last_feed = feed;
3856 last_feed = &feed->next_feed;
3858 get_arg(feed->filename, sizeof(feed->filename), &p);
3859 q = strrchr(feed->filename, '>');
3860 if (*q)
3861 *q = '\0';
3862 feed->fmt = guess_format("ffm", NULL, NULL);
3863 /* defaut feed file */
3864 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3865 "/tmp/%s.ffm", feed->filename);
3866 feed->feed_max_size = 5 * 1024 * 1024;
3867 feed->is_feed = 1;
3868 feed->feed = feed; /* self feeding :-) */
3870 } else if (!strcasecmp(cmd, "Launch")) {
3871 if (feed) {
3872 int i;
3874 feed->child_argv = av_mallocz(64 * sizeof(char *));
3876 for (i = 0; i < 62; i++) {
3877 get_arg(arg, sizeof(arg), &p);
3878 if (!arg[0])
3879 break;
3881 feed->child_argv[i] = av_strdup(arg);
3884 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3886 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3887 "http://%s:%d/%s",
3888 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3889 inet_ntoa(my_http_addr.sin_addr),
3890 ntohs(my_http_addr.sin_port), feed->filename);
3892 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3893 if (feed) {
3894 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3895 feed->readonly = 1;
3896 } else if (stream) {
3897 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3899 } else if (!strcasecmp(cmd, "File")) {
3900 if (feed) {
3901 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3902 } else if (stream)
3903 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3904 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3905 if (feed) {
3906 char *p1;
3907 double fsize;
3909 get_arg(arg, sizeof(arg), &p);
3910 p1 = arg;
3911 fsize = strtod(p1, &p1);
3912 switch(toupper(*p1)) {
3913 case 'K':
3914 fsize *= 1024;
3915 break;
3916 case 'M':
3917 fsize *= 1024 * 1024;
3918 break;
3919 case 'G':
3920 fsize *= 1024 * 1024 * 1024;
3921 break;
3923 feed->feed_max_size = (int64_t)fsize;
3925 } else if (!strcasecmp(cmd, "</Feed>")) {
3926 if (!feed) {
3927 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3928 filename, line_num);
3929 errors++;
3931 feed = NULL;
3932 } else if (!strcasecmp(cmd, "<Stream")) {
3933 /*********************************************/
3934 /* Stream related options */
3935 char *q;
3936 if (stream || feed) {
3937 fprintf(stderr, "%s:%d: Already in a tag\n",
3938 filename, line_num);
3939 } else {
3940 const AVClass *class;
3941 stream = av_mallocz(sizeof(FFStream));
3942 *last_stream = stream;
3943 last_stream = &stream->next;
3945 get_arg(stream->filename, sizeof(stream->filename), &p);
3946 q = strrchr(stream->filename, '>');
3947 if (*q)
3948 *q = '\0';
3949 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3950 /* fetch avclass so AVOption works
3951 * FIXME try to use avcodec_get_context_defaults2
3952 * without changing defaults too much */
3953 avcodec_get_context_defaults(&video_enc);
3954 class = video_enc.av_class;
3955 memset(&audio_enc, 0, sizeof(AVCodecContext));
3956 memset(&video_enc, 0, sizeof(AVCodecContext));
3957 audio_enc.av_class = class;
3958 video_enc.av_class = class;
3959 audio_id = CODEC_ID_NONE;
3960 video_id = CODEC_ID_NONE;
3961 if (stream->fmt) {
3962 audio_id = stream->fmt->audio_codec;
3963 video_id = stream->fmt->video_codec;
3966 } else if (!strcasecmp(cmd, "Feed")) {
3967 get_arg(arg, sizeof(arg), &p);
3968 if (stream) {
3969 FFStream *sfeed;
3971 sfeed = first_feed;
3972 while (sfeed != NULL) {
3973 if (!strcmp(sfeed->filename, arg))
3974 break;
3975 sfeed = sfeed->next_feed;
3977 if (!sfeed)
3978 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3979 filename, line_num, arg);
3980 else
3981 stream->feed = sfeed;
3983 } else if (!strcasecmp(cmd, "Format")) {
3984 get_arg(arg, sizeof(arg), &p);
3985 if (stream) {
3986 if (!strcmp(arg, "status")) {
3987 stream->stream_type = STREAM_TYPE_STATUS;
3988 stream->fmt = NULL;
3989 } else {
3990 stream->stream_type = STREAM_TYPE_LIVE;
3991 /* jpeg cannot be used here, so use single frame jpeg */
3992 if (!strcmp(arg, "jpeg"))
3993 strcpy(arg, "mjpeg");
3994 stream->fmt = guess_stream_format(arg, NULL, NULL);
3995 if (!stream->fmt) {
3996 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3997 filename, line_num, arg);
3998 errors++;
4001 if (stream->fmt) {
4002 audio_id = stream->fmt->audio_codec;
4003 video_id = stream->fmt->video_codec;
4006 } else if (!strcasecmp(cmd, "InputFormat")) {
4007 get_arg(arg, sizeof(arg), &p);
4008 stream->ifmt = av_find_input_format(arg);
4009 if (!stream->ifmt) {
4010 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4011 filename, line_num, arg);
4013 } else if (!strcasecmp(cmd, "FaviconURL")) {
4014 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4015 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4016 } else {
4017 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4018 filename, line_num);
4019 errors++;
4021 } else if (!strcasecmp(cmd, "Author")) {
4022 if (stream)
4023 get_arg(stream->author, sizeof(stream->author), &p);
4024 } else if (!strcasecmp(cmd, "Comment")) {
4025 if (stream)
4026 get_arg(stream->comment, sizeof(stream->comment), &p);
4027 } else if (!strcasecmp(cmd, "Copyright")) {
4028 if (stream)
4029 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4030 } else if (!strcasecmp(cmd, "Title")) {
4031 if (stream)
4032 get_arg(stream->title, sizeof(stream->title), &p);
4033 } else if (!strcasecmp(cmd, "Preroll")) {
4034 get_arg(arg, sizeof(arg), &p);
4035 if (stream)
4036 stream->prebuffer = atof(arg) * 1000;
4037 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4038 if (stream)
4039 stream->send_on_key = 1;
4040 } else if (!strcasecmp(cmd, "AudioCodec")) {
4041 get_arg(arg, sizeof(arg), &p);
4042 audio_id = opt_audio_codec(arg);
4043 if (audio_id == CODEC_ID_NONE) {
4044 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4045 filename, line_num, arg);
4046 errors++;
4048 } else if (!strcasecmp(cmd, "VideoCodec")) {
4049 get_arg(arg, sizeof(arg), &p);
4050 video_id = opt_video_codec(arg);
4051 if (video_id == CODEC_ID_NONE) {
4052 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4053 filename, line_num, arg);
4054 errors++;
4056 } else if (!strcasecmp(cmd, "MaxTime")) {
4057 get_arg(arg, sizeof(arg), &p);
4058 if (stream)
4059 stream->max_time = atof(arg) * 1000;
4060 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4061 get_arg(arg, sizeof(arg), &p);
4062 if (stream)
4063 audio_enc.bit_rate = atoi(arg) * 1000;
4064 } else if (!strcasecmp(cmd, "AudioChannels")) {
4065 get_arg(arg, sizeof(arg), &p);
4066 if (stream)
4067 audio_enc.channels = atoi(arg);
4068 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4069 get_arg(arg, sizeof(arg), &p);
4070 if (stream)
4071 audio_enc.sample_rate = atoi(arg);
4072 } else if (!strcasecmp(cmd, "AudioQuality")) {
4073 get_arg(arg, sizeof(arg), &p);
4074 if (stream) {
4075 // audio_enc.quality = atof(arg) * 1000;
4077 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4078 if (stream) {
4079 int minrate, maxrate;
4081 get_arg(arg, sizeof(arg), &p);
4083 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4084 video_enc.rc_min_rate = minrate * 1000;
4085 video_enc.rc_max_rate = maxrate * 1000;
4086 } else {
4087 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4088 filename, line_num, arg);
4089 errors++;
4092 } else if (!strcasecmp(cmd, "Debug")) {
4093 if (stream) {
4094 get_arg(arg, sizeof(arg), &p);
4095 video_enc.debug = strtol(arg,0,0);
4097 } else if (!strcasecmp(cmd, "Strict")) {
4098 if (stream) {
4099 get_arg(arg, sizeof(arg), &p);
4100 video_enc.strict_std_compliance = atoi(arg);
4102 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4103 if (stream) {
4104 get_arg(arg, sizeof(arg), &p);
4105 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4107 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4108 if (stream) {
4109 get_arg(arg, sizeof(arg), &p);
4110 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4112 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4113 get_arg(arg, sizeof(arg), &p);
4114 if (stream) {
4115 video_enc.bit_rate = atoi(arg) * 1000;
4117 } else if (!strcasecmp(cmd, "VideoSize")) {
4118 get_arg(arg, sizeof(arg), &p);
4119 if (stream) {
4120 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4121 if ((video_enc.width % 16) != 0 ||
4122 (video_enc.height % 16) != 0) {
4123 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4124 filename, line_num);
4125 errors++;
4128 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4129 get_arg(arg, sizeof(arg), &p);
4130 if (stream) {
4131 AVRational frame_rate;
4132 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4133 fprintf(stderr, "Incorrect frame rate\n");
4134 errors++;
4135 } else {
4136 video_enc.time_base.num = frame_rate.den;
4137 video_enc.time_base.den = frame_rate.num;
4140 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4141 get_arg(arg, sizeof(arg), &p);
4142 if (stream)
4143 video_enc.gop_size = atoi(arg);
4144 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4145 if (stream)
4146 video_enc.gop_size = 1;
4147 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4148 if (stream)
4149 video_enc.mb_decision = FF_MB_DECISION_BITS;
4150 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4151 if (stream) {
4152 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4153 video_enc.flags |= CODEC_FLAG_4MV;
4155 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4156 !strcasecmp(cmd, "AVOptionAudio")) {
4157 char arg2[1024];
4158 AVCodecContext *avctx;
4159 int type;
4160 get_arg(arg, sizeof(arg), &p);
4161 get_arg(arg2, sizeof(arg2), &p);
4162 if (!strcasecmp(cmd, "AVOptionVideo")) {
4163 avctx = &video_enc;
4164 type = AV_OPT_FLAG_VIDEO_PARAM;
4165 } else {
4166 avctx = &audio_enc;
4167 type = AV_OPT_FLAG_AUDIO_PARAM;
4169 if (opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4170 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4171 errors++;
4173 } else if (!strcasecmp(cmd, "VideoTag")) {
4174 get_arg(arg, sizeof(arg), &p);
4175 if ((strlen(arg) == 4) && stream)
4176 video_enc.codec_tag = ff_get_fourcc(arg);
4177 } else if (!strcasecmp(cmd, "BitExact")) {
4178 if (stream)
4179 video_enc.flags |= CODEC_FLAG_BITEXACT;
4180 } else if (!strcasecmp(cmd, "DctFastint")) {
4181 if (stream)
4182 video_enc.dct_algo = FF_DCT_FASTINT;
4183 } else if (!strcasecmp(cmd, "IdctSimple")) {
4184 if (stream)
4185 video_enc.idct_algo = FF_IDCT_SIMPLE;
4186 } else if (!strcasecmp(cmd, "Qscale")) {
4187 get_arg(arg, sizeof(arg), &p);
4188 if (stream) {
4189 video_enc.flags |= CODEC_FLAG_QSCALE;
4190 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4192 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4193 get_arg(arg, sizeof(arg), &p);
4194 if (stream) {
4195 video_enc.max_qdiff = atoi(arg);
4196 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4197 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4198 filename, line_num);
4199 errors++;
4202 } else if (!strcasecmp(cmd, "VideoQMax")) {
4203 get_arg(arg, sizeof(arg), &p);
4204 if (stream) {
4205 video_enc.qmax = atoi(arg);
4206 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4207 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4208 filename, line_num);
4209 errors++;
4212 } else if (!strcasecmp(cmd, "VideoQMin")) {
4213 get_arg(arg, sizeof(arg), &p);
4214 if (stream) {
4215 video_enc.qmin = atoi(arg);
4216 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4217 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4218 filename, line_num);
4219 errors++;
4222 } else if (!strcasecmp(cmd, "LumaElim")) {
4223 get_arg(arg, sizeof(arg), &p);
4224 if (stream)
4225 video_enc.luma_elim_threshold = atoi(arg);
4226 } else if (!strcasecmp(cmd, "ChromaElim")) {
4227 get_arg(arg, sizeof(arg), &p);
4228 if (stream)
4229 video_enc.chroma_elim_threshold = atoi(arg);
4230 } else if (!strcasecmp(cmd, "LumiMask")) {
4231 get_arg(arg, sizeof(arg), &p);
4232 if (stream)
4233 video_enc.lumi_masking = atof(arg);
4234 } else if (!strcasecmp(cmd, "DarkMask")) {
4235 get_arg(arg, sizeof(arg), &p);
4236 if (stream)
4237 video_enc.dark_masking = atof(arg);
4238 } else if (!strcasecmp(cmd, "NoVideo")) {
4239 video_id = CODEC_ID_NONE;
4240 } else if (!strcasecmp(cmd, "NoAudio")) {
4241 audio_id = CODEC_ID_NONE;
4242 } else if (!strcasecmp(cmd, "ACL")) {
4243 IPAddressACL acl;
4245 get_arg(arg, sizeof(arg), &p);
4246 if (strcasecmp(arg, "allow") == 0)
4247 acl.action = IP_ALLOW;
4248 else if (strcasecmp(arg, "deny") == 0)
4249 acl.action = IP_DENY;
4250 else {
4251 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4252 filename, line_num, arg);
4253 errors++;
4256 get_arg(arg, sizeof(arg), &p);
4258 if (resolve_host(&acl.first, arg) != 0) {
4259 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4260 filename, line_num, arg);
4261 errors++;
4262 } else
4263 acl.last = acl.first;
4265 get_arg(arg, sizeof(arg), &p);
4267 if (arg[0]) {
4268 if (resolve_host(&acl.last, arg) != 0) {
4269 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4270 filename, line_num, arg);
4271 errors++;
4275 if (!errors) {
4276 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4277 IPAddressACL **naclp = 0;
4279 acl.next = 0;
4280 *nacl = acl;
4282 if (stream)
4283 naclp = &stream->acl;
4284 else if (feed)
4285 naclp = &feed->acl;
4286 else {
4287 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4288 filename, line_num);
4289 errors++;
4292 if (naclp) {
4293 while (*naclp)
4294 naclp = &(*naclp)->next;
4296 *naclp = nacl;
4299 } else if (!strcasecmp(cmd, "RTSPOption")) {
4300 get_arg(arg, sizeof(arg), &p);
4301 if (stream) {
4302 av_freep(&stream->rtsp_option);
4303 stream->rtsp_option = av_strdup(arg);
4305 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4306 get_arg(arg, sizeof(arg), &p);
4307 if (stream) {
4308 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4309 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4310 filename, line_num, arg);
4311 errors++;
4313 stream->is_multicast = 1;
4314 stream->loop = 1; /* default is looping */
4316 } else if (!strcasecmp(cmd, "MulticastPort")) {
4317 get_arg(arg, sizeof(arg), &p);
4318 if (stream)
4319 stream->multicast_port = atoi(arg);
4320 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4321 get_arg(arg, sizeof(arg), &p);
4322 if (stream)
4323 stream->multicast_ttl = atoi(arg);
4324 } else if (!strcasecmp(cmd, "NoLoop")) {
4325 if (stream)
4326 stream->loop = 0;
4327 } else if (!strcasecmp(cmd, "</Stream>")) {
4328 if (!stream) {
4329 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4330 filename, line_num);
4331 errors++;
4332 } else {
4333 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4334 if (audio_id != CODEC_ID_NONE) {
4335 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4336 audio_enc.codec_id = audio_id;
4337 add_codec(stream, &audio_enc);
4339 if (video_id != CODEC_ID_NONE) {
4340 video_enc.codec_type = CODEC_TYPE_VIDEO;
4341 video_enc.codec_id = video_id;
4342 add_codec(stream, &video_enc);
4345 stream = NULL;
4347 } else if (!strcasecmp(cmd, "<Redirect")) {
4348 /*********************************************/
4349 char *q;
4350 if (stream || feed || redirect) {
4351 fprintf(stderr, "%s:%d: Already in a tag\n",
4352 filename, line_num);
4353 errors++;
4354 } else {
4355 redirect = av_mallocz(sizeof(FFStream));
4356 *last_stream = redirect;
4357 last_stream = &redirect->next;
4359 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4360 q = strrchr(redirect->filename, '>');
4361 if (*q)
4362 *q = '\0';
4363 redirect->stream_type = STREAM_TYPE_REDIRECT;
4365 } else if (!strcasecmp(cmd, "URL")) {
4366 if (redirect)
4367 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4368 } else if (!strcasecmp(cmd, "</Redirect>")) {
4369 if (!redirect) {
4370 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4371 filename, line_num);
4372 errors++;
4373 } else {
4374 if (!redirect->feed_filename[0]) {
4375 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4376 filename, line_num);
4377 errors++;
4379 redirect = NULL;
4381 } else if (!strcasecmp(cmd, "LoadModule")) {
4382 get_arg(arg, sizeof(arg), &p);
4383 #ifdef HAVE_DLOPEN
4384 load_module(arg);
4385 #else
4386 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4387 filename, line_num, arg);
4388 errors++;
4389 #endif
4390 } else {
4391 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4392 filename, line_num, cmd);
4393 errors++;
4397 fclose(f);
4398 if (errors)
4399 return -1;
4400 else
4401 return 0;
4404 static void handle_child_exit(int sig)
4406 pid_t pid;
4407 int status;
4409 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4410 FFStream *feed;
4412 for (feed = first_feed; feed; feed = feed->next) {
4413 if (feed->pid == pid) {
4414 int uptime = time(0) - feed->pid_start;
4416 feed->pid = 0;
4417 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4419 if (uptime < 30)
4420 /* Turn off any more restarts */
4421 feed->child_argv = 0;
4426 need_to_start_children = 1;
4429 static void opt_debug()
4431 ffserver_debug = 1;
4432 ffserver_daemon = 0;
4433 logfilename[0] = '-';
4436 static void opt_show_help(void)
4438 printf("usage: ffserver [options]\n"
4439 "Hyper fast multi format Audio/Video streaming server\n");
4440 printf("\n");
4441 show_help_options(options, "Main options:\n", 0, 0);
4444 static const OptionDef options[] = {
4445 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4446 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4447 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4448 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4449 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4450 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4451 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4452 { NULL },
4455 int main(int argc, char **argv)
4457 struct sigaction sigact;
4459 av_register_all();
4461 show_banner();
4463 config_filename = "/etc/ffserver.conf";
4465 my_program_name = argv[0];
4466 my_program_dir = getcwd(0, 0);
4467 ffserver_daemon = 1;
4469 parse_options(argc, argv, options, NULL);
4471 unsetenv("http_proxy"); /* Kill the http_proxy */
4473 av_init_random(av_gettime() + (getpid() << 16), &random_state);
4475 memset(&sigact, 0, sizeof(sigact));
4476 sigact.sa_handler = handle_child_exit;
4477 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4478 sigaction(SIGCHLD, &sigact, 0);
4480 if (parse_ffconfig(config_filename) < 0) {
4481 fprintf(stderr, "Incorrect config file - exiting.\n");
4482 exit(1);
4485 /* open log file if needed */
4486 if (logfilename[0] != '\0') {
4487 if (!strcmp(logfilename, "-"))
4488 logfile = stdout;
4489 else
4490 logfile = fopen(logfilename, "a");
4491 av_log_set_callback(http_av_log);
4494 build_file_streams();
4496 build_feed_streams();
4498 compute_bandwidth();
4500 /* put the process in background and detach it from its TTY */
4501 if (ffserver_daemon) {
4502 int pid;
4504 pid = fork();
4505 if (pid < 0) {
4506 perror("fork");
4507 exit(1);
4508 } else if (pid > 0) {
4509 /* parent : exit */
4510 exit(0);
4511 } else {
4512 /* child */
4513 setsid();
4514 close(0);
4515 open("/dev/null", O_RDWR);
4516 if (strcmp(logfilename, "-") != 0) {
4517 close(1);
4518 dup(0);
4520 close(2);
4521 dup(0);
4525 /* signal init */
4526 signal(SIGPIPE, SIG_IGN);
4528 if (ffserver_daemon)
4529 chdir("/");
4531 if (http_server() < 0) {
4532 http_log("Could not start server\n");
4533 exit(1);
4536 return 0;