2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #define HAVE_AV_CONFIG_H
25 #include <sys/ioctl.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
38 #ifdef CONFIG_HAVE_DLFCN
44 /* maximum number of simultaneous HTTP connections */
45 #define HTTP_MAX_CONNECTIONS 2000
48 HTTPSTATE_WAIT_REQUEST,
49 HTTPSTATE_SEND_HEADER,
50 HTTPSTATE_SEND_DATA_HEADER,
51 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
52 HTTPSTATE_SEND_DATA_TRAILER,
53 HTTPSTATE_RECEIVE_DATA,
54 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
55 HTTPSTATE_WAIT, /* wait before sending next packets */
56 HTTPSTATE_WAIT_SHORT, /* short wait for short term
57 bandwidth limitation */
60 RTSPSTATE_WAIT_REQUEST,
64 const char *http_state[] = {
81 #define IOBUFFER_INIT_SIZE 8192
83 /* coef for exponential mean for bitrate estimation in statistics */
86 /* timeouts are in ms */
87 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
88 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
90 #define SYNC_TIMEOUT (10 * 1000)
97 /* context associated with one connection */
98 typedef struct HTTPContext {
100 int fd; /* socket file descriptor */
101 struct sockaddr_in from_addr; /* origin */
102 struct pollfd *poll_entry; /* used when polling */
104 UINT8 *buffer_ptr, *buffer_end;
106 struct HTTPContext *next;
107 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
111 /* input format handling */
112 AVFormatContext *fmt_in;
113 long start_time; /* In milliseconds - this wraps fairly often */
114 INT64 first_pts; /* initial pts value */
115 int pts_stream_index; /* stream we choose as clock reference */
116 /* output format handling */
117 struct FFStream *stream;
118 /* -1 is invalid stream */
119 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
120 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
122 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
123 int last_packet_sent; /* true if last data packet was sent */
125 DataRateData datarate;
132 int is_packetized; /* if true, the stream is packetized */
133 int packet_stream_index; /* current stream for output in state machine */
135 /* RTSP state specific */
136 UINT8 *pb_buffer; /* XXX: use that in all the code */
138 int seq; /* RTSP sequence number */
140 /* RTP state specific */
141 enum RTSPProtocol rtp_protocol;
142 char session_id[32]; /* session id */
143 AVFormatContext *rtp_ctx[MAX_STREAMS];
144 URLContext *rtp_handles[MAX_STREAMS];
145 /* RTP short term bandwidth limitation */
146 int packet_byte_count;
147 int packet_start_time_us; /* used for short durations (a few
151 static AVFrame dummy_frame;
153 /* each generated stream is described here */
157 STREAM_TYPE_REDIRECT,
160 enum IPAddressAction {
165 typedef struct IPAddressACL {
166 struct IPAddressACL *next;
167 enum IPAddressAction action;
168 struct in_addr first;
172 /* description of each stream of the ffserver.conf file */
173 typedef struct FFStream {
174 enum StreamType stream_type;
175 char filename[1024]; /* stream filename */
176 struct FFStream *feed; /* feed we are using (can be null if
181 int prebuffer; /* Number of millseconds early to start */
182 long max_time; /* Number of milliseconds to run */
184 AVStream *streams[MAX_STREAMS];
185 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
186 char feed_filename[1024]; /* file name of the feed storage, or
187 input file name for a stream */
192 pid_t pid; /* Of ffmpeg process */
193 time_t pid_start; /* Of ffmpeg process */
195 struct FFStream *next;
196 int bandwidth; /* bandwidth, in kbits/s */
199 /* multicast specific */
201 struct in_addr multicast_ip;
202 int multicast_port; /* first port used for multicast */
204 int loop; /* if true, send the stream in loops (only meaningful if file) */
207 int feed_opened; /* true if someone is writing to the feed */
208 int is_feed; /* true if it is a feed */
211 INT64 feed_max_size; /* maximum storage size */
212 INT64 feed_write_index; /* current write position in feed (it wraps round) */
213 INT64 feed_size; /* current size of feed */
214 struct FFStream *next_feed;
217 typedef struct FeedData {
218 long long data_count;
219 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
222 struct sockaddr_in my_http_addr;
223 struct sockaddr_in my_rtsp_addr;
225 char logfilename[1024];
226 HTTPContext *first_http_ctx;
227 FFStream *first_feed; /* contains only feeds */
228 FFStream *first_stream; /* contains all streams, including feeds */
230 static void new_connection(int server_fd, int is_rtsp);
231 static void close_connection(HTTPContext *c);
234 static int handle_connection(HTTPContext *c);
235 static int http_parse_request(HTTPContext *c);
236 static int http_send_data(HTTPContext *c);
237 static void compute_stats(HTTPContext *c);
238 static int open_input_stream(HTTPContext *c, const char *info);
239 static int http_start_receive_data(HTTPContext *c);
240 static int http_receive_data(HTTPContext *c);
241 static int compute_send_delay(HTTPContext *c);
244 static int rtsp_parse_request(HTTPContext *c);
245 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
246 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
247 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
248 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
249 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
252 static int prepare_sdp_description(FFStream *stream, UINT8 **pbuffer,
253 struct in_addr my_ip);
256 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
257 FFStream *stream, const char *session_id);
258 static int rtp_new_av_stream(HTTPContext *c,
259 int stream_index, struct sockaddr_in *dest_addr);
261 static const char *my_program_name;
262 static const char *my_program_dir;
264 static int ffserver_debug;
265 static int ffserver_daemon;
266 static int no_launch;
267 static int need_to_start_children;
269 int nb_max_connections;
273 int current_bandwidth;
275 static long cur_time; // Making this global saves on passing it around everywhere
277 static long gettime_ms(void)
281 gettimeofday(&tv,NULL);
282 return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
285 static FILE *logfile = NULL;
287 static void http_log(char *fmt, ...)
293 vfprintf(logfile, fmt, ap);
299 static char *ctime1(char *buf2)
307 p = buf2 + strlen(p) - 1;
313 static void log_connection(HTTPContext *c)
320 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
321 inet_ntoa(c->from_addr.sin_addr),
322 ctime1(buf2), c->method, c->url,
323 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
326 static void update_datarate(DataRateData *drd, INT64 count)
328 if (!drd->time1 && !drd->count1) {
329 drd->time1 = drd->time2 = cur_time;
330 drd->count1 = drd->count2 = count;
332 if (cur_time - drd->time2 > 5000) {
333 drd->time1 = drd->time2;
334 drd->count1 = drd->count2;
335 drd->time2 = cur_time;
341 /* In bytes per second */
342 static int compute_datarate(DataRateData *drd, INT64 count)
344 if (cur_time == drd->time1)
347 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
350 static int get_longterm_datarate(DataRateData *drd, INT64 count)
352 /* You get the first 3 seconds flat out */
353 if (cur_time - drd->time1 < 3000)
355 return compute_datarate(drd, count);
359 static void start_children(FFStream *feed)
364 for (; feed; feed = feed->next) {
365 if (feed->child_argv && !feed->pid) {
366 feed->pid_start = time(0);
371 fprintf(stderr, "Unable to create children\n");
380 for (i = 3; i < 256; i++) {
384 if (!ffserver_debug) {
385 i = open("/dev/null", O_RDWR);
394 pstrcpy(pathname, sizeof(pathname), my_program_name);
396 slash = strrchr(pathname, '/');
402 strcpy(slash, "ffmpeg");
404 /* This is needed to make relative pathnames work */
405 chdir(my_program_dir);
407 signal(SIGPIPE, SIG_DFL);
409 execvp(pathname, feed->child_argv);
417 /* open a listening socket */
418 static int socket_open_listen(struct sockaddr_in *my_addr)
422 server_fd = socket(AF_INET,SOCK_STREAM,0);
429 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
431 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
437 if (listen (server_fd, 5) < 0) {
442 fcntl(server_fd, F_SETFL, O_NONBLOCK);
447 /* start all multicast streams */
448 static void start_multicast(void)
453 struct sockaddr_in dest_addr;
454 int default_port, stream_index;
457 for(stream = first_stream; stream != NULL; stream = stream->next) {
458 if (stream->is_multicast) {
459 /* open the RTP connection */
460 snprintf(session_id, sizeof(session_id),
461 "%08x%08x", (int)random(), (int)random());
463 /* choose a port if none given */
464 if (stream->multicast_port == 0) {
465 stream->multicast_port = default_port;
469 dest_addr.sin_family = AF_INET;
470 dest_addr.sin_addr = stream->multicast_ip;
471 dest_addr.sin_port = htons(stream->multicast_port);
473 rtp_c = rtp_new_connection(&dest_addr, stream, session_id);
477 if (open_input_stream(rtp_c, "") < 0) {
478 fprintf(stderr, "Could not open input stream for stream '%s'\n",
483 rtp_c->rtp_protocol = RTSP_PROTOCOL_RTP_UDP_MULTICAST;
485 /* open each RTP stream */
486 for(stream_index = 0; stream_index < stream->nb_streams;
488 dest_addr.sin_port = htons(stream->multicast_port +
490 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
491 fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
492 stream->filename, stream_index);
497 /* change state to send data */
498 rtp_c->state = HTTPSTATE_SEND_DATA;
503 /* main loop of the http server */
504 static int http_server(void)
506 int server_fd, ret, rtsp_server_fd, delay, delay1;
507 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
508 HTTPContext *c, *c_next;
510 server_fd = socket_open_listen(&my_http_addr);
514 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
515 if (rtsp_server_fd < 0)
518 http_log("ffserver started.\n");
520 start_children(first_feed);
522 first_http_ctx = NULL;
524 first_http_ctx = NULL;
529 poll_entry = poll_table;
530 poll_entry->fd = server_fd;
531 poll_entry->events = POLLIN;
534 poll_entry->fd = rtsp_server_fd;
535 poll_entry->events = POLLIN;
538 /* wait for events on each HTTP handle */
545 case HTTPSTATE_SEND_HEADER:
546 case RTSPSTATE_SEND_REPLY:
547 c->poll_entry = poll_entry;
549 poll_entry->events = POLLOUT;
552 case HTTPSTATE_SEND_DATA_HEADER:
553 case HTTPSTATE_SEND_DATA:
554 case HTTPSTATE_SEND_DATA_TRAILER:
555 if (!c->is_packetized) {
556 /* for TCP, we output as much as we can (may need to put a limit) */
557 c->poll_entry = poll_entry;
559 poll_entry->events = POLLOUT;
562 /* not strictly correct, but currently cannot add
563 more than one fd in poll entry */
567 case HTTPSTATE_WAIT_REQUEST:
568 case HTTPSTATE_RECEIVE_DATA:
569 case HTTPSTATE_WAIT_FEED:
570 case RTSPSTATE_WAIT_REQUEST:
571 /* need to catch errors */
572 c->poll_entry = poll_entry;
574 poll_entry->events = POLLIN;/* Maybe this will work */
578 c->poll_entry = NULL;
579 delay1 = compute_send_delay(c);
583 case HTTPSTATE_WAIT_SHORT:
584 c->poll_entry = NULL;
585 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
590 c->poll_entry = NULL;
596 /* wait for an event on one connection. We poll at least every
597 second to handle timeouts */
599 ret = poll(poll_table, poll_entry - poll_table, delay);
602 cur_time = gettime_ms();
604 if (need_to_start_children) {
605 need_to_start_children = 0;
606 start_children(first_feed);
609 /* now handle the events */
610 for(c = first_http_ctx; c != NULL; c = c_next) {
612 if (handle_connection(c) < 0) {
613 /* close and free the connection */
619 poll_entry = poll_table;
620 /* new HTTP connection request ? */
621 if (poll_entry->revents & POLLIN) {
622 new_connection(server_fd, 0);
625 /* new RTSP connection request ? */
626 if (poll_entry->revents & POLLIN) {
627 new_connection(rtsp_server_fd, 1);
632 /* start waiting for a new HTTP/RTSP request */
633 static void start_wait_request(HTTPContext *c, int is_rtsp)
635 c->buffer_ptr = c->buffer;
636 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
639 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
640 c->state = RTSPSTATE_WAIT_REQUEST;
642 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
643 c->state = HTTPSTATE_WAIT_REQUEST;
647 static void new_connection(int server_fd, int is_rtsp)
649 struct sockaddr_in from_addr;
651 HTTPContext *c = NULL;
653 len = sizeof(from_addr);
654 fd = accept(server_fd, (struct sockaddr *)&from_addr,
658 fcntl(fd, F_SETFL, O_NONBLOCK);
660 /* XXX: should output a warning page when coming
661 close to the connection limit */
662 if (nb_connections >= nb_max_connections)
665 /* add a new connection */
666 c = av_mallocz(sizeof(HTTPContext));
670 c->next = first_http_ctx;
673 c->poll_entry = NULL;
674 c->from_addr = from_addr;
675 c->buffer_size = IOBUFFER_INIT_SIZE;
676 c->buffer = av_malloc(c->buffer_size);
681 start_wait_request(c, is_rtsp);
693 static void close_connection(HTTPContext *c)
695 HTTPContext **cp, *c1;
697 AVFormatContext *ctx;
701 /* remove connection from list */
702 cp = &first_http_ctx;
703 while ((*cp) != NULL) {
712 /* remove connection associated resources */
716 /* close each frame parser */
717 for(i=0;i<c->fmt_in->nb_streams;i++) {
718 st = c->fmt_in->streams[i];
719 if (st->codec.codec) {
720 avcodec_close(&st->codec);
723 av_close_input_file(c->fmt_in);
726 /* free RTP output streams if any */
729 nb_streams = c->stream->nb_streams;
731 for(i=0;i<nb_streams;i++) {
734 av_write_trailer(ctx);
737 h = c->rtp_handles[i];
744 current_bandwidth -= c->stream->bandwidth;
745 av_freep(&c->pb_buffer);
751 static int handle_connection(HTTPContext *c)
756 case HTTPSTATE_WAIT_REQUEST:
757 case RTSPSTATE_WAIT_REQUEST:
759 if ((c->timeout - cur_time) < 0)
761 if (c->poll_entry->revents & (POLLERR | POLLHUP))
764 /* no need to read if no events */
765 if (!(c->poll_entry->revents & POLLIN))
768 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
770 if (errno != EAGAIN && errno != EINTR)
772 } else if (len == 0) {
775 /* search for end of request. XXX: not fully correct since garbage could come after the end */
777 c->buffer_ptr += len;
779 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
780 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
781 /* request found : parse it and reply */
782 if (c->state == HTTPSTATE_WAIT_REQUEST) {
783 ret = http_parse_request(c);
785 ret = rtsp_parse_request(c);
789 } else if (ptr >= c->buffer_end) {
790 /* request too long: cannot do anything */
796 case HTTPSTATE_SEND_HEADER:
797 if (c->poll_entry->revents & (POLLERR | POLLHUP))
800 /* no need to write if no events */
801 if (!(c->poll_entry->revents & POLLOUT))
803 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
805 if (errno != EAGAIN && errno != EINTR) {
806 /* error : close connection */
807 av_freep(&c->pb_buffer);
811 c->buffer_ptr += len;
813 c->stream->bytes_served += len;
814 c->data_count += len;
815 if (c->buffer_ptr >= c->buffer_end) {
816 av_freep(&c->pb_buffer);
821 /* all the buffer was sent : synchronize to the incoming stream */
822 c->state = HTTPSTATE_SEND_DATA_HEADER;
823 c->buffer_ptr = c->buffer_end = c->buffer;
828 case HTTPSTATE_SEND_DATA:
829 case HTTPSTATE_SEND_DATA_HEADER:
830 case HTTPSTATE_SEND_DATA_TRAILER:
831 /* for packetized output, we consider we can always write (the
832 input streams sets the speed). It may be better to verify
833 that we do not rely too much on the kernel queues */
834 if (!c->is_packetized) {
835 if (c->poll_entry->revents & (POLLERR | POLLHUP))
838 /* no need to read if no events */
839 if (!(c->poll_entry->revents & POLLOUT))
842 if (http_send_data(c) < 0)
845 case HTTPSTATE_RECEIVE_DATA:
846 /* no need to read if no events */
847 if (c->poll_entry->revents & (POLLERR | POLLHUP))
849 if (!(c->poll_entry->revents & POLLIN))
851 if (http_receive_data(c) < 0)
854 case HTTPSTATE_WAIT_FEED:
855 /* no need to read if no events */
856 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
859 /* nothing to do, we'll be waken up by incoming feed packets */
863 /* if the delay expired, we can send new packets */
864 if (compute_send_delay(c) <= 0)
865 c->state = HTTPSTATE_SEND_DATA;
867 case HTTPSTATE_WAIT_SHORT:
868 /* just return back to send data */
869 c->state = HTTPSTATE_SEND_DATA;
872 case RTSPSTATE_SEND_REPLY:
873 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
874 av_freep(&c->pb_buffer);
877 /* no need to write if no events */
878 if (!(c->poll_entry->revents & POLLOUT))
880 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
882 if (errno != EAGAIN && errno != EINTR) {
883 /* error : close connection */
884 av_freep(&c->pb_buffer);
888 c->buffer_ptr += len;
889 c->data_count += len;
890 if (c->buffer_ptr >= c->buffer_end) {
891 /* all the buffer was sent : wait for a new request */
892 av_freep(&c->pb_buffer);
893 start_wait_request(c, 1);
897 case HTTPSTATE_READY:
906 static int extract_rates(char *rates, int ratelen, const char *request)
910 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
911 if (strncasecmp(p, "Pragma:", 7) == 0) {
912 const char *q = p + 7;
914 while (*q && *q != '\n' && isspace(*q))
917 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
923 memset(rates, 0xff, ratelen);
926 while (*q && *q != '\n' && *q != ':')
929 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
933 if (stream_no < ratelen && stream_no >= 0) {
934 rates[stream_no] = rate_no;
937 while (*q && *q != '\n' && !isspace(*q))
954 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
957 int best_bitrate = 100000000;
960 for (i = 0; i < feed->nb_streams; i++) {
961 AVCodecContext *feed_codec = &feed->streams[i]->codec;
963 if (feed_codec->codec_id != codec->codec_id ||
964 feed_codec->sample_rate != codec->sample_rate ||
965 feed_codec->width != codec->width ||
966 feed_codec->height != codec->height) {
970 /* Potential stream */
972 /* We want the fastest stream less than bit_rate, or the slowest
973 * faster than bit_rate
976 if (feed_codec->bit_rate <= bit_rate) {
977 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
978 best_bitrate = feed_codec->bit_rate;
982 if (feed_codec->bit_rate < best_bitrate) {
983 best_bitrate = feed_codec->bit_rate;
992 static int modify_current_stream(HTTPContext *c, char *rates)
995 FFStream *req = c->stream;
996 int action_required = 0;
998 for (i = 0; i < req->nb_streams; i++) {
999 AVCodecContext *codec = &req->streams[i]->codec;
1003 c->switch_feed_streams[i] = req->feed_streams[i];
1006 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1009 /* Wants off or slow */
1010 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1012 /* This doesn't work well when it turns off the only stream! */
1013 c->switch_feed_streams[i] = -2;
1014 c->feed_streams[i] = -2;
1019 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1020 action_required = 1;
1023 return action_required;
1027 static void do_switch_stream(HTTPContext *c, int i)
1029 if (c->switch_feed_streams[i] >= 0) {
1031 c->feed_streams[i] = c->switch_feed_streams[i];
1034 /* Now update the stream */
1036 c->switch_feed_streams[i] = -1;
1039 /* XXX: factorize in utils.c ? */
1040 /* XXX: take care with different space meaning */
1041 static void skip_spaces(const char **pp)
1045 while (*p == ' ' || *p == '\t')
1050 static void get_word(char *buf, int buf_size, const char **pp)
1058 while (!isspace(*p) && *p != '\0') {
1059 if ((q - buf) < buf_size - 1)
1068 static int validate_acl(FFStream *stream, HTTPContext *c)
1070 enum IPAddressAction last_action = IP_DENY;
1072 struct in_addr *src = &c->from_addr.sin_addr;
1074 for (acl = stream->acl; acl; acl = acl->next) {
1075 if (src->s_addr >= acl->first.s_addr && src->s_addr <= acl->last.s_addr) {
1076 return (acl->action == IP_ALLOW) ? 1 : 0;
1078 last_action = acl->action;
1081 /* Nothing matched, so return not the last action */
1082 return (last_action == IP_DENY) ? 1 : 0;
1085 /* compute the real filename of a file by matching it without its
1086 extensions to all the stream filenames */
1087 static void compute_real_filename(char *filename, int max_size)
1094 /* compute filename by matching without the file extensions */
1095 pstrcpy(file1, sizeof(file1), filename);
1096 p = strrchr(file1, '.');
1099 for(stream = first_stream; stream != NULL; stream = stream->next) {
1100 pstrcpy(file2, sizeof(file2), stream->filename);
1101 p = strrchr(file2, '.');
1104 if (!strcmp(file1, file2)) {
1105 pstrcpy(filename, max_size, stream->filename);
1120 /* parse http request and prepare header */
1121 static int http_parse_request(HTTPContext *c)
1125 enum RedirType redir_type;
1127 char info[1024], *filename;
1131 const char *mime_type;
1135 char *useragent = 0;
1138 get_word(cmd, sizeof(cmd), (const char **)&p);
1139 pstrcpy(c->method, sizeof(c->method), cmd);
1141 if (!strcmp(cmd, "GET"))
1143 else if (!strcmp(cmd, "POST"))
1148 get_word(url, sizeof(url), (const char **)&p);
1149 pstrcpy(c->url, sizeof(c->url), url);
1151 get_word(protocol, sizeof(protocol), (const char **)&p);
1152 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1155 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
1157 /* find the filename and the optional info string in the request */
1164 pstrcpy(info, sizeof(info), p);
1170 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1171 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1173 if (*useragent && *useragent != '\n' && isspace(*useragent))
1177 p = strchr(p, '\n');
1184 redir_type = REDIR_NONE;
1185 if (match_ext(filename, "asx")) {
1186 redir_type = REDIR_ASX;
1187 filename[strlen(filename)-1] = 'f';
1188 } else if (match_ext(filename, ".asf") &&
1189 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1190 /* if this isn't WMP or lookalike, return the redirector file */
1191 redir_type = REDIR_ASF;
1192 } else if (match_ext(filename, "rpm,ram")) {
1193 redir_type = REDIR_RAM;
1194 strcpy(filename + strlen(filename)-2, "m");
1195 } else if (match_ext(filename, "rtsp")) {
1196 redir_type = REDIR_RTSP;
1197 compute_real_filename(filename, sizeof(url) - 1);
1198 } else if (match_ext(filename, "sdp")) {
1199 redir_type = REDIR_SDP;
1200 compute_real_filename(filename, sizeof(url) - 1);
1203 stream = first_stream;
1204 while (stream != NULL) {
1205 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1207 stream = stream->next;
1209 if (stream == NULL) {
1210 sprintf(msg, "File '%s' not found", url);
1215 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1216 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1218 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1219 c->http_error = 301;
1221 q += sprintf(q, "HTTP/1.0 301 Moved\r\n");
1222 q += sprintf(q, "Location: %s\r\n", stream->feed_filename);
1223 q += sprintf(q, "Content-type: text/html\r\n");
1224 q += sprintf(q, "\r\n");
1225 q += sprintf(q, "<html><head><title>Moved</title></head><body>\r\n");
1226 q += sprintf(q, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1227 q += sprintf(q, "</body></html>\r\n");
1229 /* prepare output buffer */
1230 c->buffer_ptr = c->buffer;
1232 c->state = HTTPSTATE_SEND_HEADER;
1236 /* If this is WMP, get the rate information */
1237 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1238 if (modify_current_stream(c, ratebuf)) {
1239 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1240 if (c->switch_feed_streams[i] >= 0)
1241 do_switch_stream(c, i);
1246 if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1247 current_bandwidth += stream->bandwidth;
1250 if (post == 0 && max_bandwidth < current_bandwidth) {
1251 c->http_error = 200;
1253 q += sprintf(q, "HTTP/1.0 200 Server too busy\r\n");
1254 q += sprintf(q, "Content-type: text/html\r\n");
1255 q += sprintf(q, "\r\n");
1256 q += sprintf(q, "<html><head><title>Too busy</title></head><body>\r\n");
1257 q += sprintf(q, "The server is too busy to serve your request at this time.<p>\r\n");
1258 q += sprintf(q, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
1259 current_bandwidth, max_bandwidth);
1260 q += sprintf(q, "</body></html>\r\n");
1262 /* prepare output buffer */
1263 c->buffer_ptr = c->buffer;
1265 c->state = HTTPSTATE_SEND_HEADER;
1269 if (redir_type != REDIR_NONE) {
1272 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1273 if (strncasecmp(p, "Host:", 5) == 0) {
1277 p = strchr(p, '\n');
1288 while (isspace(*hostinfo))
1291 eoh = strchr(hostinfo, '\n');
1293 if (eoh[-1] == '\r')
1296 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1297 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1298 hostbuf[eoh - hostinfo] = 0;
1300 c->http_error = 200;
1302 switch(redir_type) {
1304 q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n");
1305 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1306 q += sprintf(q, "\r\n");
1307 q += sprintf(q, "<ASX Version=\"3\">\r\n");
1308 q += sprintf(q, "<!-- Autogenerated by ffserver -->\r\n");
1309 q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1310 hostbuf, filename, info);
1311 q += sprintf(q, "</ASX>\r\n");
1314 q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n");
1315 q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n");
1316 q += sprintf(q, "\r\n");
1317 q += sprintf(q, "# Autogenerated by ffserver\r\n");
1318 q += sprintf(q, "http://%s/%s%s\r\n",
1319 hostbuf, filename, info);
1322 q += sprintf(q, "HTTP/1.0 200 ASF Redirect follows\r\n");
1323 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1324 q += sprintf(q, "\r\n");
1325 q += sprintf(q, "[Reference]\r\n");
1326 q += sprintf(q, "Ref1=http://%s/%s%s\r\n",
1327 hostbuf, filename, info);
1331 char hostname[256], *p;
1332 /* extract only hostname */
1333 pstrcpy(hostname, sizeof(hostname), hostbuf);
1334 p = strrchr(hostname, ':');
1337 q += sprintf(q, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1338 /* XXX: incorrect mime type ? */
1339 q += sprintf(q, "Content-type: application/x-rtsp\r\n");
1340 q += sprintf(q, "\r\n");
1341 q += sprintf(q, "rtsp://%s:%d/%s\r\n",
1342 hostname, ntohs(my_rtsp_addr.sin_port),
1349 int sdp_data_size, len;
1350 struct sockaddr_in my_addr;
1352 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1353 q += sprintf(q, "Content-type: application/sdp\r\n");
1354 q += sprintf(q, "\r\n");
1356 len = sizeof(my_addr);
1357 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1359 /* XXX: should use a dynamic buffer */
1360 sdp_data_size = prepare_sdp_description(stream,
1363 if (sdp_data_size > 0) {
1364 memcpy(q, sdp_data, sdp_data_size);
1376 /* prepare output buffer */
1377 c->buffer_ptr = c->buffer;
1379 c->state = HTTPSTATE_SEND_HEADER;
1385 sprintf(msg, "ASX/RAM file not handled");
1389 stream->conns_served++;
1391 /* XXX: add there authenticate and IP match */
1394 /* if post, it means a feed is being sent */
1395 if (!stream->is_feed) {
1396 /* However it might be a status report from WMP! Lets log the data
1397 * as it might come in handy one day
1402 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1403 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1407 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1408 client_id = strtol(p + 18, 0, 10);
1410 p = strchr(p, '\n');
1418 char *eol = strchr(logline, '\n');
1423 if (eol[-1] == '\r')
1425 http_log("%.*s\n", eol - logline, logline);
1426 c->suppress_log = 1;
1431 http_log("\nGot request:\n%s\n", c->buffer);
1434 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1437 /* Now we have to find the client_id */
1438 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1439 if (wmpc->wmp_client_id == client_id)
1444 if (modify_current_stream(wmpc, ratebuf)) {
1445 wmpc->switch_pending = 1;
1450 sprintf(msg, "POST command not handled");
1453 if (http_start_receive_data(c) < 0) {
1454 sprintf(msg, "could not open feed");
1458 c->state = HTTPSTATE_RECEIVE_DATA;
1463 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1464 http_log("\nGot request:\n%s\n", c->buffer);
1468 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1471 /* open input stream */
1472 if (open_input_stream(c, info) < 0) {
1473 sprintf(msg, "Input stream corresponding to '%s' not found", url);
1477 /* prepare http header */
1479 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1480 mime_type = c->stream->fmt->mime_type;
1482 mime_type = "application/x-octet_stream";
1483 q += sprintf(q, "Pragma: no-cache\r\n");
1485 /* for asf, we need extra headers */
1486 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1487 /* Need to allocate a client id */
1489 c->wmp_client_id = random() & 0x7fffffff;
1491 q += sprintf(q, "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);
1492 mime_type = "application/octet-stream";
1494 q += sprintf(q, "Content-Type: %s\r\n", mime_type);
1495 q += sprintf(q, "\r\n");
1497 /* prepare output buffer */
1499 c->buffer_ptr = c->buffer;
1501 c->state = HTTPSTATE_SEND_HEADER;
1504 c->http_error = 404;
1506 q += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
1507 q += sprintf(q, "Content-type: %s\r\n", "text/html");
1508 q += sprintf(q, "\r\n");
1509 q += sprintf(q, "<HTML>\n");
1510 q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1511 q += sprintf(q, "<BODY>%s</BODY>\n", msg);
1512 q += sprintf(q, "</HTML>\n");
1514 /* prepare output buffer */
1515 c->buffer_ptr = c->buffer;
1517 c->state = HTTPSTATE_SEND_HEADER;
1521 c->http_error = 200; /* horrible : we use this value to avoid
1522 going to the send data state */
1523 c->state = HTTPSTATE_SEND_HEADER;
1527 static void fmt_bytecount(ByteIOContext *pb, INT64 count)
1529 static const char *suffix = " kMGTP";
1532 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1535 url_fprintf(pb, "%lld%c", count, *s);
1538 static void compute_stats(HTTPContext *c)
1545 ByteIOContext pb1, *pb = &pb1;
1547 if (url_open_dyn_buf(pb) < 0) {
1548 /* XXX: return an error ? */
1549 c->buffer_ptr = c->buffer;
1550 c->buffer_end = c->buffer;
1554 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1555 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1556 url_fprintf(pb, "Pragma: no-cache\r\n");
1557 url_fprintf(pb, "\r\n");
1559 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1560 if (c->stream->feed_filename) {
1561 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1563 url_fprintf(pb, "</HEAD>\n<BODY>");
1564 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1566 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1567 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1568 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");
1569 stream = first_stream;
1570 while (stream != NULL) {
1571 char sfilename[1024];
1574 if (stream->feed != stream) {
1575 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1576 eosf = sfilename + strlen(sfilename);
1577 if (eosf - sfilename >= 4) {
1578 if (strcmp(eosf - 4, ".asf") == 0) {
1579 strcpy(eosf - 4, ".asx");
1580 } else if (strcmp(eosf - 3, ".rm") == 0) {
1581 strcpy(eosf - 3, ".ram");
1582 } else if (stream->fmt == &rtp_mux) {
1583 /* generate a sample RTSP director if
1584 unicast. Generate an SDP redirector if
1586 eosf = strrchr(sfilename, '.');
1588 eosf = sfilename + strlen(sfilename);
1589 if (stream->is_multicast)
1590 strcpy(eosf, ".sdp");
1592 strcpy(eosf, ".rtsp");
1596 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1597 sfilename, stream->filename);
1598 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1599 stream->conns_served);
1600 fmt_bytecount(pb, stream->bytes_served);
1601 switch(stream->stream_type) {
1602 case STREAM_TYPE_LIVE:
1604 int audio_bit_rate = 0;
1605 int video_bit_rate = 0;
1606 const char *audio_codec_name = "";
1607 const char *video_codec_name = "";
1608 const char *audio_codec_name_extra = "";
1609 const char *video_codec_name_extra = "";
1611 for(i=0;i<stream->nb_streams;i++) {
1612 AVStream *st = stream->streams[i];
1613 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1614 switch(st->codec.codec_type) {
1615 case CODEC_TYPE_AUDIO:
1616 audio_bit_rate += st->codec.bit_rate;
1618 if (*audio_codec_name)
1619 audio_codec_name_extra = "...";
1620 audio_codec_name = codec->name;
1623 case CODEC_TYPE_VIDEO:
1624 video_bit_rate += st->codec.bit_rate;
1626 if (*video_codec_name)
1627 video_codec_name_extra = "...";
1628 video_codec_name = codec->name;
1635 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",
1638 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1639 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1641 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1643 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1645 url_fprintf(pb, "\n");
1649 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1653 stream = stream->next;
1655 url_fprintf(pb, "</TABLE>\n");
1657 stream = first_stream;
1658 while (stream != NULL) {
1659 if (stream->feed == stream) {
1660 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1662 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1664 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1669 /* This is somewhat linux specific I guess */
1670 snprintf(ps_cmd, sizeof(ps_cmd),
1671 "ps -o \"%%cpu,cputime\" --no-headers %d",
1674 pid_stat = popen(ps_cmd, "r");
1679 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1681 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1689 url_fprintf(pb, "<p>");
1691 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");
1693 for (i = 0; i < stream->nb_streams; i++) {
1694 AVStream *st = stream->streams[i];
1695 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1696 char *type = "unknown";
1697 char parameters[64];
1701 switch(st->codec.codec_type) {
1702 case CODEC_TYPE_AUDIO:
1705 case CODEC_TYPE_VIDEO:
1707 sprintf(parameters, "%dx%d, q=%d-%d, fps=%d", st->codec.width, st->codec.height,
1708 st->codec.qmin, st->codec.qmax, st->codec.frame_rate / FRAME_RATE_BASE);
1713 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1714 i, type, st->codec.bit_rate/1000, codec ? codec->name : "", parameters);
1716 url_fprintf(pb, "</table>\n");
1719 stream = stream->next;
1725 AVCodecContext *enc;
1729 stream = first_feed;
1730 while (stream != NULL) {
1731 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1732 url_fprintf(pb, "<TABLE>\n");
1733 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1734 for(i=0;i<stream->nb_streams;i++) {
1735 AVStream *st = stream->streams[i];
1736 FeedData *fdata = st->priv_data;
1739 avcodec_string(buf, sizeof(buf), enc);
1740 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1741 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1742 avg /= enc->frame_size;
1743 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1744 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1746 url_fprintf(pb, "</TABLE>\n");
1747 stream = stream->next_feed;
1752 /* connection status */
1753 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1755 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1756 nb_connections, nb_max_connections);
1758 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1759 current_bandwidth, max_bandwidth);
1761 url_fprintf(pb, "<TABLE>\n");
1762 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");
1763 c1 = first_http_ctx;
1765 while (c1 != NULL) {
1771 for (j = 0; j < c1->stream->nb_streams; j++) {
1772 if (!c1->stream->feed) {
1773 bitrate += c1->stream->streams[j]->codec.bit_rate;
1775 if (c1->feed_streams[j] >= 0) {
1776 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec.bit_rate;
1783 p = inet_ntoa(c1->from_addr.sin_addr);
1784 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1786 c1->stream ? c1->stream->filename : "",
1787 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1790 http_state[c1->state]);
1791 fmt_bytecount(pb, bitrate);
1792 url_fprintf(pb, "<td align=right>");
1793 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1794 url_fprintf(pb, "<td align=right>");
1795 fmt_bytecount(pb, c1->data_count);
1796 url_fprintf(pb, "\n");
1799 url_fprintf(pb, "</TABLE>\n");
1804 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1805 url_fprintf(pb, "</BODY>\n</HTML>\n");
1807 len = url_close_dyn_buf(pb, &c->pb_buffer);
1808 c->buffer_ptr = c->pb_buffer;
1809 c->buffer_end = c->pb_buffer + len;
1812 /* check if the parser needs to be opened for stream i */
1813 static void open_parser(AVFormatContext *s, int i)
1815 AVStream *st = s->streams[i];
1818 if (!st->codec.codec) {
1819 codec = avcodec_find_decoder(st->codec.codec_id);
1820 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1821 st->codec.parse_only = 1;
1822 if (avcodec_open(&st->codec, codec) < 0) {
1823 st->codec.parse_only = 0;
1829 static int open_input_stream(HTTPContext *c, const char *info)
1832 char input_filename[1024];
1837 /* find file name */
1838 if (c->stream->feed) {
1839 strcpy(input_filename, c->stream->feed->feed_filename);
1840 buf_size = FFM_PACKET_SIZE;
1841 /* compute position (absolute time) */
1842 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1843 stream_pos = parse_date(buf, 0);
1844 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1845 int prebuffer = strtol(buf, 0, 10);
1846 stream_pos = av_gettime() - prebuffer * (INT64)1000000;
1848 stream_pos = av_gettime() - c->stream->prebuffer * (INT64)1000;
1851 strcpy(input_filename, c->stream->feed_filename);
1853 /* compute position (relative time) */
1854 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1855 stream_pos = parse_date(buf, 1);
1860 if (input_filename[0] == '\0')
1864 { time_t when = stream_pos / 1000000;
1865 http_log("Stream pos = %lld, time=%s", stream_pos, ctime(&when));
1870 if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0) {
1871 http_log("%s not found", input_filename);
1876 /* open each parser */
1877 for(i=0;i<s->nb_streams;i++)
1880 /* choose stream as clock source (we favorize video stream if
1881 present) for packet sending */
1882 c->pts_stream_index = 0;
1883 for(i=0;i<c->stream->nb_streams;i++) {
1884 if (c->pts_stream_index == 0 &&
1885 c->stream->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO) {
1886 c->pts_stream_index = i;
1890 if (c->fmt_in->iformat->read_seek) {
1891 c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
1893 /* set the start time (needed for maxtime and RTP packet timing) */
1894 c->start_time = cur_time;
1895 c->first_pts = AV_NOPTS_VALUE;
1899 /* currently desactivated because the new PTS handling is not
1901 //#define AV_READ_FRAME
1902 #ifdef AV_READ_FRAME
1904 /* XXX: generalize that in ffmpeg for picture/audio/data. Currently
1905 the return packet MUST NOT be freed */
1906 int av_read_frame(AVFormatContext *s, AVPacket *pkt)
1909 int len, ret, old_nb_streams, i;
1911 /* see if remaining frames must be parsed */
1913 if (s->cur_len > 0) {
1914 st = s->streams[s->cur_pkt.stream_index];
1915 len = avcodec_parse_frame(&st->codec, &pkt->data, &pkt->size,
1916 s->cur_ptr, s->cur_len);
1918 /* error: get next packet */
1924 /* init pts counter if not done */
1925 if (st->pts.den == 0) {
1926 switch(st->codec.codec_type) {
1927 case CODEC_TYPE_AUDIO:
1928 st->pts_incr = (INT64)s->pts_den;
1929 av_frac_init(&st->pts, st->pts.val, 0,
1930 (INT64)s->pts_num * st->codec.sample_rate);
1932 case CODEC_TYPE_VIDEO:
1933 st->pts_incr = (INT64)s->pts_den * FRAME_RATE_BASE;
1934 av_frac_init(&st->pts, st->pts.val, 0,
1935 (INT64)s->pts_num * st->codec.frame_rate);
1942 /* a frame was read: return it */
1943 pkt->pts = st->pts.val;
1945 printf("add pts=%Lx num=%Lx den=%Lx incr=%Lx\n",
1946 st->pts.val, st->pts.num, st->pts.den, st->pts_incr);
1948 switch(st->codec.codec_type) {
1949 case CODEC_TYPE_AUDIO:
1950 av_frac_add(&st->pts, st->pts_incr * st->codec.frame_size);
1952 case CODEC_TYPE_VIDEO:
1953 av_frac_add(&st->pts, st->pts_incr);
1958 pkt->stream_index = s->cur_pkt.stream_index;
1959 /* we use the codec indication because it is
1960 more accurate than the demux flags */
1962 if (st->codec.coded_frame->key_frame)
1963 pkt->flags |= PKT_FLAG_KEY;
1968 /* free previous packet */
1969 av_free_packet(&s->cur_pkt);
1971 old_nb_streams = s->nb_streams;
1972 ret = av_read_packet(s, &s->cur_pkt);
1975 /* open parsers for each new streams */
1976 for(i = old_nb_streams; i < s->nb_streams; i++)
1978 st = s->streams[s->cur_pkt.stream_index];
1980 /* update current pts (XXX: dts handling) from packet, or
1981 use current pts if none given */
1982 if (s->cur_pkt.pts != AV_NOPTS_VALUE) {
1983 av_frac_set(&st->pts, s->cur_pkt.pts);
1985 s->cur_pkt.pts = st->pts.val;
1987 if (!st->codec.codec) {
1988 /* no codec opened: just return the raw packet */
1991 /* no codec opened: just update the pts by considering we
1992 have one frame and free the packet */
1993 if (st->pts.den == 0) {
1994 switch(st->codec.codec_type) {
1995 case CODEC_TYPE_AUDIO:
1996 st->pts_incr = (INT64)s->pts_den * st->codec.frame_size;
1997 av_frac_init(&st->pts, st->pts.val, 0,
1998 (INT64)s->pts_num * st->codec.sample_rate);
2000 case CODEC_TYPE_VIDEO:
2001 st->pts_incr = (INT64)s->pts_den * FRAME_RATE_BASE;
2002 av_frac_init(&st->pts, st->pts.val, 0,
2003 (INT64)s->pts_num * st->codec.frame_rate);
2009 av_frac_add(&st->pts, st->pts_incr);
2012 s->cur_ptr = s->cur_pkt.data;
2013 s->cur_len = s->cur_pkt.size;
2019 static int compute_send_delay(HTTPContext *c)
2021 INT64 cur_pts, delta_pts, next_pts;
2024 /* compute current pts value from system time */
2025 cur_pts = ((INT64)(cur_time - c->start_time) * c->fmt_in->pts_den) /
2026 (c->fmt_in->pts_num * 1000LL);
2027 /* compute the delta from the stream we choose as
2028 main clock (we do that to avoid using explicit
2029 buffers to do exact packet reordering for each
2031 /* XXX: really need to fix the number of streams */
2032 if (c->pts_stream_index >= c->fmt_in->nb_streams)
2035 next_pts = c->fmt_in->streams[c->pts_stream_index]->pts.val;
2036 delta_pts = next_pts - cur_pts;
2037 if (delta_pts <= 0) {
2040 delay1 = (delta_pts * 1000 * c->fmt_in->pts_num) / c->fmt_in->pts_den;
2046 /* just fall backs */
2047 int av_read_frame(AVFormatContext *s, AVPacket *pkt)
2049 return av_read_packet(s, pkt);
2052 static int compute_send_delay(HTTPContext *c)
2054 int datarate = 8 * get_longterm_datarate(&c->datarate, c->data_count);
2056 if (datarate > c->stream->bandwidth * 2000) {
2064 static int http_prepare_data(HTTPContext *c)
2067 AVFormatContext *ctx;
2070 case HTTPSTATE_SEND_DATA_HEADER:
2071 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2072 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
2074 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
2075 c->stream->comment);
2076 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
2077 c->stream->copyright);
2078 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
2081 /* open output stream by using specified codecs */
2082 c->fmt_ctx.oformat = c->stream->fmt;
2083 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2084 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2086 st = av_mallocz(sizeof(AVStream));
2087 c->fmt_ctx.streams[i] = st;
2088 /* if file or feed, then just take streams from FFStream struct */
2089 if (!c->stream->feed ||
2090 c->stream->feed == c->stream)
2091 memcpy(st, c->stream->streams[i], sizeof(AVStream));
2093 memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]],
2095 st->codec.frame_number = 0; /* XXX: should be done in
2096 AVStream, not in codec */
2097 /* I'm pretty sure that this is not correct...
2098 * However, without it, we crash
2100 st->codec.coded_frame = &dummy_frame;
2102 c->got_key_frame = 0;
2104 /* prepare header and save header data in a stream */
2105 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2106 /* XXX: potential leak */
2109 c->fmt_ctx.pb.is_streamed = 1;
2111 av_write_header(&c->fmt_ctx);
2113 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2114 c->buffer_ptr = c->pb_buffer;
2115 c->buffer_end = c->pb_buffer + len;
2117 c->state = HTTPSTATE_SEND_DATA;
2118 c->last_packet_sent = 0;
2120 case HTTPSTATE_SEND_DATA:
2121 /* find a new packet */
2125 /* read a packet from the input stream */
2126 if (c->stream->feed) {
2127 ffm_set_write_index(c->fmt_in,
2128 c->stream->feed->feed_write_index,
2129 c->stream->feed->feed_size);
2132 if (c->stream->max_time &&
2133 c->stream->max_time + c->start_time - cur_time < 0) {
2134 /* We have timed out */
2135 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2137 if (1 || c->is_packetized) {
2138 if (compute_send_delay(c) > 0) {
2139 c->state = HTTPSTATE_WAIT;
2140 return 1; /* state changed */
2144 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2145 if (c->stream->feed && c->stream->feed->feed_opened) {
2146 /* if coming from feed, it means we reached the end of the
2147 ffm file, so must wait for more data */
2148 c->state = HTTPSTATE_WAIT_FEED;
2149 return 1; /* state changed */
2151 if (c->stream->loop) {
2152 av_close_input_file(c->fmt_in);
2154 if (open_input_stream(c, "") < 0)
2159 /* must send trailer now because eof or error */
2160 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2164 /* update first pts if needed */
2165 if (c->first_pts == AV_NOPTS_VALUE)
2166 c->first_pts = pkt.pts;
2168 /* send it to the appropriate stream */
2169 if (c->stream->feed) {
2170 /* if coming from a feed, select the right stream */
2171 if (c->switch_pending) {
2172 c->switch_pending = 0;
2173 for(i=0;i<c->stream->nb_streams;i++) {
2174 if (c->switch_feed_streams[i] == pkt.stream_index) {
2175 if (pkt.flags & PKT_FLAG_KEY) {
2176 do_switch_stream(c, i);
2179 if (c->switch_feed_streams[i] >= 0) {
2180 c->switch_pending = 1;
2184 for(i=0;i<c->stream->nb_streams;i++) {
2185 if (c->feed_streams[i] == pkt.stream_index) {
2186 pkt.stream_index = i;
2187 if (pkt.flags & PKT_FLAG_KEY) {
2188 c->got_key_frame |= 1 << i;
2190 /* See if we have all the key frames, then
2191 * we start to send. This logic is not quite
2192 * right, but it works for the case of a
2193 * single video stream with one or more
2194 * audio streams (for which every frame is
2195 * typically a key frame).
2197 if (!c->stream->send_on_key ||
2198 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2204 AVCodecContext *codec;
2207 /* specific handling for RTP: we use several
2208 output stream (one for each RTP
2209 connection). XXX: need more abstract handling */
2210 if (c->is_packetized) {
2211 c->packet_stream_index = pkt.stream_index;
2212 ctx = c->rtp_ctx[c->packet_stream_index];
2213 codec = &ctx->streams[0]->codec;
2214 /* only one stream per RTP connection */
2215 pkt.stream_index = 0;
2219 codec = &ctx->streams[pkt.stream_index]->codec;
2222 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2225 if (codec->codec_type == CODEC_TYPE_AUDIO) {
2226 codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
2227 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
2231 if (c->is_packetized) {
2232 ret = url_open_dyn_packet_buf(&ctx->pb,
2233 url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]));
2234 c->packet_byte_count = 0;
2235 c->packet_start_time_us = av_gettime();
2237 ret = url_open_dyn_buf(&ctx->pb);
2240 /* XXX: potential leak */
2243 if (av_write_frame(ctx, pkt.stream_index, pkt.data, pkt.size)) {
2244 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2247 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2248 c->buffer_ptr = c->pb_buffer;
2249 c->buffer_end = c->pb_buffer + len;
2251 codec->frame_number++;
2253 #ifndef AV_READ_FRAME
2254 av_free_packet(&pkt);
2261 case HTTPSTATE_SEND_DATA_TRAILER:
2262 /* last packet test ? */
2263 if (c->last_packet_sent || c->is_packetized)
2266 /* prepare header */
2267 if (url_open_dyn_buf(&ctx->pb) < 0) {
2268 /* XXX: potential leak */
2271 av_write_trailer(ctx);
2272 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2273 c->buffer_ptr = c->pb_buffer;
2274 c->buffer_end = c->pb_buffer + len;
2276 c->last_packet_sent = 1;
2283 #define SHORT_TERM_BANDWIDTH 8000000
2285 /* should convert the format at the same time */
2286 static int http_send_data(HTTPContext *c)
2290 while (c->buffer_ptr >= c->buffer_end) {
2291 av_freep(&c->pb_buffer);
2292 ret = http_prepare_data(c);
2295 else if (ret == 0) {
2298 /* state change requested */
2303 if (c->buffer_ptr < c->buffer_end) {
2304 if (c->is_packetized) {
2305 /* RTP/UDP data output */
2306 len = c->buffer_end - c->buffer_ptr;
2308 /* fail safe - should never happen */
2310 c->buffer_ptr = c->buffer_end;
2313 len = (c->buffer_ptr[0] << 24) |
2314 (c->buffer_ptr[1] << 16) |
2315 (c->buffer_ptr[2] << 8) |
2317 if (len > (c->buffer_end - c->buffer_ptr))
2320 /* short term bandwidth limitation */
2321 dt = av_gettime() - c->packet_start_time_us;
2325 if ((c->packet_byte_count + len) * (INT64)1000000 >=
2326 (SHORT_TERM_BANDWIDTH / 8) * (INT64)dt) {
2327 /* bandwidth overflow : wait at most one tick and retry */
2328 c->state = HTTPSTATE_WAIT_SHORT;
2333 url_write(c->rtp_handles[c->packet_stream_index],
2334 c->buffer_ptr, len);
2335 c->buffer_ptr += len;
2336 c->packet_byte_count += len;
2338 /* TCP data output */
2339 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2341 if (errno != EAGAIN && errno != EINTR) {
2342 /* error : close connection */
2348 c->buffer_ptr += len;
2351 c->data_count += len;
2352 update_datarate(&c->datarate, c->data_count);
2354 c->stream->bytes_served += len;
2359 static int http_start_receive_data(HTTPContext *c)
2363 if (c->stream->feed_opened)
2367 fd = open(c->stream->feed_filename, O_RDWR);
2372 c->stream->feed_write_index = ffm_read_write_index(fd);
2373 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2374 lseek(fd, 0, SEEK_SET);
2376 /* init buffer input */
2377 c->buffer_ptr = c->buffer;
2378 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2379 c->stream->feed_opened = 1;
2383 static int http_receive_data(HTTPContext *c)
2387 if (c->buffer_end > c->buffer_ptr) {
2390 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2392 if (errno != EAGAIN && errno != EINTR) {
2393 /* error : close connection */
2396 } else if (len == 0) {
2397 /* end of connection : close it */
2400 c->buffer_ptr += len;
2401 c->data_count += len;
2402 update_datarate(&c->datarate, c->data_count);
2406 if (c->buffer_ptr >= c->buffer_end) {
2407 FFStream *feed = c->stream;
2408 /* a packet has been received : write it in the store, except
2410 if (c->data_count > FFM_PACKET_SIZE) {
2412 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2413 /* XXX: use llseek or url_seek */
2414 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2415 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2417 feed->feed_write_index += FFM_PACKET_SIZE;
2418 /* update file size */
2419 if (feed->feed_write_index > c->stream->feed_size)
2420 feed->feed_size = feed->feed_write_index;
2422 /* handle wrap around if max file size reached */
2423 if (feed->feed_write_index >= c->stream->feed_max_size)
2424 feed->feed_write_index = FFM_PACKET_SIZE;
2427 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2429 /* wake up any waiting connections */
2430 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2431 if (c1->state == HTTPSTATE_WAIT_FEED &&
2432 c1->stream->feed == c->stream->feed) {
2433 c1->state = HTTPSTATE_SEND_DATA;
2437 /* We have a header in our hands that contains useful data */
2439 AVInputFormat *fmt_in;
2440 ByteIOContext *pb = &s.pb;
2443 memset(&s, 0, sizeof(s));
2445 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2446 pb->buf_end = c->buffer_end; /* ?? */
2447 pb->is_streamed = 1;
2449 /* use feed output format name to find corresponding input format */
2450 fmt_in = av_find_input_format(feed->fmt->name);
2454 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2458 if (fmt_in->read_header(&s, 0) < 0) {
2459 av_freep(&s.priv_data);
2463 /* Now we have the actual streams */
2464 if (s.nb_streams != feed->nb_streams) {
2465 av_freep(&s.priv_data);
2468 for (i = 0; i < s.nb_streams; i++) {
2469 memcpy(&feed->streams[i]->codec,
2470 &s.streams[i]->codec, sizeof(AVCodecContext));
2472 av_freep(&s.priv_data);
2474 c->buffer_ptr = c->buffer;
2479 c->stream->feed_opened = 0;
2484 /********************************************************************/
2487 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2494 switch(error_number) {
2495 #define DEF(n, c, s) case c: str = s; break;
2496 #include "rtspcodes.h"
2499 str = "Unknown Error";
2503 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2504 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2506 /* output GMT time */
2510 p = buf2 + strlen(p) - 1;
2513 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2516 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2518 rtsp_reply_header(c, error_number);
2519 url_fprintf(c->pb, "\r\n");
2522 static int rtsp_parse_request(HTTPContext *c)
2524 const char *p, *p1, *p2;
2531 RTSPHeader header1, *header = &header1;
2533 c->buffer_ptr[0] = '\0';
2536 get_word(cmd, sizeof(cmd), &p);
2537 get_word(url, sizeof(url), &p);
2538 get_word(protocol, sizeof(protocol), &p);
2540 pstrcpy(c->method, sizeof(c->method), cmd);
2541 pstrcpy(c->url, sizeof(c->url), url);
2542 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2545 if (url_open_dyn_buf(c->pb) < 0) {
2546 /* XXX: cannot do more */
2547 c->pb = NULL; /* safety */
2551 /* check version name */
2552 if (strcmp(protocol, "RTSP/1.0") != 0) {
2553 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2557 /* parse each header line */
2558 memset(header, 0, sizeof(RTSPHeader));
2559 /* skip to next line */
2560 while (*p != '\n' && *p != '\0')
2564 while (*p != '\0') {
2565 p1 = strchr(p, '\n');
2569 if (p2 > p && p2[-1] == '\r')
2571 /* skip empty line */
2575 if (len > sizeof(line) - 1)
2576 len = sizeof(line) - 1;
2577 memcpy(line, p, len);
2579 rtsp_parse_line(header, line);
2583 /* handle sequence number */
2584 c->seq = header->seq;
2586 if (!strcmp(cmd, "DESCRIBE")) {
2587 rtsp_cmd_describe(c, url);
2588 } else if (!strcmp(cmd, "SETUP")) {
2589 rtsp_cmd_setup(c, url, header);
2590 } else if (!strcmp(cmd, "PLAY")) {
2591 rtsp_cmd_play(c, url, header);
2592 } else if (!strcmp(cmd, "PAUSE")) {
2593 rtsp_cmd_pause(c, url, header);
2594 } else if (!strcmp(cmd, "TEARDOWN")) {
2595 rtsp_cmd_teardown(c, url, header);
2597 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2600 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2601 c->pb = NULL; /* safety */
2603 /* XXX: cannot do more */
2606 c->buffer_ptr = c->pb_buffer;
2607 c->buffer_end = c->pb_buffer + len;
2608 c->state = RTSPSTATE_SEND_REPLY;
2612 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2614 static int prepare_sdp_description(FFStream *stream, UINT8 **pbuffer,
2615 struct in_addr my_ip)
2617 ByteIOContext pb1, *pb = &pb1;
2618 int i, payload_type, port, private_payload_type, j;
2619 const char *ipstr, *title, *mediatype;
2622 if (url_open_dyn_buf(pb) < 0)
2625 /* general media info */
2627 url_fprintf(pb, "v=0\n");
2628 ipstr = inet_ntoa(my_ip);
2629 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2630 title = stream->title;
2631 if (title[0] == '\0')
2633 url_fprintf(pb, "s=%s\n", title);
2634 if (stream->comment[0] != '\0')
2635 url_fprintf(pb, "i=%s\n", stream->comment);
2636 if (stream->is_multicast) {
2637 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2639 /* for each stream, we output the necessary info */
2640 private_payload_type = 96;
2641 for(i = 0; i < stream->nb_streams; i++) {
2642 st = stream->streams[i];
2643 switch(st->codec.codec_type) {
2644 case CODEC_TYPE_AUDIO:
2645 mediatype = "audio";
2647 case CODEC_TYPE_VIDEO:
2648 mediatype = "video";
2651 mediatype = "application";
2654 /* NOTE: the port indication is not correct in case of
2655 unicast. It is not an issue because RTSP gives it */
2656 payload_type = rtp_get_payload_type(&st->codec);
2657 if (payload_type < 0)
2658 payload_type = private_payload_type++;
2659 if (stream->is_multicast) {
2660 port = stream->multicast_port + 2 * i;
2664 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2665 mediatype, port, payload_type);
2666 if (payload_type >= 96) {
2667 /* for private payload type, we need to give more info */
2668 switch(st->codec.codec_id) {
2669 case CODEC_ID_MPEG4:
2672 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2673 payload_type, 90000);
2674 /* we must also add the mpeg4 header */
2675 data = st->codec.extradata;
2677 url_fprintf(pb, "a=fmtp:%d config=");
2678 for(j=0;j<st->codec.extradata_size;j++) {
2679 url_fprintf(pb, "%02x", data[j]);
2681 url_fprintf(pb, "\n");
2686 /* XXX: add other codecs ? */
2690 url_fprintf(pb, "a=control:streamid=%d\n", i);
2692 return url_close_dyn_buf(pb, pbuffer);
2694 url_close_dyn_buf(pb, pbuffer);
2699 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2705 int content_length, len;
2706 struct sockaddr_in my_addr;
2708 /* find which url is asked */
2709 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2714 for(stream = first_stream; stream != NULL; stream = stream->next) {
2715 if (!stream->is_feed && stream->fmt == &rtp_mux &&
2716 !strcmp(path, stream->filename)) {
2720 /* no stream found */
2721 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2725 /* prepare the media description in sdp format */
2727 /* get the host IP */
2728 len = sizeof(my_addr);
2729 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2731 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2732 if (content_length < 0) {
2733 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2736 rtsp_reply_header(c, RTSP_STATUS_OK);
2737 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2738 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2739 url_fprintf(c->pb, "\r\n");
2740 put_buffer(c->pb, content, content_length);
2743 static HTTPContext *find_rtp_session(const char *session_id)
2747 if (session_id[0] == '\0')
2750 for(c = first_http_ctx; c != NULL; c = c->next) {
2751 if (!strcmp(c->session_id, session_id))
2757 RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2759 RTSPTransportField *th;
2762 for(i=0;i<h->nb_transports;i++) {
2763 th = &h->transports[i];
2764 if (th->protocol == protocol)
2770 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2774 int stream_index, port;
2779 RTSPTransportField *th;
2780 struct sockaddr_in dest_addr;
2781 RTSPActionServerSetup setup;
2783 /* find which url is asked */
2784 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2789 /* now check each stream */
2790 for(stream = first_stream; stream != NULL; stream = stream->next) {
2791 if (!stream->is_feed && stream->fmt == &rtp_mux) {
2792 /* accept aggregate filenames only if single stream */
2793 if (!strcmp(path, stream->filename)) {
2794 if (stream->nb_streams != 1) {
2795 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2802 for(stream_index = 0; stream_index < stream->nb_streams;
2804 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2805 stream->filename, stream_index);
2806 if (!strcmp(path, buf))
2811 /* no stream found */
2812 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2816 /* generate session id if needed */
2817 if (h->session_id[0] == '\0') {
2818 snprintf(h->session_id, sizeof(h->session_id),
2819 "%08x%08x", (int)random(), (int)random());
2822 /* find rtp session, and create it if none found */
2823 rtp_c = find_rtp_session(h->session_id);
2825 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id);
2827 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2831 /* open input stream */
2832 if (open_input_stream(rtp_c, "") < 0) {
2833 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2837 /* always prefer UDP */
2838 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2840 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2842 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2846 rtp_c->rtp_protocol = th->protocol;
2849 /* test if stream is OK (test needed because several SETUP needs
2850 to be done for a given file) */
2851 if (rtp_c->stream != stream) {
2852 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2856 /* test if stream is already set up */
2857 if (rtp_c->rtp_ctx[stream_index]) {
2858 rtsp_reply_error(c, RTSP_STATUS_STATE);
2862 /* check transport */
2863 th = find_transport(h, rtp_c->rtp_protocol);
2864 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2865 th->client_port_min <= 0)) {
2866 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2870 /* setup default options */
2871 setup.transport_option[0] = '\0';
2872 dest_addr = rtp_c->from_addr;
2873 dest_addr.sin_port = htons(th->client_port_min);
2875 /* add transport option if needed */
2876 if (ff_rtsp_callback) {
2877 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2878 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2879 (char *)&setup, sizeof(setup),
2880 stream->rtsp_option) < 0) {
2881 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2884 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2888 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
2889 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2893 /* now everything is OK, so we can send the connection parameters */
2894 rtsp_reply_header(c, RTSP_STATUS_OK);
2896 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2898 switch(rtp_c->rtp_protocol) {
2899 case RTSP_PROTOCOL_RTP_UDP:
2900 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2901 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2902 "client_port=%d-%d;server_port=%d-%d",
2903 th->client_port_min, th->client_port_min + 1,
2906 case RTSP_PROTOCOL_RTP_TCP:
2907 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2908 stream_index * 2, stream_index * 2 + 1);
2913 if (setup.transport_option[0] != '\0') {
2914 url_fprintf(c->pb, ";%s", setup.transport_option);
2916 url_fprintf(c->pb, "\r\n");
2919 url_fprintf(c->pb, "\r\n");
2923 /* find an rtp connection by using the session ID. Check consistency
2925 static HTTPContext *find_rtp_session_with_url(const char *url,
2926 const char *session_id)
2932 rtp_c = find_rtp_session(session_id);
2936 /* find which url is asked */
2937 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2941 if (strcmp(path, rtp_c->stream->filename) != 0)
2946 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2950 rtp_c = find_rtp_session_with_url(url, h->session_id);
2952 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2956 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2957 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2958 rtp_c->state != HTTPSTATE_READY) {
2959 rtsp_reply_error(c, RTSP_STATUS_STATE);
2963 rtp_c->state = HTTPSTATE_SEND_DATA;
2965 /* now everything is OK, so we can send the connection parameters */
2966 rtsp_reply_header(c, RTSP_STATUS_OK);
2968 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2969 url_fprintf(c->pb, "\r\n");
2972 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
2976 rtp_c = find_rtp_session_with_url(url, h->session_id);
2978 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2982 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2983 rtp_c->state != HTTPSTATE_WAIT_FEED) {
2984 rtsp_reply_error(c, RTSP_STATUS_STATE);
2988 rtp_c->state = HTTPSTATE_READY;
2990 /* now everything is OK, so we can send the connection parameters */
2991 rtsp_reply_header(c, RTSP_STATUS_OK);
2993 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2994 url_fprintf(c->pb, "\r\n");
2997 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3001 rtp_c = find_rtp_session_with_url(url, h->session_id);
3003 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3007 /* abort the session */
3008 close_connection(rtp_c);
3010 if (ff_rtsp_callback) {
3011 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
3013 rtp_c->stream->rtsp_option);
3016 /* now everything is OK, so we can send the connection parameters */
3017 rtsp_reply_header(c, RTSP_STATUS_OK);
3019 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3020 url_fprintf(c->pb, "\r\n");
3024 /********************************************************************/
3027 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3028 FFStream *stream, const char *session_id)
3030 HTTPContext *c = NULL;
3032 /* XXX: should output a warning page when coming
3033 close to the connection limit */
3034 if (nb_connections >= nb_max_connections)
3037 /* add a new connection */
3038 c = av_mallocz(sizeof(HTTPContext));
3043 c->poll_entry = NULL;
3044 c->from_addr = *from_addr;
3045 c->buffer_size = IOBUFFER_INIT_SIZE;
3046 c->buffer = av_malloc(c->buffer_size);
3051 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
3052 c->state = HTTPSTATE_READY;
3053 c->is_packetized = 1;
3054 /* protocol is shown in statistics */
3055 pstrcpy(c->protocol, sizeof(c->protocol), "RTP");
3057 current_bandwidth += stream->bandwidth;
3059 c->next = first_http_ctx;
3071 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3072 command). if dest_addr is NULL, then TCP tunneling in RTSP is
3074 static int rtp_new_av_stream(HTTPContext *c,
3075 int stream_index, struct sockaddr_in *dest_addr)
3077 AVFormatContext *ctx;
3084 /* now we can open the relevant output stream */
3085 ctx = av_mallocz(sizeof(AVFormatContext));
3088 ctx->oformat = &rtp_mux;
3090 st = av_mallocz(sizeof(AVStream));
3093 ctx->nb_streams = 1;
3094 ctx->streams[0] = st;
3096 if (!c->stream->feed ||
3097 c->stream->feed == c->stream) {
3098 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3101 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3106 /* build destination RTP address */
3107 ipaddr = inet_ntoa(dest_addr->sin_addr);
3109 /* XXX: also pass as parameter to function ? */
3110 if (c->stream->is_multicast) {
3112 ttl = c->stream->multicast_ttl;
3115 snprintf(ctx->filename, sizeof(ctx->filename),
3116 "rtp://%s:%d?multicast=1&ttl=%d",
3117 ipaddr, ntohs(dest_addr->sin_port), ttl);
3119 snprintf(ctx->filename, sizeof(ctx->filename),
3120 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3123 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3125 c->rtp_handles[stream_index] = h;
3130 http_log("%s:%d - - [%s] \"RTPSTART %s/streamid=%d\"\n",
3131 ipaddr, ntohs(dest_addr->sin_port),
3133 c->stream->filename, stream_index);
3135 /* normally, no packets should be output here, but the packet size may be checked */
3136 if (url_open_dyn_packet_buf(&ctx->pb,
3137 url_get_max_packet_size(h)) < 0) {
3138 /* XXX: close stream */
3141 if (av_write_header(ctx) < 0) {
3148 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3151 c->rtp_ctx[stream_index] = ctx;
3155 /********************************************************************/
3156 /* ffserver initialization */
3158 AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3162 fst = av_mallocz(sizeof(AVStream));
3165 fst->priv_data = av_mallocz(sizeof(FeedData));
3166 memcpy(&fst->codec, codec, sizeof(AVCodecContext));
3167 fst->codec.coded_frame = &dummy_frame;
3168 stream->streams[stream->nb_streams++] = fst;
3172 /* return the stream number in the feed */
3173 int add_av_stream(FFStream *feed,
3177 AVCodecContext *av, *av1;
3181 for(i=0;i<feed->nb_streams;i++) {
3182 st = feed->streams[i];
3184 if (av1->codec_id == av->codec_id &&
3185 av1->codec_type == av->codec_type &&
3186 av1->bit_rate == av->bit_rate) {
3188 switch(av->codec_type) {
3189 case CODEC_TYPE_AUDIO:
3190 if (av1->channels == av->channels &&
3191 av1->sample_rate == av->sample_rate)
3194 case CODEC_TYPE_VIDEO:
3195 if (av1->width == av->width &&
3196 av1->height == av->height &&
3197 av1->frame_rate == av->frame_rate &&
3198 av1->gop_size == av->gop_size)
3207 fst = add_av_stream1(feed, av);
3210 return feed->nb_streams - 1;
3215 void remove_stream(FFStream *stream)
3219 while (*ps != NULL) {
3220 if (*ps == stream) {
3228 /* specific mpeg4 handling : we extract the raw parameters */
3229 void extract_mpeg4_header(AVFormatContext *infile)
3231 int mpeg4_count, i, size;
3237 for(i=0;i<infile->nb_streams;i++) {
3238 st = infile->streams[i];
3239 if (st->codec.codec_id == CODEC_ID_MPEG4 &&
3240 st->codec.extradata == NULL) {
3247 printf("MPEG4 without extra data: trying to find header\n");
3248 while (mpeg4_count > 0) {
3249 if (av_read_packet(infile, &pkt) < 0)
3251 st = infile->streams[pkt.stream_index];
3252 if (st->codec.codec_id == CODEC_ID_MPEG4 &&
3253 st->codec.extradata == NULL) {
3254 /* fill extradata with the header */
3255 /* XXX: we make hard suppositions here ! */
3257 while (p < pkt.data + pkt.size - 4) {
3258 /* stop when vop header is found */
3259 if (p[0] == 0x00 && p[1] == 0x00 &&
3260 p[2] == 0x01 && p[3] == 0xb6) {
3261 size = p - pkt.data;
3262 // av_hex_dump(pkt.data, size);
3263 st->codec.extradata = av_malloc(size);
3264 st->codec.extradata_size = size;
3265 memcpy(st->codec.extradata, pkt.data, size);
3272 av_free_packet(&pkt);
3276 /* compute the needed AVStream for each file */
3277 void build_file_streams(void)
3279 FFStream *stream, *stream_next;
3280 AVFormatContext *infile;
3283 /* gather all streams */
3284 for(stream = first_stream; stream != NULL; stream = stream_next) {
3285 stream_next = stream->next;
3286 if (stream->stream_type == STREAM_TYPE_LIVE &&
3288 /* the stream comes from a file */
3289 /* try to open the file */
3291 if (av_open_input_file(&infile, stream->feed_filename,
3292 NULL, 0, NULL) < 0) {
3293 http_log("%s not found", stream->feed_filename);
3294 /* remove stream (no need to spend more time on it) */
3296 remove_stream(stream);
3298 /* find all the AVStreams inside and reference them in
3300 if (av_find_stream_info(infile) < 0) {
3301 http_log("Could not find codec parameters from '%s'",
3302 stream->feed_filename);
3303 av_close_input_file(infile);
3306 extract_mpeg4_header(infile);
3308 for(i=0;i<infile->nb_streams;i++) {
3309 add_av_stream1(stream, &infile->streams[i]->codec);
3311 av_close_input_file(infile);
3317 /* compute the needed AVStream for each feed */
3318 void build_feed_streams(void)
3320 FFStream *stream, *feed;
3323 /* gather all streams */
3324 for(stream = first_stream; stream != NULL; stream = stream->next) {
3325 feed = stream->feed;
3327 if (!stream->is_feed) {
3328 /* we handle a stream coming from a feed */
3329 for(i=0;i<stream->nb_streams;i++) {
3330 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3336 /* gather all streams */
3337 for(stream = first_stream; stream != NULL; stream = stream->next) {
3338 feed = stream->feed;
3340 if (stream->is_feed) {
3341 for(i=0;i<stream->nb_streams;i++) {
3342 stream->feed_streams[i] = i;
3348 /* create feed files if needed */
3349 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3352 if (url_exist(feed->feed_filename)) {
3353 /* See if it matches */
3357 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3358 /* Now see if it matches */
3359 if (s->nb_streams == feed->nb_streams) {
3361 for(i=0;i<s->nb_streams;i++) {
3363 sf = feed->streams[i];
3366 if (sf->index != ss->index ||
3368 printf("Index & Id do not match for stream %d\n", i);
3371 AVCodecContext *ccf, *ccs;
3375 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3377 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3378 printf("Codecs do not match for stream %d\n", i);
3380 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3381 printf("Codec bitrates do not match for stream %d\n", i);
3383 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3384 if (CHECK_CODEC(frame_rate) ||
3385 CHECK_CODEC(width) ||
3386 CHECK_CODEC(height)) {
3387 printf("Codec width, height and framerate do not match for stream %d\n", i);
3390 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3391 if (CHECK_CODEC(sample_rate) ||
3392 CHECK_CODEC(channels) ||
3393 CHECK_CODEC(frame_size)) {
3394 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3398 printf("Unknown codec type\n");
3407 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3408 feed->feed_filename, s->nb_streams, feed->nb_streams);
3411 av_close_input_file(s);
3413 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3414 feed->feed_filename);
3417 unlink(feed->feed_filename);
3419 if (!url_exist(feed->feed_filename)) {
3420 AVFormatContext s1, *s = &s1;
3422 /* only write the header of the ffm file */
3423 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3424 fprintf(stderr, "Could not open output feed file '%s'\n",
3425 feed->feed_filename);
3428 s->oformat = feed->fmt;
3429 s->nb_streams = feed->nb_streams;
3430 for(i=0;i<s->nb_streams;i++) {
3432 st = feed->streams[i];
3436 /* XXX: need better api */
3437 av_freep(&s->priv_data);
3440 /* get feed size and write index */
3441 fd = open(feed->feed_filename, O_RDONLY);
3443 fprintf(stderr, "Could not open output feed file '%s'\n",
3444 feed->feed_filename);
3448 feed->feed_write_index = ffm_read_write_index(fd);
3449 feed->feed_size = lseek(fd, 0, SEEK_END);
3450 /* ensure that we do not wrap before the end of file */
3451 if (feed->feed_max_size < feed->feed_size)
3452 feed->feed_max_size = feed->feed_size;
3458 /* compute the bandwidth used by each stream */
3459 static void compute_bandwidth(void)
3464 for(stream = first_stream; stream != NULL; stream = stream->next) {
3466 for(i=0;i<stream->nb_streams;i++) {
3467 AVStream *st = stream->streams[i];
3468 switch(st->codec.codec_type) {
3469 case CODEC_TYPE_AUDIO:
3470 case CODEC_TYPE_VIDEO:
3471 bandwidth += st->codec.bit_rate;
3477 stream->bandwidth = (bandwidth + 999) / 1000;
3481 static void get_arg(char *buf, int buf_size, const char **pp)
3488 while (isspace(*p)) p++;
3491 if (*p == '\"' || *p == '\'')
3503 if ((q - buf) < buf_size - 1)
3508 if (quote && *p == quote)
3513 /* add a codec and set the default parameters */
3514 void add_codec(FFStream *stream, AVCodecContext *av)
3518 /* compute default parameters */
3519 switch(av->codec_type) {
3520 case CODEC_TYPE_AUDIO:
3521 if (av->bit_rate == 0)
3522 av->bit_rate = 64000;
3523 if (av->sample_rate == 0)
3524 av->sample_rate = 22050;
3525 if (av->channels == 0)
3528 case CODEC_TYPE_VIDEO:
3529 if (av->bit_rate == 0)
3530 av->bit_rate = 64000;
3531 if (av->frame_rate == 0)
3532 av->frame_rate = 5 * FRAME_RATE_BASE;
3533 if (av->width == 0 || av->height == 0) {
3537 /* Bitrate tolerance is less for streaming */
3538 if (av->bit_rate_tolerance == 0)
3539 av->bit_rate_tolerance = av->bit_rate / 4;
3544 if (av->max_qdiff == 0)
3546 av->qcompress = 0.5;
3550 av->rc_eq = "tex^qComp";
3551 if (!av->i_quant_factor)
3552 av->i_quant_factor = -0.8;
3553 if (!av->b_quant_factor)
3554 av->b_quant_factor = 1.25;
3555 if (!av->b_quant_offset)
3556 av->b_quant_offset = 1.25;
3557 if (!av->rc_min_rate)
3558 av->rc_min_rate = av->bit_rate / 2;
3559 if (!av->rc_max_rate)
3560 av->rc_max_rate = av->bit_rate * 2;
3567 st = av_mallocz(sizeof(AVStream));
3570 stream->streams[stream->nb_streams++] = st;
3571 memcpy(&st->codec, av, sizeof(AVCodecContext));
3574 int opt_audio_codec(const char *arg)
3580 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3585 return CODEC_ID_NONE;
3591 int opt_video_codec(const char *arg)
3597 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3602 return CODEC_ID_NONE;
3608 /* simplistic plugin support */
3610 #ifdef CONFIG_HAVE_DLOPEN
3611 void load_module(const char *filename)
3614 void (*init_func)(void);
3615 dll = dlopen(filename, RTLD_NOW);
3617 fprintf(stderr, "Could not load module '%s' - %s\n",
3618 filename, dlerror());
3622 init_func = dlsym(dll, "ffserver_module_init");
3625 "%s: init function 'ffserver_module_init()' not found\n",
3634 int parse_ffconfig(const char *filename)
3641 int val, errors, line_num;
3642 FFStream **last_stream, *stream, *redirect;
3643 FFStream **last_feed, *feed;
3644 AVCodecContext audio_enc, video_enc;
3645 int audio_id, video_id;
3647 f = fopen(filename, "r");
3655 first_stream = NULL;
3656 last_stream = &first_stream;
3658 last_feed = &first_feed;
3662 audio_id = CODEC_ID_NONE;
3663 video_id = CODEC_ID_NONE;
3665 if (fgets(line, sizeof(line), f) == NULL)
3671 if (*p == '\0' || *p == '#')
3674 get_arg(cmd, sizeof(cmd), &p);
3676 if (!strcasecmp(cmd, "Port")) {
3677 get_arg(arg, sizeof(arg), &p);
3678 my_http_addr.sin_port = htons (atoi(arg));
3679 } else if (!strcasecmp(cmd, "BindAddress")) {
3680 get_arg(arg, sizeof(arg), &p);
3681 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3682 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3683 filename, line_num, arg);
3686 } else if (!strcasecmp(cmd, "NoDaemon")) {
3687 ffserver_daemon = 0;
3688 } else if (!strcasecmp(cmd, "RTSPPort")) {
3689 get_arg(arg, sizeof(arg), &p);
3690 my_rtsp_addr.sin_port = htons (atoi(arg));
3691 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3692 get_arg(arg, sizeof(arg), &p);
3693 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3694 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3695 filename, line_num, arg);
3698 } else if (!strcasecmp(cmd, "MaxClients")) {
3699 get_arg(arg, sizeof(arg), &p);
3701 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3702 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3703 filename, line_num, arg);
3706 nb_max_connections = val;
3708 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3709 get_arg(arg, sizeof(arg), &p);
3711 if (val < 10 || val > 100000) {
3712 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3713 filename, line_num, arg);
3716 max_bandwidth = val;
3718 } else if (!strcasecmp(cmd, "CustomLog")) {
3719 get_arg(logfilename, sizeof(logfilename), &p);
3720 } else if (!strcasecmp(cmd, "<Feed")) {
3721 /*********************************************/
3722 /* Feed related options */
3724 if (stream || feed) {
3725 fprintf(stderr, "%s:%d: Already in a tag\n",
3726 filename, line_num);
3728 feed = av_mallocz(sizeof(FFStream));
3729 /* add in stream list */
3730 *last_stream = feed;
3731 last_stream = &feed->next;
3732 /* add in feed list */
3734 last_feed = &feed->next_feed;
3736 get_arg(feed->filename, sizeof(feed->filename), &p);
3737 q = strrchr(feed->filename, '>');
3740 feed->fmt = guess_format("ffm", NULL, NULL);
3741 /* defaut feed file */
3742 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3743 "/tmp/%s.ffm", feed->filename);
3744 feed->feed_max_size = 5 * 1024 * 1024;
3746 feed->feed = feed; /* self feeding :-) */
3748 } else if (!strcasecmp(cmd, "Launch")) {
3752 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3754 feed->child_argv[0] = av_malloc(7);
3755 strcpy(feed->child_argv[0], "ffmpeg");
3757 for (i = 1; i < 62; i++) {
3760 get_arg(argbuf, sizeof(argbuf), &p);
3764 feed->child_argv[i] = av_malloc(strlen(argbuf + 1));
3765 strcpy(feed->child_argv[i], argbuf);
3768 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3770 snprintf(feed->child_argv[i], 256, "http://127.0.0.1:%d/%s",
3771 ntohs(my_http_addr.sin_port), feed->filename);
3773 } else if (!strcasecmp(cmd, "File")) {
3775 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3776 } else if (stream) {
3777 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3779 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3784 get_arg(arg, sizeof(arg), &p);
3786 fsize = strtod(p1, (char **)&p1);
3787 switch(toupper(*p1)) {
3792 fsize *= 1024 * 1024;
3795 fsize *= 1024 * 1024 * 1024;
3798 feed->feed_max_size = (INT64)fsize;
3800 } else if (!strcasecmp(cmd, "</Feed>")) {
3802 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3803 filename, line_num);
3807 /* Make sure that we start out clean */
3808 if (unlink(feed->feed_filename) < 0
3809 && errno != ENOENT) {
3810 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3811 filename, line_num, feed->feed_filename, strerror(errno));
3817 } else if (!strcasecmp(cmd, "<Stream")) {
3818 /*********************************************/
3819 /* Stream related options */
3821 if (stream || feed) {
3822 fprintf(stderr, "%s:%d: Already in a tag\n",
3823 filename, line_num);
3825 stream = av_mallocz(sizeof(FFStream));
3826 *last_stream = stream;
3827 last_stream = &stream->next;
3829 get_arg(stream->filename, sizeof(stream->filename), &p);
3830 q = strrchr(stream->filename, '>');
3833 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3834 memset(&audio_enc, 0, sizeof(AVCodecContext));
3835 memset(&video_enc, 0, sizeof(AVCodecContext));
3836 audio_id = CODEC_ID_NONE;
3837 video_id = CODEC_ID_NONE;
3839 audio_id = stream->fmt->audio_codec;
3840 video_id = stream->fmt->video_codec;
3843 } else if (!strcasecmp(cmd, "Feed")) {
3844 get_arg(arg, sizeof(arg), &p);
3849 while (sfeed != NULL) {
3850 if (!strcmp(sfeed->filename, arg))
3852 sfeed = sfeed->next_feed;
3855 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3856 filename, line_num, arg);
3858 stream->feed = sfeed;
3861 } else if (!strcasecmp(cmd, "Format")) {
3862 get_arg(arg, sizeof(arg), &p);
3863 if (!strcmp(arg, "status")) {
3864 stream->stream_type = STREAM_TYPE_STATUS;
3867 stream->stream_type = STREAM_TYPE_LIVE;
3868 /* jpeg cannot be used here, so use single frame jpeg */
3869 if (!strcmp(arg, "jpeg"))
3870 strcpy(arg, "singlejpeg");
3871 stream->fmt = guess_stream_format(arg, NULL, NULL);
3873 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3874 filename, line_num, arg);
3879 audio_id = stream->fmt->audio_codec;
3880 video_id = stream->fmt->video_codec;
3882 } else if (!strcasecmp(cmd, "FaviconURL")) {
3883 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3884 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3886 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3887 filename, line_num);
3890 } else if (!strcasecmp(cmd, "Author")) {
3892 get_arg(stream->author, sizeof(stream->author), &p);
3894 } else if (!strcasecmp(cmd, "Comment")) {
3896 get_arg(stream->comment, sizeof(stream->comment), &p);
3898 } else if (!strcasecmp(cmd, "Copyright")) {
3900 get_arg(stream->copyright, sizeof(stream->copyright), &p);
3902 } else if (!strcasecmp(cmd, "Title")) {
3904 get_arg(stream->title, sizeof(stream->title), &p);
3906 } else if (!strcasecmp(cmd, "Preroll")) {
3907 get_arg(arg, sizeof(arg), &p);
3909 stream->prebuffer = atof(arg) * 1000;
3911 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
3913 stream->send_on_key = 1;
3915 } else if (!strcasecmp(cmd, "AudioCodec")) {
3916 get_arg(arg, sizeof(arg), &p);
3917 audio_id = opt_audio_codec(arg);
3918 if (audio_id == CODEC_ID_NONE) {
3919 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
3920 filename, line_num, arg);
3923 } else if (!strcasecmp(cmd, "VideoCodec")) {
3924 get_arg(arg, sizeof(arg), &p);
3925 video_id = opt_video_codec(arg);
3926 if (video_id == CODEC_ID_NONE) {
3927 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
3928 filename, line_num, arg);
3931 } else if (!strcasecmp(cmd, "MaxTime")) {
3932 get_arg(arg, sizeof(arg), &p);
3934 stream->max_time = atof(arg) * 1000;
3936 } else if (!strcasecmp(cmd, "AudioBitRate")) {
3937 get_arg(arg, sizeof(arg), &p);
3939 audio_enc.bit_rate = atoi(arg) * 1000;
3941 } else if (!strcasecmp(cmd, "AudioChannels")) {
3942 get_arg(arg, sizeof(arg), &p);
3944 audio_enc.channels = atoi(arg);
3946 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
3947 get_arg(arg, sizeof(arg), &p);
3949 audio_enc.sample_rate = atoi(arg);
3951 } else if (!strcasecmp(cmd, "AudioQuality")) {
3952 get_arg(arg, sizeof(arg), &p);
3954 // audio_enc.quality = atof(arg) * 1000;
3956 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
3958 int minrate, maxrate;
3960 get_arg(arg, sizeof(arg), &p);
3962 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
3963 video_enc.rc_min_rate = minrate * 1000;
3964 video_enc.rc_max_rate = maxrate * 1000;
3966 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
3967 filename, line_num, arg);
3971 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
3973 get_arg(arg, sizeof(arg), &p);
3974 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
3976 } else if (!strcasecmp(cmd, "VideoBitRate")) {
3977 get_arg(arg, sizeof(arg), &p);
3979 video_enc.bit_rate = atoi(arg) * 1000;
3981 } else if (!strcasecmp(cmd, "VideoSize")) {
3982 get_arg(arg, sizeof(arg), &p);
3984 parse_image_size(&video_enc.width, &video_enc.height, arg);
3985 if ((video_enc.width % 16) != 0 ||
3986 (video_enc.height % 16) != 0) {
3987 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
3988 filename, line_num);
3992 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
3993 get_arg(arg, sizeof(arg), &p);
3995 video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
3997 } else if (!strcasecmp(cmd, "VideoGopSize")) {
3998 get_arg(arg, sizeof(arg), &p);
4000 video_enc.gop_size = atoi(arg);
4002 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4004 video_enc.gop_size = 1;
4006 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4008 video_enc.flags |= CODEC_FLAG_HQ;
4010 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4011 get_arg(arg, sizeof(arg), &p);
4013 video_enc.max_qdiff = atoi(arg);
4014 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4015 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4016 filename, line_num);
4020 } else if (!strcasecmp(cmd, "VideoQMax")) {
4021 get_arg(arg, sizeof(arg), &p);
4023 video_enc.qmax = atoi(arg);
4024 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4025 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4026 filename, line_num);
4030 } else if (!strcasecmp(cmd, "VideoQMin")) {
4031 get_arg(arg, sizeof(arg), &p);
4033 video_enc.qmin = atoi(arg);
4034 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4035 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4036 filename, line_num);
4040 } else if (!strcasecmp(cmd, "LumaElim")) {
4041 get_arg(arg, sizeof(arg), &p);
4043 video_enc.luma_elim_threshold = atoi(arg);
4045 } else if (!strcasecmp(cmd, "ChromaElim")) {
4046 get_arg(arg, sizeof(arg), &p);
4048 video_enc.chroma_elim_threshold = atoi(arg);
4050 } else if (!strcasecmp(cmd, "LumiMask")) {
4051 get_arg(arg, sizeof(arg), &p);
4053 video_enc.lumi_masking = atof(arg);
4055 } else if (!strcasecmp(cmd, "DarkMask")) {
4056 get_arg(arg, sizeof(arg), &p);
4058 video_enc.dark_masking = atof(arg);
4060 } else if (!strcasecmp(cmd, "NoVideo")) {
4061 video_id = CODEC_ID_NONE;
4062 } else if (!strcasecmp(cmd, "NoAudio")) {
4063 audio_id = CODEC_ID_NONE;
4064 } else if (!strcasecmp(cmd, "ACL")) {
4068 get_arg(arg, sizeof(arg), &p);
4069 if (strcasecmp(arg, "allow") == 0) {
4070 acl.action = IP_ALLOW;
4071 } else if (strcasecmp(arg, "deny") == 0) {
4072 acl.action = IP_DENY;
4074 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4075 filename, line_num, arg);
4079 get_arg(arg, sizeof(arg), &p);
4081 he = gethostbyname(arg);
4083 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4084 filename, line_num, arg);
4087 /* Only take the first */
4088 acl.first = *(struct in_addr *) he->h_addr_list[0];
4089 acl.last = acl.first;
4092 get_arg(arg, sizeof(arg), &p);
4095 he = gethostbyname(arg);
4097 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4098 filename, line_num, arg);
4101 /* Only take the first */
4102 acl.last = *(struct in_addr *) he->h_addr_list[0];
4107 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4108 IPAddressACL **naclp = 0;
4114 naclp = &stream->acl;
4118 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4119 filename, line_num);
4125 naclp = &(*naclp)->next;
4130 } else if (!strcasecmp(cmd, "RTSPOption")) {
4131 get_arg(arg, sizeof(arg), &p);
4133 av_freep(&stream->rtsp_option);
4134 /* XXX: av_strdup ? */
4135 stream->rtsp_option = av_malloc(strlen(arg) + 1);
4136 if (stream->rtsp_option) {
4137 strcpy(stream->rtsp_option, arg);
4140 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4141 get_arg(arg, sizeof(arg), &p);
4143 if (!inet_aton(arg, &stream->multicast_ip)) {
4144 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
4145 filename, line_num, arg);
4148 stream->is_multicast = 1;
4149 stream->loop = 1; /* default is looping */
4151 } else if (!strcasecmp(cmd, "MulticastPort")) {
4152 get_arg(arg, sizeof(arg), &p);
4154 stream->multicast_port = atoi(arg);
4156 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4157 get_arg(arg, sizeof(arg), &p);
4159 stream->multicast_ttl = atoi(arg);
4161 } else if (!strcasecmp(cmd, "NoLoop")) {
4165 } else if (!strcasecmp(cmd, "</Stream>")) {
4167 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4168 filename, line_num);
4171 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4172 if (audio_id != CODEC_ID_NONE) {
4173 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4174 audio_enc.codec_id = audio_id;
4175 add_codec(stream, &audio_enc);
4177 if (video_id != CODEC_ID_NONE) {
4178 video_enc.codec_type = CODEC_TYPE_VIDEO;
4179 video_enc.codec_id = video_id;
4180 add_codec(stream, &video_enc);
4184 } else if (!strcasecmp(cmd, "<Redirect")) {
4185 /*********************************************/
4187 if (stream || feed || redirect) {
4188 fprintf(stderr, "%s:%d: Already in a tag\n",
4189 filename, line_num);
4192 redirect = av_mallocz(sizeof(FFStream));
4193 *last_stream = redirect;
4194 last_stream = &redirect->next;
4196 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4197 q = strrchr(redirect->filename, '>');
4200 redirect->stream_type = STREAM_TYPE_REDIRECT;
4202 } else if (!strcasecmp(cmd, "URL")) {
4204 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4206 } else if (!strcasecmp(cmd, "</Redirect>")) {
4208 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4209 filename, line_num);
4212 if (!redirect->feed_filename[0]) {
4213 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4214 filename, line_num);
4218 } else if (!strcasecmp(cmd, "LoadModule")) {
4219 get_arg(arg, sizeof(arg), &p);
4220 #ifdef CONFIG_HAVE_DLOPEN
4223 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4224 filename, line_num, arg);
4228 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4229 filename, line_num, cmd);
4243 static void write_packet(FFCodec *ffenc,
4244 UINT8 *buf, int size)
4247 AVCodecContext *enc = &ffenc->enc;
4249 mk_header(&hdr, enc, size);
4250 wptr = http_fifo.wptr;
4251 fifo_write(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &wptr);
4252 fifo_write(&http_fifo, buf, size, &wptr);
4253 /* atomic modification of wptr */
4254 http_fifo.wptr = wptr;
4255 ffenc->data_count += size;
4256 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4262 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4263 "usage: ffserver [-L] [-h] [-f configfile]\n"
4264 "Hyper fast multi format Audio/Video streaming server\n"
4266 "-L : print the LICENCE\n"
4268 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4275 "ffserver version " FFMPEG_VERSION "\n"
4276 "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4277 "This library is free software; you can redistribute it and/or\n"
4278 "modify it under the terms of the GNU Lesser General Public\n"
4279 "License as published by the Free Software Foundation; either\n"
4280 "version 2 of the License, or (at your option) any later version.\n"
4282 "This library is distributed in the hope that it will be useful,\n"
4283 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4284 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4285 "Lesser General Public License for more details.\n"
4287 "You should have received a copy of the GNU Lesser General Public\n"
4288 "License along with this library; if not, write to the Free Software\n"
4289 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
4293 static void handle_child_exit(int sig)
4298 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4301 for (feed = first_feed; feed; feed = feed->next) {
4302 if (feed->pid == pid) {
4303 int uptime = time(0) - feed->pid_start;
4306 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4309 /* Turn off any more restarts */
4310 feed->child_argv = 0;
4316 need_to_start_children = 1;
4319 int main(int argc, char **argv)
4321 const char *config_filename;
4323 struct sigaction sigact;
4327 config_filename = "/etc/ffserver.conf";
4329 my_program_name = argv[0];
4330 my_program_dir = getcwd(0, 0);
4331 ffserver_daemon = 1;
4334 c = getopt(argc, argv, "ndLh?f:");
4350 ffserver_daemon = 0;
4353 config_filename = optarg;
4360 putenv("http_proxy"); /* Kill the http_proxy */
4362 srandom(gettime_ms() + (getpid() << 16));
4364 /* address on which the server will handle HTTP connections */
4365 my_http_addr.sin_family = AF_INET;
4366 my_http_addr.sin_port = htons (8080);
4367 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4369 /* address on which the server will handle RTSP connections */
4370 my_rtsp_addr.sin_family = AF_INET;
4371 my_rtsp_addr.sin_port = htons (5454);
4372 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4374 nb_max_connections = 5;
4375 max_bandwidth = 1000;
4376 first_stream = NULL;
4377 logfilename[0] = '\0';
4379 memset(&sigact, 0, sizeof(sigact));
4380 sigact.sa_handler = handle_child_exit;
4381 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4382 sigaction(SIGCHLD, &sigact, 0);
4384 if (parse_ffconfig(config_filename) < 0) {
4385 fprintf(stderr, "Incorrect config file - exiting.\n");
4389 build_file_streams();
4391 build_feed_streams();
4393 compute_bandwidth();
4395 /* put the process in background and detach it from its TTY */
4396 if (ffserver_daemon) {
4403 } else if (pid > 0) {
4411 open("/dev/null", O_RDWR);
4412 if (strcmp(logfilename, "-") != 0) {
4422 signal(SIGPIPE, SIG_IGN);
4424 /* open log file if needed */
4425 if (logfilename[0] != '\0') {
4426 if (!strcmp(logfilename, "-"))
4429 logfile = fopen(logfilename, "w");
4432 if (http_server() < 0) {
4433 fprintf(stderr, "Could not start server\n");