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
23 #include <netinet/in.h>
26 #include <sys/ioctl.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
34 #include <arpa/inet.h>
42 /* maximum number of simultaneous HTTP connections */
43 #define HTTP_MAX_CONNECTIONS 2000
46 HTTPSTATE_WAIT_REQUEST,
47 HTTPSTATE_SEND_HEADER,
48 HTTPSTATE_SEND_DATA_HEADER,
49 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
50 HTTPSTATE_SEND_DATA_TRAILER,
51 HTTPSTATE_RECEIVE_DATA,
52 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
53 HTTPSTATE_WAIT, /* wait before sending next packets */
54 HTTPSTATE_WAIT_SHORT, /* short wait for short term
55 bandwidth limitation */
58 RTSPSTATE_WAIT_REQUEST,
62 const char *http_state[] = {
79 #define IOBUFFER_INIT_SIZE 8192
81 /* coef for exponential mean for bitrate estimation in statistics */
84 /* timeouts are in ms */
85 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
86 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
88 #define SYNC_TIMEOUT (10 * 1000)
95 /* context associated with one connection */
96 typedef struct HTTPContext {
98 int fd; /* socket file descriptor */
99 struct sockaddr_in from_addr; /* origin */
100 struct pollfd *poll_entry; /* used when polling */
102 UINT8 *buffer_ptr, *buffer_end;
104 struct HTTPContext *next;
105 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
109 /* input format handling */
110 AVFormatContext *fmt_in;
111 long start_time; /* In milliseconds - this wraps fairly often */
112 INT64 first_pts; /* initial pts value */
113 int pts_stream_index; /* stream we choose as clock reference */
114 /* output format handling */
115 struct FFStream *stream;
116 /* -1 is invalid stream */
117 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
118 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
120 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
121 int last_packet_sent; /* true if last data packet was sent */
123 DataRateData datarate;
130 int is_packetized; /* if true, the stream is packetized */
131 int packet_stream_index; /* current stream for output in state machine */
133 /* RTSP state specific */
134 UINT8 *pb_buffer; /* XXX: use that in all the code */
136 int seq; /* RTSP sequence number */
138 /* RTP state specific */
139 enum RTSPProtocol rtp_protocol;
140 char session_id[32]; /* session id */
141 AVFormatContext *rtp_ctx[MAX_STREAMS];
142 URLContext *rtp_handles[MAX_STREAMS];
143 /* RTP short term bandwidth limitation */
144 int packet_byte_count;
145 int packet_start_time_us; /* used for short durations (a few
149 /* each generated stream is described here */
153 STREAM_TYPE_REDIRECT,
156 enum IPAddressAction {
161 typedef struct IPAddressACL {
162 struct IPAddressACL *next;
163 enum IPAddressAction action;
164 struct in_addr first;
168 /* description of each stream of the ffserver.conf file */
169 typedef struct FFStream {
170 enum StreamType stream_type;
171 char filename[1024]; /* stream filename */
172 struct FFStream *feed; /* feed we are using (can be null if
177 int prebuffer; /* Number of millseconds early to start */
178 long max_time; /* Number of milliseconds to run */
180 AVStream *streams[MAX_STREAMS];
181 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
182 char feed_filename[1024]; /* file name of the feed storage, or
183 input file name for a stream */
188 pid_t pid; /* Of ffmpeg process */
189 time_t pid_start; /* Of ffmpeg process */
191 struct FFStream *next;
192 int bandwidth; /* bandwidth, in kbits/s */
195 /* multicast specific */
197 struct in_addr multicast_ip;
198 int multicast_port; /* first port used for multicast */
200 int loop; /* if true, send the stream in loops (only meaningful if file) */
203 int feed_opened; /* true if someone is writing to the feed */
204 int is_feed; /* true if it is a feed */
207 INT64 feed_max_size; /* maximum storage size */
208 INT64 feed_write_index; /* current write position in feed (it wraps round) */
209 INT64 feed_size; /* current size of feed */
210 struct FFStream *next_feed;
213 typedef struct FeedData {
214 long long data_count;
215 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
218 struct sockaddr_in my_http_addr;
219 struct sockaddr_in my_rtsp_addr;
221 char logfilename[1024];
222 HTTPContext *first_http_ctx;
223 FFStream *first_feed; /* contains only feeds */
224 FFStream *first_stream; /* contains all streams, including feeds */
226 static void new_connection(int server_fd, int is_rtsp);
227 static void close_connection(HTTPContext *c);
230 static int handle_connection(HTTPContext *c);
231 static int http_parse_request(HTTPContext *c);
232 static int http_send_data(HTTPContext *c);
233 static void compute_stats(HTTPContext *c);
234 static int open_input_stream(HTTPContext *c, const char *info);
235 static int http_start_receive_data(HTTPContext *c);
236 static int http_receive_data(HTTPContext *c);
237 static int compute_send_delay(HTTPContext *c);
240 static int rtsp_parse_request(HTTPContext *c);
241 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
242 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
243 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
244 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
245 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
248 static int prepare_sdp_description(FFStream *stream, UINT8 **pbuffer,
249 struct in_addr my_ip);
252 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
253 FFStream *stream, const char *session_id);
254 static int rtp_new_av_stream(HTTPContext *c,
255 int stream_index, struct sockaddr_in *dest_addr);
257 static const char *my_program_name;
258 static const char *my_program_dir;
260 static int ffserver_debug;
261 static int ffserver_daemon;
262 static int no_launch;
263 static int need_to_start_children;
265 int nb_max_connections;
269 int current_bandwidth;
271 static long cur_time; // Making this global saves on passing it around everywhere
273 static long gettime_ms(void)
277 gettimeofday(&tv,NULL);
278 return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
281 static FILE *logfile = NULL;
283 static void http_log(char *fmt, ...)
289 vfprintf(logfile, fmt, ap);
295 static char *ctime1(char *buf2)
303 p = buf2 + strlen(p) - 1;
309 static void log_connection(HTTPContext *c)
316 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
317 inet_ntoa(c->from_addr.sin_addr),
318 ctime1(buf2), c->method, c->url,
319 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
322 static void update_datarate(DataRateData *drd, INT64 count)
324 if (!drd->time1 && !drd->count1) {
325 drd->time1 = drd->time2 = cur_time;
326 drd->count1 = drd->count2 = count;
328 if (cur_time - drd->time2 > 5000) {
329 drd->time1 = drd->time2;
330 drd->count1 = drd->count2;
331 drd->time2 = cur_time;
337 /* In bytes per second */
338 static int compute_datarate(DataRateData *drd, INT64 count)
340 if (cur_time == drd->time1)
343 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
346 static int get_longterm_datarate(DataRateData *drd, INT64 count)
348 /* You get the first 3 seconds flat out */
349 if (cur_time - drd->time1 < 3000)
351 return compute_datarate(drd, count);
355 static void start_children(FFStream *feed)
360 for (; feed; feed = feed->next) {
361 if (feed->child_argv && !feed->pid) {
362 feed->pid_start = time(0);
367 fprintf(stderr, "Unable to create children\n");
376 for (i = 3; i < 256; i++) {
380 if (!ffserver_debug) {
381 i = open("/dev/null", O_RDWR);
390 pstrcpy(pathname, sizeof(pathname), my_program_name);
392 slash = strrchr(pathname, '/');
398 strcpy(slash, "ffmpeg");
400 /* This is needed to make relative pathnames work */
401 chdir(my_program_dir);
403 execvp(pathname, feed->child_argv);
411 /* open a listening socket */
412 static int socket_open_listen(struct sockaddr_in *my_addr)
416 server_fd = socket(AF_INET,SOCK_STREAM,0);
423 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
425 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
431 if (listen (server_fd, 5) < 0) {
436 fcntl(server_fd, F_SETFL, O_NONBLOCK);
441 /* start all multicast streams */
442 static void start_multicast(void)
447 struct sockaddr_in dest_addr;
448 int default_port, stream_index;
451 for(stream = first_stream; stream != NULL; stream = stream->next) {
452 if (stream->is_multicast) {
453 /* open the RTP connection */
454 snprintf(session_id, sizeof(session_id),
455 "%08x%08x", (int)random(), (int)random());
457 /* choose a port if none given */
458 if (stream->multicast_port == 0) {
459 stream->multicast_port = default_port;
463 dest_addr.sin_family = AF_INET;
464 dest_addr.sin_addr = stream->multicast_ip;
465 dest_addr.sin_port = htons(stream->multicast_port);
467 rtp_c = rtp_new_connection(&dest_addr, stream, session_id);
471 if (open_input_stream(rtp_c, "") < 0) {
472 fprintf(stderr, "Could not open input stream for stream '%s'\n",
477 rtp_c->rtp_protocol = RTSP_PROTOCOL_RTP_UDP_MULTICAST;
479 /* open each RTP stream */
480 for(stream_index = 0; stream_index < stream->nb_streams;
482 dest_addr.sin_port = htons(stream->multicast_port +
484 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
485 fprintf(stderr, "Could not open input stream %d for stream '%s'\n",
486 stream_index, stream->filename);
491 /* change state to send data */
492 rtp_c->state = HTTPSTATE_SEND_DATA;
497 /* main loop of the http server */
498 static int http_server(void)
500 int server_fd, ret, rtsp_server_fd, delay, delay1;
501 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
502 HTTPContext *c, *c_next;
504 server_fd = socket_open_listen(&my_http_addr);
508 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
509 if (rtsp_server_fd < 0)
512 http_log("ffserver started.\n");
514 start_children(first_feed);
516 first_http_ctx = NULL;
518 first_http_ctx = NULL;
523 poll_entry = poll_table;
524 poll_entry->fd = server_fd;
525 poll_entry->events = POLLIN;
528 poll_entry->fd = rtsp_server_fd;
529 poll_entry->events = POLLIN;
532 /* wait for events on each HTTP handle */
539 case HTTPSTATE_SEND_HEADER:
540 case RTSPSTATE_SEND_REPLY:
541 c->poll_entry = poll_entry;
543 poll_entry->events = POLLOUT;
546 case HTTPSTATE_SEND_DATA_HEADER:
547 case HTTPSTATE_SEND_DATA:
548 case HTTPSTATE_SEND_DATA_TRAILER:
549 if (!c->is_packetized) {
550 /* for TCP, we output as much as we can (may need to put a limit) */
551 c->poll_entry = poll_entry;
553 poll_entry->events = POLLOUT;
556 /* not strictly correct, but currently cannot add
557 more than one fd in poll entry */
561 case HTTPSTATE_WAIT_REQUEST:
562 case HTTPSTATE_RECEIVE_DATA:
563 case HTTPSTATE_WAIT_FEED:
564 case RTSPSTATE_WAIT_REQUEST:
565 /* need to catch errors */
566 c->poll_entry = poll_entry;
568 poll_entry->events = POLLIN;/* Maybe this will work */
572 c->poll_entry = NULL;
573 delay1 = compute_send_delay(c);
577 case HTTPSTATE_WAIT_SHORT:
578 c->poll_entry = NULL;
579 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
584 c->poll_entry = NULL;
590 /* wait for an event on one connection. We poll at least every
591 second to handle timeouts */
593 ret = poll(poll_table, poll_entry - poll_table, delay);
596 cur_time = gettime_ms();
598 if (need_to_start_children) {
599 need_to_start_children = 0;
600 start_children(first_feed);
603 /* now handle the events */
604 for(c = first_http_ctx; c != NULL; c = c_next) {
606 if (handle_connection(c) < 0) {
607 /* close and free the connection */
613 poll_entry = poll_table;
614 /* new HTTP connection request ? */
615 if (poll_entry->revents & POLLIN) {
616 new_connection(server_fd, 0);
619 /* new RTSP connection request ? */
620 if (poll_entry->revents & POLLIN) {
621 new_connection(rtsp_server_fd, 1);
626 /* start waiting for a new HTTP/RTSP request */
627 static void start_wait_request(HTTPContext *c, int is_rtsp)
629 c->buffer_ptr = c->buffer;
630 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
633 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
634 c->state = RTSPSTATE_WAIT_REQUEST;
636 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
637 c->state = HTTPSTATE_WAIT_REQUEST;
641 static void new_connection(int server_fd, int is_rtsp)
643 struct sockaddr_in from_addr;
645 HTTPContext *c = NULL;
647 len = sizeof(from_addr);
648 fd = accept(server_fd, (struct sockaddr *)&from_addr,
652 fcntl(fd, F_SETFL, O_NONBLOCK);
654 /* XXX: should output a warning page when coming
655 close to the connection limit */
656 if (nb_connections >= nb_max_connections)
659 /* add a new connection */
660 c = av_mallocz(sizeof(HTTPContext));
664 c->next = first_http_ctx;
667 c->poll_entry = NULL;
668 c->from_addr = from_addr;
669 c->buffer_size = IOBUFFER_INIT_SIZE;
670 c->buffer = av_malloc(c->buffer_size);
675 start_wait_request(c, is_rtsp);
687 static void close_connection(HTTPContext *c)
689 HTTPContext **cp, *c1;
691 AVFormatContext *ctx;
695 /* remove connection from list */
696 cp = &first_http_ctx;
697 while ((*cp) != NULL) {
706 /* remove connection associated resources */
710 /* close each frame parser */
711 for(i=0;i<c->fmt_in->nb_streams;i++) {
712 st = c->fmt_in->streams[i];
713 if (st->codec.codec) {
714 avcodec_close(&st->codec);
717 av_close_input_file(c->fmt_in);
720 /* free RTP output streams if any */
723 nb_streams = c->stream->nb_streams;
725 for(i=0;i<nb_streams;i++) {
728 av_write_trailer(ctx);
731 h = c->rtp_handles[i];
738 current_bandwidth -= c->stream->bandwidth;
739 av_freep(&c->pb_buffer);
745 static int handle_connection(HTTPContext *c)
750 case HTTPSTATE_WAIT_REQUEST:
751 case RTSPSTATE_WAIT_REQUEST:
753 if ((c->timeout - cur_time) < 0)
755 if (c->poll_entry->revents & (POLLERR | POLLHUP))
758 /* no need to read if no events */
759 if (!(c->poll_entry->revents & POLLIN))
762 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
764 if (errno != EAGAIN && errno != EINTR)
766 } else if (len == 0) {
769 /* search for end of request. XXX: not fully correct since garbage could come after the end */
771 c->buffer_ptr += len;
773 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
774 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
775 /* request found : parse it and reply */
776 if (c->state == HTTPSTATE_WAIT_REQUEST) {
777 ret = http_parse_request(c);
779 ret = rtsp_parse_request(c);
783 } else if (ptr >= c->buffer_end) {
784 /* request too long: cannot do anything */
790 case HTTPSTATE_SEND_HEADER:
791 if (c->poll_entry->revents & (POLLERR | POLLHUP))
794 /* no need to write if no events */
795 if (!(c->poll_entry->revents & POLLOUT))
797 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
799 if (errno != EAGAIN && errno != EINTR) {
800 /* error : close connection */
801 av_freep(&c->pb_buffer);
805 c->buffer_ptr += len;
807 c->stream->bytes_served += len;
808 c->data_count += len;
809 if (c->buffer_ptr >= c->buffer_end) {
810 av_freep(&c->pb_buffer);
815 /* all the buffer was sent : synchronize to the incoming stream */
816 c->state = HTTPSTATE_SEND_DATA_HEADER;
817 c->buffer_ptr = c->buffer_end = c->buffer;
822 case HTTPSTATE_SEND_DATA:
823 case HTTPSTATE_SEND_DATA_HEADER:
824 case HTTPSTATE_SEND_DATA_TRAILER:
825 /* for packetized output, we consider we can always write (the
826 input streams sets the speed). It may be better to verify
827 that we do not rely too much on the kernel queues */
828 if (!c->is_packetized) {
829 if (c->poll_entry->revents & (POLLERR | POLLHUP))
832 /* no need to read if no events */
833 if (!(c->poll_entry->revents & POLLOUT))
836 if (http_send_data(c) < 0)
839 case HTTPSTATE_RECEIVE_DATA:
840 /* no need to read if no events */
841 if (c->poll_entry->revents & (POLLERR | POLLHUP))
843 if (!(c->poll_entry->revents & POLLIN))
845 if (http_receive_data(c) < 0)
848 case HTTPSTATE_WAIT_FEED:
849 /* no need to read if no events */
850 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
853 /* nothing to do, we'll be waken up by incoming feed packets */
857 /* if the delay expired, we can send new packets */
858 if (compute_send_delay(c) <= 0)
859 c->state = HTTPSTATE_SEND_DATA;
861 case HTTPSTATE_WAIT_SHORT:
862 /* just return back to send data */
863 c->state = HTTPSTATE_SEND_DATA;
866 case RTSPSTATE_SEND_REPLY:
867 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
868 av_freep(&c->pb_buffer);
871 /* no need to write if no events */
872 if (!(c->poll_entry->revents & POLLOUT))
874 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
876 if (errno != EAGAIN && errno != EINTR) {
877 /* error : close connection */
878 av_freep(&c->pb_buffer);
882 c->buffer_ptr += len;
883 c->data_count += len;
884 if (c->buffer_ptr >= c->buffer_end) {
885 /* all the buffer was sent : wait for a new request */
886 av_freep(&c->pb_buffer);
887 start_wait_request(c, 1);
891 case HTTPSTATE_READY:
900 static int extract_rates(char *rates, int ratelen, const char *request)
904 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
905 if (strncasecmp(p, "Pragma:", 7) == 0) {
906 const char *q = p + 7;
908 while (*q && *q != '\n' && isspace(*q))
911 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
917 memset(rates, 0xff, ratelen);
920 while (*q && *q != '\n' && *q != ':')
923 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
927 if (stream_no < ratelen && stream_no >= 0) {
928 rates[stream_no] = rate_no;
931 while (*q && *q != '\n' && !isspace(*q))
948 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
951 int best_bitrate = 100000000;
954 for (i = 0; i < feed->nb_streams; i++) {
955 AVCodecContext *feed_codec = &feed->streams[i]->codec;
957 if (feed_codec->codec_id != codec->codec_id ||
958 feed_codec->sample_rate != codec->sample_rate ||
959 feed_codec->width != codec->width ||
960 feed_codec->height != codec->height) {
964 /* Potential stream */
966 /* We want the fastest stream less than bit_rate, or the slowest
967 * faster than bit_rate
970 if (feed_codec->bit_rate <= bit_rate) {
971 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
972 best_bitrate = feed_codec->bit_rate;
976 if (feed_codec->bit_rate < best_bitrate) {
977 best_bitrate = feed_codec->bit_rate;
986 static int modify_current_stream(HTTPContext *c, char *rates)
989 FFStream *req = c->stream;
990 int action_required = 0;
992 for (i = 0; i < req->nb_streams; i++) {
993 AVCodecContext *codec = &req->streams[i]->codec;
997 c->switch_feed_streams[i] = req->feed_streams[i];
1000 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1003 /* Wants off or slow */
1004 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1006 /* This doesn't work well when it turns off the only stream! */
1007 c->switch_feed_streams[i] = -2;
1008 c->feed_streams[i] = -2;
1013 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1014 action_required = 1;
1017 return action_required;
1021 static void do_switch_stream(HTTPContext *c, int i)
1023 if (c->switch_feed_streams[i] >= 0) {
1025 c->feed_streams[i] = c->switch_feed_streams[i];
1028 /* Now update the stream */
1030 c->switch_feed_streams[i] = -1;
1033 /* XXX: factorize in utils.c ? */
1034 /* XXX: take care with different space meaning */
1035 static void skip_spaces(const char **pp)
1039 while (*p == ' ' || *p == '\t')
1044 static void get_word(char *buf, int buf_size, const char **pp)
1052 while (!isspace(*p) && *p != '\0') {
1053 if ((q - buf) < buf_size - 1)
1062 static int validate_acl(FFStream *stream, HTTPContext *c)
1064 enum IPAddressAction last_action = IP_DENY;
1066 struct in_addr *src = &c->from_addr.sin_addr;
1068 for (acl = stream->acl; acl; acl = acl->next) {
1069 if (src->s_addr >= acl->first.s_addr && src->s_addr <= acl->last.s_addr) {
1070 return (acl->action == IP_ALLOW) ? 1 : 0;
1072 last_action = acl->action;
1075 /* Nothing matched, so return not the last action */
1076 return (last_action == IP_DENY) ? 1 : 0;
1079 /* compute the real filename of a file by matching it without its
1080 extensions to all the stream filenames */
1081 static void compute_real_filename(char *filename, int max_size)
1088 /* compute filename by matching without the file extensions */
1089 pstrcpy(file1, sizeof(file1), filename);
1090 p = strrchr(file1, '.');
1093 for(stream = first_stream; stream != NULL; stream = stream->next) {
1094 pstrcpy(file2, sizeof(file2), stream->filename);
1095 p = strrchr(file2, '.');
1098 if (!strcmp(file1, file2)) {
1099 pstrcpy(filename, max_size, stream->filename);
1114 /* parse http request and prepare header */
1115 static int http_parse_request(HTTPContext *c)
1119 enum RedirType redir_type;
1121 char info[1024], *filename;
1125 const char *mime_type;
1129 char *useragent = 0;
1132 get_word(cmd, sizeof(cmd), (const char **)&p);
1133 pstrcpy(c->method, sizeof(c->method), cmd);
1135 if (!strcmp(cmd, "GET"))
1137 else if (!strcmp(cmd, "POST"))
1142 get_word(url, sizeof(url), (const char **)&p);
1143 pstrcpy(c->url, sizeof(c->url), url);
1145 get_word(protocol, sizeof(protocol), (const char **)&p);
1146 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1149 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
1151 /* find the filename and the optional info string in the request */
1158 pstrcpy(info, sizeof(info), p);
1164 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1165 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1167 if (*useragent && *useragent != '\n' && isspace(*useragent))
1171 p = strchr(p, '\n');
1178 redir_type = REDIR_NONE;
1179 if (match_ext(filename, "asx")) {
1180 redir_type = REDIR_ASX;
1181 filename[strlen(filename)-1] = 'f';
1182 } else if (match_ext(filename, ".asf") &&
1183 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1184 /* if this isn't WMP or lookalike, return the redirector file */
1185 redir_type = REDIR_ASF;
1186 } else if (match_ext(filename, "rpm,ram")) {
1187 redir_type = REDIR_RAM;
1188 strcpy(filename + strlen(filename)-2, "m");
1189 } else if (match_ext(filename, "rtsp")) {
1190 redir_type = REDIR_RTSP;
1191 compute_real_filename(filename, sizeof(url) - 1);
1192 } else if (match_ext(filename, "sdp")) {
1193 redir_type = REDIR_SDP;
1194 compute_real_filename(filename, sizeof(url) - 1);
1197 stream = first_stream;
1198 while (stream != NULL) {
1199 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1201 stream = stream->next;
1203 if (stream == NULL) {
1204 sprintf(msg, "File '%s' not found", url);
1209 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1210 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1212 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1213 c->http_error = 301;
1215 q += sprintf(q, "HTTP/1.0 301 Moved\r\n");
1216 q += sprintf(q, "Location: %s\r\n", stream->feed_filename);
1217 q += sprintf(q, "Content-type: text/html\r\n");
1218 q += sprintf(q, "\r\n");
1219 q += sprintf(q, "<html><head><title>Moved</title></head><body>\r\n");
1220 q += sprintf(q, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1221 q += sprintf(q, "</body></html>\r\n");
1223 /* prepare output buffer */
1224 c->buffer_ptr = c->buffer;
1226 c->state = HTTPSTATE_SEND_HEADER;
1230 /* If this is WMP, get the rate information */
1231 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1232 if (modify_current_stream(c, ratebuf)) {
1233 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1234 if (c->switch_feed_streams[i] >= 0)
1235 do_switch_stream(c, i);
1240 if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1241 current_bandwidth += stream->bandwidth;
1244 if (post == 0 && max_bandwidth < current_bandwidth) {
1245 c->http_error = 200;
1247 q += sprintf(q, "HTTP/1.0 200 Server too busy\r\n");
1248 q += sprintf(q, "Content-type: text/html\r\n");
1249 q += sprintf(q, "\r\n");
1250 q += sprintf(q, "<html><head><title>Too busy</title></head><body>\r\n");
1251 q += sprintf(q, "The server is too busy to serve your request at this time.<p>\r\n");
1252 q += sprintf(q, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
1253 current_bandwidth, max_bandwidth);
1254 q += sprintf(q, "</body></html>\r\n");
1256 /* prepare output buffer */
1257 c->buffer_ptr = c->buffer;
1259 c->state = HTTPSTATE_SEND_HEADER;
1263 if (redir_type != REDIR_NONE) {
1266 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1267 if (strncasecmp(p, "Host:", 5) == 0) {
1271 p = strchr(p, '\n');
1282 while (isspace(*hostinfo))
1285 eoh = strchr(hostinfo, '\n');
1287 if (eoh[-1] == '\r')
1290 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1291 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1292 hostbuf[eoh - hostinfo] = 0;
1294 c->http_error = 200;
1296 switch(redir_type) {
1298 q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n");
1299 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1300 q += sprintf(q, "\r\n");
1301 q += sprintf(q, "<ASX Version=\"3\">\r\n");
1302 q += sprintf(q, "<!-- Autogenerated by ffserver -->\r\n");
1303 q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1304 hostbuf, filename, info);
1305 q += sprintf(q, "</ASX>\r\n");
1308 q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n");
1309 q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n");
1310 q += sprintf(q, "\r\n");
1311 q += sprintf(q, "# Autogenerated by ffserver\r\n");
1312 q += sprintf(q, "http://%s/%s%s\r\n",
1313 hostbuf, filename, info);
1316 q += sprintf(q, "HTTP/1.0 200 ASF Redirect follows\r\n");
1317 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1318 q += sprintf(q, "\r\n");
1319 q += sprintf(q, "[Reference]\r\n");
1320 q += sprintf(q, "Ref1=http://%s/%s%s\r\n",
1321 hostbuf, filename, info);
1325 char hostname[256], *p;
1326 /* extract only hostname */
1327 pstrcpy(hostname, sizeof(hostname), hostbuf);
1328 p = strrchr(hostname, ':');
1331 q += sprintf(q, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1332 /* XXX: incorrect mime type ? */
1333 q += sprintf(q, "Content-type: application/x-rtsp\r\n");
1334 q += sprintf(q, "\r\n");
1335 q += sprintf(q, "rtsp://%s:%d/%s\r\n",
1336 hostname, ntohs(my_rtsp_addr.sin_port),
1343 int sdp_data_size, len;
1344 struct sockaddr_in my_addr;
1346 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1347 q += sprintf(q, "Content-type: application/sdp\r\n");
1348 q += sprintf(q, "\r\n");
1350 len = sizeof(my_addr);
1351 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1353 /* XXX: should use a dynamic buffer */
1354 sdp_data_size = prepare_sdp_description(stream,
1357 if (sdp_data_size > 0) {
1358 memcpy(q, sdp_data, sdp_data_size);
1370 /* prepare output buffer */
1371 c->buffer_ptr = c->buffer;
1373 c->state = HTTPSTATE_SEND_HEADER;
1379 sprintf(msg, "ASX/RAM file not handled");
1383 stream->conns_served++;
1385 /* XXX: add there authenticate and IP match */
1388 /* if post, it means a feed is being sent */
1389 if (!stream->is_feed) {
1390 /* However it might be a status report from WMP! Lets log the data
1391 * as it might come in handy one day
1396 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1397 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1401 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1402 client_id = strtol(p + 18, 0, 10);
1404 p = strchr(p, '\n');
1412 char *eol = strchr(logline, '\n');
1417 if (eol[-1] == '\r')
1419 http_log("%.*s\n", eol - logline, logline);
1420 c->suppress_log = 1;
1425 http_log("\nGot request:\n%s\n", c->buffer);
1428 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1431 /* Now we have to find the client_id */
1432 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1433 if (wmpc->wmp_client_id == client_id)
1438 if (modify_current_stream(wmpc, ratebuf)) {
1439 wmpc->switch_pending = 1;
1444 sprintf(msg, "POST command not handled");
1447 if (http_start_receive_data(c) < 0) {
1448 sprintf(msg, "could not open feed");
1452 c->state = HTTPSTATE_RECEIVE_DATA;
1457 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1458 http_log("\nGot request:\n%s\n", c->buffer);
1462 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1465 /* open input stream */
1466 if (open_input_stream(c, info) < 0) {
1467 sprintf(msg, "Input stream corresponding to '%s' not found", url);
1471 /* prepare http header */
1473 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1474 mime_type = c->stream->fmt->mime_type;
1476 mime_type = "application/x-octet_stream";
1477 q += sprintf(q, "Pragma: no-cache\r\n");
1479 /* for asf, we need extra headers */
1480 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1481 /* Need to allocate a client id */
1483 c->wmp_client_id = random() & 0x7fffffff;
1485 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);
1486 mime_type = "application/octet-stream";
1488 q += sprintf(q, "Content-Type: %s\r\n", mime_type);
1489 q += sprintf(q, "\r\n");
1491 /* prepare output buffer */
1493 c->buffer_ptr = c->buffer;
1495 c->state = HTTPSTATE_SEND_HEADER;
1498 c->http_error = 404;
1500 q += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
1501 q += sprintf(q, "Content-type: %s\r\n", "text/html");
1502 q += sprintf(q, "\r\n");
1503 q += sprintf(q, "<HTML>\n");
1504 q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1505 q += sprintf(q, "<BODY>%s</BODY>\n", msg);
1506 q += sprintf(q, "</HTML>\n");
1508 /* prepare output buffer */
1509 c->buffer_ptr = c->buffer;
1511 c->state = HTTPSTATE_SEND_HEADER;
1515 c->http_error = 200; /* horrible : we use this value to avoid
1516 going to the send data state */
1517 c->state = HTTPSTATE_SEND_HEADER;
1521 static void fmt_bytecount(ByteIOContext *pb, INT64 count)
1523 static const char *suffix = " kMGTP";
1526 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1529 url_fprintf(pb, "%lld%c", count, *s);
1532 static void compute_stats(HTTPContext *c)
1539 ByteIOContext pb1, *pb = &pb1;
1541 if (url_open_dyn_buf(pb) < 0) {
1542 /* XXX: return an error ? */
1543 c->buffer_ptr = c->buffer;
1544 c->buffer_end = c->buffer;
1548 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1549 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1550 url_fprintf(pb, "Pragma: no-cache\r\n");
1551 url_fprintf(pb, "\r\n");
1553 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1554 if (c->stream->feed_filename) {
1555 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1557 url_fprintf(pb, "</HEAD>\n<BODY>");
1558 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1560 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1561 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1562 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");
1563 stream = first_stream;
1564 while (stream != NULL) {
1565 char sfilename[1024];
1568 if (stream->feed != stream) {
1569 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1570 eosf = sfilename + strlen(sfilename);
1571 if (eosf - sfilename >= 4) {
1572 if (strcmp(eosf - 4, ".asf") == 0) {
1573 strcpy(eosf - 4, ".asx");
1574 } else if (strcmp(eosf - 3, ".rm") == 0) {
1575 strcpy(eosf - 3, ".ram");
1576 } else if (stream->fmt == &rtp_mux) {
1577 /* generate a sample RTSP director if
1578 unicast. Generate an SDP redirector if
1580 eosf = strrchr(sfilename, '.');
1582 eosf = sfilename + strlen(sfilename);
1583 if (stream->is_multicast)
1584 strcpy(eosf, ".sdp");
1586 strcpy(eosf, ".rtsp");
1590 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1591 sfilename, stream->filename);
1592 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1593 stream->conns_served);
1594 fmt_bytecount(pb, stream->bytes_served);
1595 switch(stream->stream_type) {
1596 case STREAM_TYPE_LIVE:
1598 int audio_bit_rate = 0;
1599 int video_bit_rate = 0;
1600 const char *audio_codec_name = "";
1601 const char *video_codec_name = "";
1602 const char *audio_codec_name_extra = "";
1603 const char *video_codec_name_extra = "";
1605 for(i=0;i<stream->nb_streams;i++) {
1606 AVStream *st = stream->streams[i];
1607 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1608 switch(st->codec.codec_type) {
1609 case CODEC_TYPE_AUDIO:
1610 audio_bit_rate += st->codec.bit_rate;
1612 if (*audio_codec_name)
1613 audio_codec_name_extra = "...";
1614 audio_codec_name = codec->name;
1617 case CODEC_TYPE_VIDEO:
1618 video_bit_rate += st->codec.bit_rate;
1620 if (*video_codec_name)
1621 video_codec_name_extra = "...";
1622 video_codec_name = codec->name;
1629 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",
1632 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1633 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1635 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1637 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1639 url_fprintf(pb, "\n");
1643 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1647 stream = stream->next;
1649 url_fprintf(pb, "</TABLE>\n");
1651 stream = first_stream;
1652 while (stream != NULL) {
1653 if (stream->feed == stream) {
1654 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1656 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1658 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1663 /* This is somewhat linux specific I guess */
1664 snprintf(ps_cmd, sizeof(ps_cmd),
1665 "ps -o \"%%cpu,cputime\" --no-headers %d",
1668 pid_stat = popen(ps_cmd, "r");
1673 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1675 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1683 url_fprintf(pb, "<p>");
1685 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");
1687 for (i = 0; i < stream->nb_streams; i++) {
1688 AVStream *st = stream->streams[i];
1689 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1690 char *type = "unknown";
1691 char parameters[64];
1695 switch(st->codec.codec_type) {
1696 case CODEC_TYPE_AUDIO:
1699 case CODEC_TYPE_VIDEO:
1701 sprintf(parameters, "%dx%d, q=%d-%d, fps=%d", st->codec.width, st->codec.height,
1702 st->codec.qmin, st->codec.qmax, st->codec.frame_rate / FRAME_RATE_BASE);
1707 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1708 i, type, st->codec.bit_rate/1000, codec ? codec->name : "", parameters);
1710 url_fprintf(pb, "</table>\n");
1713 stream = stream->next;
1719 AVCodecContext *enc;
1723 stream = first_feed;
1724 while (stream != NULL) {
1725 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1726 url_fprintf(pb, "<TABLE>\n");
1727 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1728 for(i=0;i<stream->nb_streams;i++) {
1729 AVStream *st = stream->streams[i];
1730 FeedData *fdata = st->priv_data;
1733 avcodec_string(buf, sizeof(buf), enc);
1734 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1735 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1736 avg /= enc->frame_size;
1737 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1738 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1740 url_fprintf(pb, "</TABLE>\n");
1741 stream = stream->next_feed;
1746 /* connection status */
1747 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1749 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1750 nb_connections, nb_max_connections);
1752 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1753 current_bandwidth, max_bandwidth);
1755 url_fprintf(pb, "<TABLE>\n");
1756 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");
1757 c1 = first_http_ctx;
1759 while (c1 != NULL) {
1765 for (j = 0; j < c1->stream->nb_streams; j++) {
1766 if (!c1->stream->feed) {
1767 bitrate += c1->stream->streams[j]->codec.bit_rate;
1769 if (c1->feed_streams[j] >= 0) {
1770 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec.bit_rate;
1777 p = inet_ntoa(c1->from_addr.sin_addr);
1778 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1780 c1->stream ? c1->stream->filename : "",
1781 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1784 http_state[c1->state]);
1785 fmt_bytecount(pb, bitrate);
1786 url_fprintf(pb, "<td align=right>");
1787 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1788 url_fprintf(pb, "<td align=right>");
1789 fmt_bytecount(pb, c1->data_count);
1790 url_fprintf(pb, "\n");
1793 url_fprintf(pb, "</TABLE>\n");
1798 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1799 url_fprintf(pb, "</BODY>\n</HTML>\n");
1801 len = url_close_dyn_buf(pb, &c->pb_buffer);
1802 c->buffer_ptr = c->pb_buffer;
1803 c->buffer_end = c->pb_buffer + len;
1806 /* check if the parser needs to be opened for stream i */
1807 static void open_parser(AVFormatContext *s, int i)
1809 AVStream *st = s->streams[i];
1812 if (!st->codec.codec) {
1813 codec = avcodec_find_decoder(st->codec.codec_id);
1814 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1815 st->codec.parse_only = 1;
1816 if (avcodec_open(&st->codec, codec) < 0) {
1817 st->codec.parse_only = 0;
1823 static int open_input_stream(HTTPContext *c, const char *info)
1826 char input_filename[1024];
1831 /* find file name */
1832 if (c->stream->feed) {
1833 strcpy(input_filename, c->stream->feed->feed_filename);
1834 buf_size = FFM_PACKET_SIZE;
1835 /* compute position (absolute time) */
1836 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1837 stream_pos = parse_date(buf, 0);
1838 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1839 int prebuffer = strtol(buf, 0, 10);
1840 stream_pos = av_gettime() - prebuffer * (INT64)1000000;
1842 stream_pos = av_gettime() - c->stream->prebuffer * (INT64)1000;
1845 strcpy(input_filename, c->stream->feed_filename);
1847 /* compute position (relative time) */
1848 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1849 stream_pos = parse_date(buf, 1);
1854 if (input_filename[0] == '\0')
1858 { time_t when = stream_pos / 1000000;
1859 http_log("Stream pos = %lld, time=%s", stream_pos, ctime(&when));
1864 if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0) {
1865 http_log("%s not found", input_filename);
1870 /* open each parser */
1871 for(i=0;i<s->nb_streams;i++)
1874 /* choose stream as clock source (we favorize video stream if
1875 present) for packet sending */
1876 c->pts_stream_index = 0;
1877 for(i=0;i<c->stream->nb_streams;i++) {
1878 if (c->pts_stream_index == 0 &&
1879 c->stream->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO) {
1880 c->pts_stream_index = i;
1884 if (c->fmt_in->iformat->read_seek) {
1885 c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
1887 /* set the start time (needed for maxtime and RTP packet timing) */
1888 c->start_time = cur_time;
1889 c->first_pts = AV_NOPTS_VALUE;
1893 /* currently desactivated because the new PTS handling is not
1895 //#define AV_READ_FRAME
1896 #ifdef AV_READ_FRAME
1898 /* XXX: generalize that in ffmpeg for picture/audio/data. Currently
1899 the return packet MUST NOT be freed */
1900 int av_read_frame(AVFormatContext *s, AVPacket *pkt)
1903 int len, ret, old_nb_streams, i;
1905 /* see if remaining frames must be parsed */
1907 if (s->cur_len > 0) {
1908 st = s->streams[s->cur_pkt.stream_index];
1909 len = avcodec_parse_frame(&st->codec, &pkt->data, &pkt->size,
1910 s->cur_ptr, s->cur_len);
1912 /* error: get next packet */
1918 /* init pts counter if not done */
1919 if (st->pts.den == 0) {
1920 switch(st->codec.codec_type) {
1921 case CODEC_TYPE_AUDIO:
1922 st->pts_incr = (INT64)s->pts_den;
1923 av_frac_init(&st->pts, st->pts.val, 0,
1924 (INT64)s->pts_num * st->codec.sample_rate);
1926 case CODEC_TYPE_VIDEO:
1927 st->pts_incr = (INT64)s->pts_den * FRAME_RATE_BASE;
1928 av_frac_init(&st->pts, st->pts.val, 0,
1929 (INT64)s->pts_num * st->codec.frame_rate);
1936 /* a frame was read: return it */
1937 pkt->pts = st->pts.val;
1939 printf("add pts=%Lx num=%Lx den=%Lx incr=%Lx\n",
1940 st->pts.val, st->pts.num, st->pts.den, st->pts_incr);
1942 switch(st->codec.codec_type) {
1943 case CODEC_TYPE_AUDIO:
1944 av_frac_add(&st->pts, st->pts_incr * st->codec.frame_size);
1946 case CODEC_TYPE_VIDEO:
1947 av_frac_add(&st->pts, st->pts_incr);
1952 pkt->stream_index = s->cur_pkt.stream_index;
1953 /* we use the codec indication because it is
1954 more accurate than the demux flags */
1956 if (st->codec.key_frame)
1957 pkt->flags |= PKT_FLAG_KEY;
1962 /* free previous packet */
1963 av_free_packet(&s->cur_pkt);
1965 old_nb_streams = s->nb_streams;
1966 ret = av_read_packet(s, &s->cur_pkt);
1969 /* open parsers for each new streams */
1970 for(i = old_nb_streams; i < s->nb_streams; i++)
1972 st = s->streams[s->cur_pkt.stream_index];
1974 /* update current pts (XXX: dts handling) from packet, or
1975 use current pts if none given */
1976 if (s->cur_pkt.pts != AV_NOPTS_VALUE) {
1977 av_frac_set(&st->pts, s->cur_pkt.pts);
1979 s->cur_pkt.pts = st->pts.val;
1981 if (!st->codec.codec) {
1982 /* no codec opened: just return the raw packet */
1985 /* no codec opened: just update the pts by considering we
1986 have one frame and free the packet */
1987 if (st->pts.den == 0) {
1988 switch(st->codec.codec_type) {
1989 case CODEC_TYPE_AUDIO:
1990 st->pts_incr = (INT64)s->pts_den * st->codec.frame_size;
1991 av_frac_init(&st->pts, st->pts.val, 0,
1992 (INT64)s->pts_num * st->codec.sample_rate);
1994 case CODEC_TYPE_VIDEO:
1995 st->pts_incr = (INT64)s->pts_den * FRAME_RATE_BASE;
1996 av_frac_init(&st->pts, st->pts.val, 0,
1997 (INT64)s->pts_num * st->codec.frame_rate);
2003 av_frac_add(&st->pts, st->pts_incr);
2006 s->cur_ptr = s->cur_pkt.data;
2007 s->cur_len = s->cur_pkt.size;
2013 static int compute_send_delay(HTTPContext *c)
2015 INT64 cur_pts, delta_pts, next_pts;
2018 /* compute current pts value from system time */
2019 cur_pts = ((INT64)(cur_time - c->start_time) * c->fmt_in->pts_den) /
2020 (c->fmt_in->pts_num * 1000LL);
2021 /* compute the delta from the stream we choose as
2022 main clock (we do that to avoid using explicit
2023 buffers to do exact packet reordering for each
2025 /* XXX: really need to fix the number of streams */
2026 if (c->pts_stream_index >= c->fmt_in->nb_streams)
2029 next_pts = c->fmt_in->streams[c->pts_stream_index]->pts.val;
2030 delta_pts = next_pts - cur_pts;
2031 if (delta_pts <= 0) {
2034 delay1 = (delta_pts * 1000 * c->fmt_in->pts_num) / c->fmt_in->pts_den;
2040 /* just fall backs */
2041 int av_read_frame(AVFormatContext *s, AVPacket *pkt)
2043 return av_read_packet(s, pkt);
2046 static int compute_send_delay(HTTPContext *c)
2048 int datarate = 8 * get_longterm_datarate(&c->datarate, c->data_count);
2050 if (datarate > c->stream->bandwidth * 2000) {
2058 static int http_prepare_data(HTTPContext *c)
2061 AVFormatContext *ctx;
2064 case HTTPSTATE_SEND_DATA_HEADER:
2065 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2066 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
2068 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
2069 c->stream->comment);
2070 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
2071 c->stream->copyright);
2072 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
2075 /* open output stream by using specified codecs */
2076 c->fmt_ctx.oformat = c->stream->fmt;
2077 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2078 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2080 st = av_mallocz(sizeof(AVStream));
2081 c->fmt_ctx.streams[i] = st;
2082 /* if file or feed, then just take streams from FFStream struct */
2083 if (!c->stream->feed ||
2084 c->stream->feed == c->stream)
2085 memcpy(st, c->stream->streams[i], sizeof(AVStream));
2087 memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]],
2089 st->codec.frame_number = 0; /* XXX: should be done in
2090 AVStream, not in codec */
2092 c->got_key_frame = 0;
2094 /* prepare header and save header data in a stream */
2095 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2096 /* XXX: potential leak */
2099 c->fmt_ctx.pb.is_streamed = 1;
2101 av_write_header(&c->fmt_ctx);
2103 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2104 c->buffer_ptr = c->pb_buffer;
2105 c->buffer_end = c->pb_buffer + len;
2107 c->state = HTTPSTATE_SEND_DATA;
2108 c->last_packet_sent = 0;
2110 case HTTPSTATE_SEND_DATA:
2111 /* find a new packet */
2115 /* read a packet from the input stream */
2116 if (c->stream->feed) {
2117 ffm_set_write_index(c->fmt_in,
2118 c->stream->feed->feed_write_index,
2119 c->stream->feed->feed_size);
2122 if (c->stream->max_time &&
2123 c->stream->max_time + c->start_time - cur_time < 0) {
2124 /* We have timed out */
2125 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2127 if (1 || c->is_packetized) {
2128 if (compute_send_delay(c) > 0) {
2129 c->state = HTTPSTATE_WAIT;
2130 return 1; /* state changed */
2134 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2135 if (c->stream->feed && c->stream->feed->feed_opened) {
2136 /* if coming from feed, it means we reached the end of the
2137 ffm file, so must wait for more data */
2138 c->state = HTTPSTATE_WAIT_FEED;
2139 return 1; /* state changed */
2141 if (c->stream->loop) {
2142 av_close_input_file(c->fmt_in);
2144 if (open_input_stream(c, "") < 0)
2149 /* must send trailer now because eof or error */
2150 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2154 /* update first pts if needed */
2155 if (c->first_pts == AV_NOPTS_VALUE)
2156 c->first_pts = pkt.pts;
2158 /* send it to the appropriate stream */
2159 if (c->stream->feed) {
2160 /* if coming from a feed, select the right stream */
2161 if (c->switch_pending) {
2162 c->switch_pending = 0;
2163 for(i=0;i<c->stream->nb_streams;i++) {
2164 if (c->switch_feed_streams[i] == pkt.stream_index) {
2165 if (pkt.flags & PKT_FLAG_KEY) {
2166 do_switch_stream(c, i);
2169 if (c->switch_feed_streams[i] >= 0) {
2170 c->switch_pending = 1;
2174 for(i=0;i<c->stream->nb_streams;i++) {
2175 if (c->feed_streams[i] == pkt.stream_index) {
2176 pkt.stream_index = i;
2177 if (pkt.flags & PKT_FLAG_KEY) {
2178 c->got_key_frame |= 1 << i;
2180 /* See if we have all the key frames, then
2181 * we start to send. This logic is not quite
2182 * right, but it works for the case of a
2183 * single video stream with one or more
2184 * audio streams (for which every frame is
2185 * typically a key frame).
2187 if (!c->stream->send_on_key ||
2188 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2194 AVCodecContext *codec;
2197 /* specific handling for RTP: we use several
2198 output stream (one for each RTP
2199 connection). XXX: need more abstract handling */
2200 if (c->is_packetized) {
2201 c->packet_stream_index = pkt.stream_index;
2202 ctx = c->rtp_ctx[c->packet_stream_index];
2203 codec = &ctx->streams[0]->codec;
2204 /* only one stream per RTP connection */
2205 pkt.stream_index = 0;
2209 codec = &ctx->streams[pkt.stream_index]->codec;
2212 codec->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2215 if (codec->codec_type == CODEC_TYPE_AUDIO) {
2216 codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
2217 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
2221 if (c->is_packetized) {
2222 ret = url_open_dyn_packet_buf(&ctx->pb,
2223 url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]));
2224 c->packet_byte_count = 0;
2225 c->packet_start_time_us = av_gettime();
2227 ret = url_open_dyn_buf(&ctx->pb);
2230 /* XXX: potential leak */
2233 if (av_write_frame(ctx, pkt.stream_index, pkt.data, pkt.size)) {
2234 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2237 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2238 c->buffer_ptr = c->pb_buffer;
2239 c->buffer_end = c->pb_buffer + len;
2241 codec->frame_number++;
2243 #ifndef AV_READ_FRAME
2244 av_free_packet(&pkt);
2251 case HTTPSTATE_SEND_DATA_TRAILER:
2252 /* last packet test ? */
2253 if (c->last_packet_sent || c->is_packetized)
2256 /* prepare header */
2257 if (url_open_dyn_buf(&ctx->pb) < 0) {
2258 /* XXX: potential leak */
2261 av_write_trailer(ctx);
2262 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2263 c->buffer_ptr = c->pb_buffer;
2264 c->buffer_end = c->pb_buffer + len;
2266 c->last_packet_sent = 1;
2273 #define SHORT_TERM_BANDWIDTH 8000000
2275 /* should convert the format at the same time */
2276 static int http_send_data(HTTPContext *c)
2280 while (c->buffer_ptr >= c->buffer_end) {
2281 av_freep(&c->pb_buffer);
2282 ret = http_prepare_data(c);
2285 else if (ret == 0) {
2288 /* state change requested */
2293 if (c->buffer_ptr < c->buffer_end) {
2294 if (c->is_packetized) {
2295 /* RTP/UDP data output */
2296 len = c->buffer_end - c->buffer_ptr;
2298 /* fail safe - should never happen */
2300 c->buffer_ptr = c->buffer_end;
2303 len = (c->buffer_ptr[0] << 24) |
2304 (c->buffer_ptr[1] << 16) |
2305 (c->buffer_ptr[2] << 8) |
2307 if (len > (c->buffer_end - c->buffer_ptr))
2310 /* short term bandwidth limitation */
2311 dt = av_gettime() - c->packet_start_time_us;
2315 if ((c->packet_byte_count + len) * (INT64)1000000 >=
2316 (SHORT_TERM_BANDWIDTH / 8) * (INT64)dt) {
2317 /* bandwidth overflow : wait at most one tick and retry */
2318 c->state = HTTPSTATE_WAIT_SHORT;
2323 url_write(c->rtp_handles[c->packet_stream_index],
2324 c->buffer_ptr, len);
2325 c->buffer_ptr += len;
2326 c->packet_byte_count += len;
2328 /* TCP data output */
2329 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2331 if (errno != EAGAIN && errno != EINTR) {
2332 /* error : close connection */
2338 c->buffer_ptr += len;
2341 c->data_count += len;
2342 update_datarate(&c->datarate, c->data_count);
2344 c->stream->bytes_served += len;
2349 static int http_start_receive_data(HTTPContext *c)
2353 if (c->stream->feed_opened)
2357 fd = open(c->stream->feed_filename, O_RDWR);
2362 c->stream->feed_write_index = ffm_read_write_index(fd);
2363 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2364 lseek(fd, 0, SEEK_SET);
2366 /* init buffer input */
2367 c->buffer_ptr = c->buffer;
2368 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2369 c->stream->feed_opened = 1;
2373 static int http_receive_data(HTTPContext *c)
2377 if (c->buffer_end > c->buffer_ptr) {
2380 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2382 if (errno != EAGAIN && errno != EINTR) {
2383 /* error : close connection */
2386 } else if (len == 0) {
2387 /* end of connection : close it */
2390 c->buffer_ptr += len;
2391 c->data_count += len;
2392 update_datarate(&c->datarate, c->data_count);
2396 if (c->buffer_ptr >= c->buffer_end) {
2397 FFStream *feed = c->stream;
2398 /* a packet has been received : write it in the store, except
2400 if (c->data_count > FFM_PACKET_SIZE) {
2402 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2403 /* XXX: use llseek or url_seek */
2404 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2405 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2407 feed->feed_write_index += FFM_PACKET_SIZE;
2408 /* update file size */
2409 if (feed->feed_write_index > c->stream->feed_size)
2410 feed->feed_size = feed->feed_write_index;
2412 /* handle wrap around if max file size reached */
2413 if (feed->feed_write_index >= c->stream->feed_max_size)
2414 feed->feed_write_index = FFM_PACKET_SIZE;
2417 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2419 /* wake up any waiting connections */
2420 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2421 if (c1->state == HTTPSTATE_WAIT_FEED &&
2422 c1->stream->feed == c->stream->feed) {
2423 c1->state = HTTPSTATE_SEND_DATA;
2427 /* We have a header in our hands that contains useful data */
2429 AVInputFormat *fmt_in;
2430 ByteIOContext *pb = &s.pb;
2433 memset(&s, 0, sizeof(s));
2435 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2436 pb->buf_end = c->buffer_end; /* ?? */
2437 pb->is_streamed = 1;
2439 /* use feed output format name to find corresponding input format */
2440 fmt_in = av_find_input_format(feed->fmt->name);
2444 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2448 if (fmt_in->read_header(&s, 0) < 0) {
2449 av_freep(&s.priv_data);
2453 /* Now we have the actual streams */
2454 if (s.nb_streams != feed->nb_streams) {
2455 av_freep(&s.priv_data);
2458 for (i = 0; i < s.nb_streams; i++) {
2459 memcpy(&feed->streams[i]->codec,
2460 &s.streams[i]->codec, sizeof(AVCodecContext));
2462 av_freep(&s.priv_data);
2464 c->buffer_ptr = c->buffer;
2469 c->stream->feed_opened = 0;
2474 /********************************************************************/
2477 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2484 switch(error_number) {
2485 #define DEF(n, c, s) case c: str = s; break;
2486 #include "rtspcodes.h"
2489 str = "Unknown Error";
2493 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2494 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2496 /* output GMT time */
2500 p = buf2 + strlen(p) - 1;
2503 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2506 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2508 rtsp_reply_header(c, error_number);
2509 url_fprintf(c->pb, "\r\n");
2512 static int rtsp_parse_request(HTTPContext *c)
2514 const char *p, *p1, *p2;
2521 RTSPHeader header1, *header = &header1;
2523 c->buffer_ptr[0] = '\0';
2526 get_word(cmd, sizeof(cmd), &p);
2527 get_word(url, sizeof(url), &p);
2528 get_word(protocol, sizeof(protocol), &p);
2530 pstrcpy(c->method, sizeof(c->method), cmd);
2531 pstrcpy(c->url, sizeof(c->url), url);
2532 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2535 if (url_open_dyn_buf(c->pb) < 0) {
2536 /* XXX: cannot do more */
2537 c->pb = NULL; /* safety */
2541 /* check version name */
2542 if (strcmp(protocol, "RTSP/1.0") != 0) {
2543 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2547 /* parse each header line */
2548 memset(header, 0, sizeof(RTSPHeader));
2549 /* skip to next line */
2550 while (*p != '\n' && *p != '\0')
2554 while (*p != '\0') {
2555 p1 = strchr(p, '\n');
2559 if (p2 > p && p2[-1] == '\r')
2561 /* skip empty line */
2565 if (len > sizeof(line) - 1)
2566 len = sizeof(line) - 1;
2567 memcpy(line, p, len);
2569 rtsp_parse_line(header, line);
2573 /* handle sequence number */
2574 c->seq = header->seq;
2576 if (!strcmp(cmd, "DESCRIBE")) {
2577 rtsp_cmd_describe(c, url);
2578 } else if (!strcmp(cmd, "SETUP")) {
2579 rtsp_cmd_setup(c, url, header);
2580 } else if (!strcmp(cmd, "PLAY")) {
2581 rtsp_cmd_play(c, url, header);
2582 } else if (!strcmp(cmd, "PAUSE")) {
2583 rtsp_cmd_pause(c, url, header);
2584 } else if (!strcmp(cmd, "TEARDOWN")) {
2585 rtsp_cmd_teardown(c, url, header);
2587 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2590 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2591 c->pb = NULL; /* safety */
2593 /* XXX: cannot do more */
2596 c->buffer_ptr = c->pb_buffer;
2597 c->buffer_end = c->pb_buffer + len;
2598 c->state = RTSPSTATE_SEND_REPLY;
2602 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2604 static int prepare_sdp_description(FFStream *stream, UINT8 **pbuffer,
2605 struct in_addr my_ip)
2607 ByteIOContext pb1, *pb = &pb1;
2608 int i, payload_type, port;
2609 const char *ipstr, *title, *mediatype;
2612 if (url_open_dyn_buf(pb) < 0)
2615 /* general media info */
2617 url_fprintf(pb, "v=0\n");
2618 ipstr = inet_ntoa(my_ip);
2619 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2620 title = stream->title;
2621 if (title[0] == '\0')
2623 url_fprintf(pb, "s=%s\n", title);
2624 if (stream->comment[0] != '\0')
2625 url_fprintf(pb, "i=%s\n", stream->comment);
2626 if (stream->is_multicast) {
2627 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2629 /* for each stream, we output the necessary info */
2630 for(i = 0; i < stream->nb_streams; i++) {
2631 st = stream->streams[i];
2632 switch(st->codec.codec_type) {
2633 case CODEC_TYPE_AUDIO:
2634 mediatype = "audio";
2636 case CODEC_TYPE_VIDEO:
2637 mediatype = "video";
2640 mediatype = "application";
2643 /* NOTE: the port indication is not correct in case of
2644 unicast. It is not an issue because RTSP gives it */
2645 payload_type = rtp_get_payload_type(&st->codec);
2646 if (stream->is_multicast) {
2647 port = stream->multicast_port + 2 * i;
2651 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2652 mediatype, port, payload_type);
2653 url_fprintf(pb, "a=control:streamid=%d\n", i);
2655 return url_close_dyn_buf(pb, pbuffer);
2658 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2664 int content_length, len;
2665 struct sockaddr_in my_addr;
2667 /* find which url is asked */
2668 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2673 for(stream = first_stream; stream != NULL; stream = stream->next) {
2674 if (!stream->is_feed && stream->fmt == &rtp_mux &&
2675 !strcmp(path, stream->filename)) {
2679 /* no stream found */
2680 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2684 /* prepare the media description in sdp format */
2686 /* get the host IP */
2687 len = sizeof(my_addr);
2688 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2690 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2691 if (content_length < 0) {
2692 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2695 rtsp_reply_header(c, RTSP_STATUS_OK);
2696 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2697 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2698 url_fprintf(c->pb, "\r\n");
2699 put_buffer(c->pb, content, content_length);
2702 static HTTPContext *find_rtp_session(const char *session_id)
2706 if (session_id[0] == '\0')
2709 for(c = first_http_ctx; c != NULL; c = c->next) {
2710 if (!strcmp(c->session_id, session_id))
2716 RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2718 RTSPTransportField *th;
2721 for(i=0;i<h->nb_transports;i++) {
2722 th = &h->transports[i];
2723 if (th->protocol == protocol)
2729 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2733 int stream_index, port;
2738 RTSPTransportField *th;
2739 struct sockaddr_in dest_addr;
2740 RTSPActionServerSetup setup;
2742 /* find which url is asked */
2743 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2748 /* now check each stream */
2749 for(stream = first_stream; stream != NULL; stream = stream->next) {
2750 if (!stream->is_feed && stream->fmt == &rtp_mux) {
2751 /* accept aggregate filenames only if single stream */
2752 if (!strcmp(path, stream->filename)) {
2753 if (stream->nb_streams != 1) {
2754 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2761 for(stream_index = 0; stream_index < stream->nb_streams;
2763 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2764 stream->filename, stream_index);
2765 if (!strcmp(path, buf))
2770 /* no stream found */
2771 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2775 /* generate session id if needed */
2776 if (h->session_id[0] == '\0') {
2777 snprintf(h->session_id, sizeof(h->session_id),
2778 "%08x%08x", (int)random(), (int)random());
2781 /* find rtp session, and create it if none found */
2782 rtp_c = find_rtp_session(h->session_id);
2784 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id);
2786 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2790 /* open input stream */
2791 if (open_input_stream(rtp_c, "") < 0) {
2792 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2796 /* always prefer UDP */
2797 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2799 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2801 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2805 rtp_c->rtp_protocol = th->protocol;
2808 /* test if stream is OK (test needed because several SETUP needs
2809 to be done for a given file) */
2810 if (rtp_c->stream != stream) {
2811 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2815 /* test if stream is already set up */
2816 if (rtp_c->rtp_ctx[stream_index]) {
2817 rtsp_reply_error(c, RTSP_STATUS_STATE);
2821 /* check transport */
2822 th = find_transport(h, rtp_c->rtp_protocol);
2823 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2824 th->client_port_min <= 0)) {
2825 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2829 /* setup default options */
2830 setup.transport_option[0] = '\0';
2831 dest_addr = rtp_c->from_addr;
2832 dest_addr.sin_port = htons(th->client_port_min);
2834 /* add transport option if needed */
2835 if (ff_rtsp_callback) {
2836 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2837 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2838 (char *)&setup, sizeof(setup),
2839 stream->rtsp_option) < 0) {
2840 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2843 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2847 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
2848 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2852 /* now everything is OK, so we can send the connection parameters */
2853 rtsp_reply_header(c, RTSP_STATUS_OK);
2855 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2857 switch(rtp_c->rtp_protocol) {
2858 case RTSP_PROTOCOL_RTP_UDP:
2859 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2860 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2861 "client_port=%d-%d;server_port=%d-%d",
2862 th->client_port_min, th->client_port_min + 1,
2865 case RTSP_PROTOCOL_RTP_TCP:
2866 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2867 stream_index * 2, stream_index * 2 + 1);
2872 if (setup.transport_option[0] != '\0') {
2873 url_fprintf(c->pb, ";%s", setup.transport_option);
2875 url_fprintf(c->pb, "\r\n");
2878 url_fprintf(c->pb, "\r\n");
2882 /* find an rtp connection by using the session ID. Check consistency
2884 static HTTPContext *find_rtp_session_with_url(const char *url,
2885 const char *session_id)
2891 rtp_c = find_rtp_session(session_id);
2895 /* find which url is asked */
2896 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2900 if (strcmp(path, rtp_c->stream->filename) != 0)
2905 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2909 rtp_c = find_rtp_session_with_url(url, h->session_id);
2911 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2915 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2916 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2917 rtp_c->state != HTTPSTATE_READY) {
2918 rtsp_reply_error(c, RTSP_STATUS_STATE);
2922 rtp_c->state = HTTPSTATE_SEND_DATA;
2924 /* now everything is OK, so we can send the connection parameters */
2925 rtsp_reply_header(c, RTSP_STATUS_OK);
2927 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2928 url_fprintf(c->pb, "\r\n");
2931 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
2935 rtp_c = find_rtp_session_with_url(url, h->session_id);
2937 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2941 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2942 rtp_c->state != HTTPSTATE_WAIT_FEED) {
2943 rtsp_reply_error(c, RTSP_STATUS_STATE);
2947 rtp_c->state = HTTPSTATE_READY;
2949 /* now everything is OK, so we can send the connection parameters */
2950 rtsp_reply_header(c, RTSP_STATUS_OK);
2952 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2953 url_fprintf(c->pb, "\r\n");
2956 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
2960 rtp_c = find_rtp_session_with_url(url, h->session_id);
2962 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2966 /* abort the session */
2967 close_connection(rtp_c);
2969 if (ff_rtsp_callback) {
2970 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
2972 rtp_c->stream->rtsp_option);
2975 /* now everything is OK, so we can send the connection parameters */
2976 rtsp_reply_header(c, RTSP_STATUS_OK);
2978 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2979 url_fprintf(c->pb, "\r\n");
2983 /********************************************************************/
2986 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
2987 FFStream *stream, const char *session_id)
2989 HTTPContext *c = NULL;
2991 /* XXX: should output a warning page when coming
2992 close to the connection limit */
2993 if (nb_connections >= nb_max_connections)
2996 /* add a new connection */
2997 c = av_mallocz(sizeof(HTTPContext));
3002 c->poll_entry = NULL;
3003 c->from_addr = *from_addr;
3004 c->buffer_size = IOBUFFER_INIT_SIZE;
3005 c->buffer = av_malloc(c->buffer_size);
3010 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
3011 c->state = HTTPSTATE_READY;
3012 c->is_packetized = 1;
3013 /* protocol is shown in statistics */
3014 pstrcpy(c->protocol, sizeof(c->protocol), "RTP");
3016 current_bandwidth += stream->bandwidth;
3018 c->next = first_http_ctx;
3030 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3031 command). if dest_addr is NULL, then TCP tunneling in RTSP is
3033 static int rtp_new_av_stream(HTTPContext *c,
3034 int stream_index, struct sockaddr_in *dest_addr)
3036 AVFormatContext *ctx;
3043 /* now we can open the relevant output stream */
3044 ctx = av_mallocz(sizeof(AVFormatContext));
3047 ctx->oformat = &rtp_mux;
3049 st = av_mallocz(sizeof(AVStream));
3052 ctx->nb_streams = 1;
3053 ctx->streams[0] = st;
3055 if (!c->stream->feed ||
3056 c->stream->feed == c->stream) {
3057 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3060 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3065 /* build destination RTP address */
3066 ipaddr = inet_ntoa(dest_addr->sin_addr);
3068 /* XXX: also pass as parameter to function ? */
3069 if (c->stream->is_multicast) {
3071 ttl = c->stream->multicast_ttl;
3074 snprintf(ctx->filename, sizeof(ctx->filename),
3075 "rtp://%s:%d?multicast=1&ttl=%d",
3076 ipaddr, ntohs(dest_addr->sin_port), ttl);
3078 snprintf(ctx->filename, sizeof(ctx->filename),
3079 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3082 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3084 c->rtp_handles[stream_index] = h;
3089 http_log("%s:%d - - [%s] \"RTPSTART %s/streamid=%d\"\n",
3090 ipaddr, ntohs(dest_addr->sin_port),
3092 c->stream->filename, stream_index);
3094 /* normally, no packets should be output here, but the packet size may be checked */
3095 if (url_open_dyn_packet_buf(&ctx->pb,
3096 url_get_max_packet_size(h)) < 0) {
3097 /* XXX: close stream */
3100 if (av_write_header(ctx) < 0) {
3107 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3110 c->rtp_ctx[stream_index] = ctx;
3114 /********************************************************************/
3115 /* ffserver initialization */
3117 AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3121 fst = av_mallocz(sizeof(AVStream));
3124 fst->priv_data = av_mallocz(sizeof(FeedData));
3125 memcpy(&fst->codec, codec, sizeof(AVCodecContext));
3126 stream->streams[stream->nb_streams++] = fst;
3130 /* return the stream number in the feed */
3131 int add_av_stream(FFStream *feed,
3135 AVCodecContext *av, *av1;
3139 for(i=0;i<feed->nb_streams;i++) {
3140 st = feed->streams[i];
3142 if (av1->codec_id == av->codec_id &&
3143 av1->codec_type == av->codec_type &&
3144 av1->bit_rate == av->bit_rate) {
3146 switch(av->codec_type) {
3147 case CODEC_TYPE_AUDIO:
3148 if (av1->channels == av->channels &&
3149 av1->sample_rate == av->sample_rate)
3152 case CODEC_TYPE_VIDEO:
3153 if (av1->width == av->width &&
3154 av1->height == av->height &&
3155 av1->frame_rate == av->frame_rate &&
3156 av1->gop_size == av->gop_size)
3165 fst = add_av_stream1(feed, av);
3168 return feed->nb_streams - 1;
3173 void remove_stream(FFStream *stream)
3177 while (*ps != NULL) {
3178 if (*ps == stream) {
3186 /* compute the needed AVStream for each file */
3187 void build_file_streams(void)
3189 FFStream *stream, *stream_next;
3190 AVFormatContext *infile;
3193 /* gather all streams */
3194 for(stream = first_stream; stream != NULL; stream = stream_next) {
3195 stream_next = stream->next;
3196 if (stream->stream_type == STREAM_TYPE_LIVE &&
3198 /* the stream comes from a file */
3199 /* try to open the file */
3201 if (av_open_input_file(&infile, stream->feed_filename,
3202 NULL, 0, NULL) < 0) {
3203 http_log("%s not found", stream->feed_filename);
3204 /* remove stream (no need to spend more time on it) */
3206 remove_stream(stream);
3208 /* find all the AVStreams inside and reference them in
3210 if (av_find_stream_info(infile) < 0) {
3211 http_log("Could not find codec parameters from '%s'",
3212 stream->feed_filename);
3213 av_close_input_file(infile);
3216 for(i=0;i<infile->nb_streams;i++) {
3217 add_av_stream1(stream, &infile->streams[i]->codec);
3219 av_close_input_file(infile);
3225 /* compute the needed AVStream for each feed */
3226 void build_feed_streams(void)
3228 FFStream *stream, *feed;
3231 /* gather all streams */
3232 for(stream = first_stream; stream != NULL; stream = stream->next) {
3233 feed = stream->feed;
3235 if (!stream->is_feed) {
3236 /* we handle a stream coming from a feed */
3237 for(i=0;i<stream->nb_streams;i++) {
3238 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3244 /* gather all streams */
3245 for(stream = first_stream; stream != NULL; stream = stream->next) {
3246 feed = stream->feed;
3248 if (stream->is_feed) {
3249 for(i=0;i<stream->nb_streams;i++) {
3250 stream->feed_streams[i] = i;
3256 /* create feed files if needed */
3257 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3260 if (url_exist(feed->feed_filename)) {
3261 /* See if it matches */
3265 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3266 /* Now see if it matches */
3267 if (s->nb_streams == feed->nb_streams) {
3269 for(i=0;i<s->nb_streams;i++) {
3271 sf = feed->streams[i];
3274 if (sf->index != ss->index ||
3276 printf("Index & Id do not match for stream %d\n", i);
3279 AVCodecContext *ccf, *ccs;
3283 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3285 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3286 printf("Codecs do not match for stream %d\n", i);
3288 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3289 printf("Codec bitrates do not match for stream %d\n", i);
3291 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3292 if (CHECK_CODEC(frame_rate) ||
3293 CHECK_CODEC(width) ||
3294 CHECK_CODEC(height)) {
3295 printf("Codec width, height and framerate do not match for stream %d\n", i);
3298 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3299 if (CHECK_CODEC(sample_rate) ||
3300 CHECK_CODEC(channels) ||
3301 CHECK_CODEC(frame_size)) {
3302 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3306 printf("Unknown codec type\n");
3315 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3316 feed->feed_filename, s->nb_streams, feed->nb_streams);
3319 av_close_input_file(s);
3321 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3322 feed->feed_filename);
3325 unlink(feed->feed_filename);
3327 if (!url_exist(feed->feed_filename)) {
3328 AVFormatContext s1, *s = &s1;
3330 /* only write the header of the ffm file */
3331 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3332 fprintf(stderr, "Could not open output feed file '%s'\n",
3333 feed->feed_filename);
3336 s->oformat = feed->fmt;
3337 s->nb_streams = feed->nb_streams;
3338 for(i=0;i<s->nb_streams;i++) {
3340 st = feed->streams[i];
3344 /* XXX: need better api */
3345 av_freep(&s->priv_data);
3348 /* get feed size and write index */
3349 fd = open(feed->feed_filename, O_RDONLY);
3351 fprintf(stderr, "Could not open output feed file '%s'\n",
3352 feed->feed_filename);
3356 feed->feed_write_index = ffm_read_write_index(fd);
3357 feed->feed_size = lseek(fd, 0, SEEK_END);
3358 /* ensure that we do not wrap before the end of file */
3359 if (feed->feed_max_size < feed->feed_size)
3360 feed->feed_max_size = feed->feed_size;
3366 /* compute the bandwidth used by each stream */
3367 static void compute_bandwidth(void)
3372 for(stream = first_stream; stream != NULL; stream = stream->next) {
3374 for(i=0;i<stream->nb_streams;i++) {
3375 AVStream *st = stream->streams[i];
3376 switch(st->codec.codec_type) {
3377 case CODEC_TYPE_AUDIO:
3378 case CODEC_TYPE_VIDEO:
3379 bandwidth += st->codec.bit_rate;
3385 stream->bandwidth = (bandwidth + 999) / 1000;
3389 static void get_arg(char *buf, int buf_size, const char **pp)
3396 while (isspace(*p)) p++;
3399 if (*p == '\"' || *p == '\'')
3411 if ((q - buf) < buf_size - 1)
3416 if (quote && *p == quote)
3421 /* add a codec and set the default parameters */
3422 void add_codec(FFStream *stream, AVCodecContext *av)
3426 /* compute default parameters */
3427 switch(av->codec_type) {
3428 case CODEC_TYPE_AUDIO:
3429 if (av->bit_rate == 0)
3430 av->bit_rate = 64000;
3431 if (av->sample_rate == 0)
3432 av->sample_rate = 22050;
3433 if (av->channels == 0)
3436 case CODEC_TYPE_VIDEO:
3437 if (av->bit_rate == 0)
3438 av->bit_rate = 64000;
3439 if (av->frame_rate == 0)
3440 av->frame_rate = 5 * FRAME_RATE_BASE;
3441 if (av->width == 0 || av->height == 0) {
3445 /* Bitrate tolerance is less for streaming */
3446 if (av->bit_rate_tolerance == 0)
3447 av->bit_rate_tolerance = av->bit_rate / 4;
3452 if (av->max_qdiff == 0)
3454 av->qcompress = 0.5;
3458 av->rc_eq = "tex^qComp";
3459 if (!av->i_quant_factor)
3460 av->i_quant_factor = -0.8;
3461 if (!av->b_quant_factor)
3462 av->b_quant_factor = 1.25;
3463 if (!av->b_quant_offset)
3464 av->b_quant_offset = 1.25;
3465 if (!av->rc_min_rate)
3466 av->rc_min_rate = av->bit_rate / 2;
3467 if (!av->rc_max_rate)
3468 av->rc_max_rate = av->bit_rate * 2;
3475 st = av_mallocz(sizeof(AVStream));
3478 stream->streams[stream->nb_streams++] = st;
3479 memcpy(&st->codec, av, sizeof(AVCodecContext));
3482 int opt_audio_codec(const char *arg)
3488 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3493 return CODEC_ID_NONE;
3499 int opt_video_codec(const char *arg)
3505 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3510 return CODEC_ID_NONE;
3516 /* simplistic plugin support */
3518 void load_module(const char *filename)
3521 void (*init_func)(void);
3522 dll = dlopen(filename, RTLD_NOW);
3524 fprintf(stderr, "Could not load module '%s' - %s\n",
3525 filename, dlerror());
3529 init_func = dlsym(dll, "ffserver_module_init");
3532 "%s: init function 'ffserver_module_init()' not found\n",
3540 int parse_ffconfig(const char *filename)
3547 int val, errors, line_num;
3548 FFStream **last_stream, *stream, *redirect;
3549 FFStream **last_feed, *feed;
3550 AVCodecContext audio_enc, video_enc;
3551 int audio_id, video_id;
3553 f = fopen(filename, "r");
3561 first_stream = NULL;
3562 last_stream = &first_stream;
3564 last_feed = &first_feed;
3568 audio_id = CODEC_ID_NONE;
3569 video_id = CODEC_ID_NONE;
3571 if (fgets(line, sizeof(line), f) == NULL)
3577 if (*p == '\0' || *p == '#')
3580 get_arg(cmd, sizeof(cmd), &p);
3582 if (!strcasecmp(cmd, "Port")) {
3583 get_arg(arg, sizeof(arg), &p);
3584 my_http_addr.sin_port = htons (atoi(arg));
3585 } else if (!strcasecmp(cmd, "BindAddress")) {
3586 get_arg(arg, sizeof(arg), &p);
3587 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3588 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3589 filename, line_num, arg);
3592 } else if (!strcasecmp(cmd, "NoDaemon")) {
3593 ffserver_daemon = 0;
3594 } else if (!strcasecmp(cmd, "RTSPPort")) {
3595 get_arg(arg, sizeof(arg), &p);
3596 my_rtsp_addr.sin_port = htons (atoi(arg));
3597 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3598 get_arg(arg, sizeof(arg), &p);
3599 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3600 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3601 filename, line_num, arg);
3604 } else if (!strcasecmp(cmd, "MaxClients")) {
3605 get_arg(arg, sizeof(arg), &p);
3607 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3608 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3609 filename, line_num, arg);
3612 nb_max_connections = val;
3614 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3615 get_arg(arg, sizeof(arg), &p);
3617 if (val < 10 || val > 100000) {
3618 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3619 filename, line_num, arg);
3622 max_bandwidth = val;
3624 } else if (!strcasecmp(cmd, "CustomLog")) {
3625 get_arg(logfilename, sizeof(logfilename), &p);
3626 } else if (!strcasecmp(cmd, "<Feed")) {
3627 /*********************************************/
3628 /* Feed related options */
3630 if (stream || feed) {
3631 fprintf(stderr, "%s:%d: Already in a tag\n",
3632 filename, line_num);
3634 feed = av_mallocz(sizeof(FFStream));
3635 /* add in stream list */
3636 *last_stream = feed;
3637 last_stream = &feed->next;
3638 /* add in feed list */
3640 last_feed = &feed->next_feed;
3642 get_arg(feed->filename, sizeof(feed->filename), &p);
3643 q = strrchr(feed->filename, '>');
3646 feed->fmt = guess_format("ffm", NULL, NULL);
3647 /* defaut feed file */
3648 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3649 "/tmp/%s.ffm", feed->filename);
3650 feed->feed_max_size = 5 * 1024 * 1024;
3652 feed->feed = feed; /* self feeding :-) */
3654 } else if (!strcasecmp(cmd, "Launch")) {
3658 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3660 feed->child_argv[0] = av_malloc(7);
3661 strcpy(feed->child_argv[0], "ffmpeg");
3663 for (i = 1; i < 62; i++) {
3666 get_arg(argbuf, sizeof(argbuf), &p);
3670 feed->child_argv[i] = av_malloc(strlen(argbuf + 1));
3671 strcpy(feed->child_argv[i], argbuf);
3674 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3676 snprintf(feed->child_argv[i], 256, "http://127.0.0.1:%d/%s",
3677 ntohs(my_http_addr.sin_port), feed->filename);
3679 } else if (!strcasecmp(cmd, "File")) {
3681 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3682 } else if (stream) {
3683 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3685 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3690 get_arg(arg, sizeof(arg), &p);
3692 fsize = strtod(p1, (char **)&p1);
3693 switch(toupper(*p1)) {
3698 fsize *= 1024 * 1024;
3701 fsize *= 1024 * 1024 * 1024;
3704 feed->feed_max_size = (INT64)fsize;
3706 } else if (!strcasecmp(cmd, "</Feed>")) {
3708 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3709 filename, line_num);
3713 /* Make sure that we start out clean */
3714 if (unlink(feed->feed_filename) < 0
3715 && errno != ENOENT) {
3716 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3717 filename, line_num, feed->feed_filename, strerror(errno));
3723 } else if (!strcasecmp(cmd, "<Stream")) {
3724 /*********************************************/
3725 /* Stream related options */
3727 if (stream || feed) {
3728 fprintf(stderr, "%s:%d: Already in a tag\n",
3729 filename, line_num);
3731 stream = av_mallocz(sizeof(FFStream));
3732 *last_stream = stream;
3733 last_stream = &stream->next;
3735 get_arg(stream->filename, sizeof(stream->filename), &p);
3736 q = strrchr(stream->filename, '>');
3739 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3740 memset(&audio_enc, 0, sizeof(AVCodecContext));
3741 memset(&video_enc, 0, sizeof(AVCodecContext));
3742 audio_id = CODEC_ID_NONE;
3743 video_id = CODEC_ID_NONE;
3745 audio_id = stream->fmt->audio_codec;
3746 video_id = stream->fmt->video_codec;
3749 } else if (!strcasecmp(cmd, "Feed")) {
3750 get_arg(arg, sizeof(arg), &p);
3755 while (sfeed != NULL) {
3756 if (!strcmp(sfeed->filename, arg))
3758 sfeed = sfeed->next_feed;
3761 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3762 filename, line_num, arg);
3764 stream->feed = sfeed;
3767 } else if (!strcasecmp(cmd, "Format")) {
3768 get_arg(arg, sizeof(arg), &p);
3769 if (!strcmp(arg, "status")) {
3770 stream->stream_type = STREAM_TYPE_STATUS;
3773 stream->stream_type = STREAM_TYPE_LIVE;
3774 /* jpeg cannot be used here, so use single frame jpeg */
3775 if (!strcmp(arg, "jpeg"))
3776 strcpy(arg, "singlejpeg");
3777 stream->fmt = guess_stream_format(arg, NULL, NULL);
3779 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3780 filename, line_num, arg);
3785 audio_id = stream->fmt->audio_codec;
3786 video_id = stream->fmt->video_codec;
3788 } else if (!strcasecmp(cmd, "FaviconURL")) {
3789 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3790 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3792 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3793 filename, line_num);
3796 } else if (!strcasecmp(cmd, "Author")) {
3798 get_arg(stream->author, sizeof(stream->author), &p);
3800 } else if (!strcasecmp(cmd, "Comment")) {
3802 get_arg(stream->comment, sizeof(stream->comment), &p);
3804 } else if (!strcasecmp(cmd, "Copyright")) {
3806 get_arg(stream->copyright, sizeof(stream->copyright), &p);
3808 } else if (!strcasecmp(cmd, "Title")) {
3810 get_arg(stream->title, sizeof(stream->title), &p);
3812 } else if (!strcasecmp(cmd, "Preroll")) {
3813 get_arg(arg, sizeof(arg), &p);
3815 stream->prebuffer = atof(arg) * 1000;
3817 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
3819 stream->send_on_key = 1;
3821 } else if (!strcasecmp(cmd, "AudioCodec")) {
3822 get_arg(arg, sizeof(arg), &p);
3823 audio_id = opt_audio_codec(arg);
3824 if (audio_id == CODEC_ID_NONE) {
3825 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
3826 filename, line_num, arg);
3829 } else if (!strcasecmp(cmd, "VideoCodec")) {
3830 get_arg(arg, sizeof(arg), &p);
3831 video_id = opt_video_codec(arg);
3832 if (video_id == CODEC_ID_NONE) {
3833 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
3834 filename, line_num, arg);
3837 } else if (!strcasecmp(cmd, "MaxTime")) {
3838 get_arg(arg, sizeof(arg), &p);
3840 stream->max_time = atof(arg) * 1000;
3842 } else if (!strcasecmp(cmd, "AudioBitRate")) {
3843 get_arg(arg, sizeof(arg), &p);
3845 audio_enc.bit_rate = atoi(arg) * 1000;
3847 } else if (!strcasecmp(cmd, "AudioChannels")) {
3848 get_arg(arg, sizeof(arg), &p);
3850 audio_enc.channels = atoi(arg);
3852 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
3853 get_arg(arg, sizeof(arg), &p);
3855 audio_enc.sample_rate = atoi(arg);
3857 } else if (!strcasecmp(cmd, "AudioQuality")) {
3858 get_arg(arg, sizeof(arg), &p);
3860 audio_enc.quality = atof(arg) * 1000;
3862 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
3864 int minrate, maxrate;
3866 get_arg(arg, sizeof(arg), &p);
3868 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
3869 video_enc.rc_min_rate = minrate * 1000;
3870 video_enc.rc_max_rate = maxrate * 1000;
3872 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
3873 filename, line_num, arg);
3877 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
3879 get_arg(arg, sizeof(arg), &p);
3880 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
3882 } else if (!strcasecmp(cmd, "VideoBitRate")) {
3883 get_arg(arg, sizeof(arg), &p);
3885 video_enc.bit_rate = atoi(arg) * 1000;
3887 } else if (!strcasecmp(cmd, "VideoSize")) {
3888 get_arg(arg, sizeof(arg), &p);
3890 parse_image_size(&video_enc.width, &video_enc.height, arg);
3891 if ((video_enc.width % 16) != 0 ||
3892 (video_enc.height % 16) != 0) {
3893 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
3894 filename, line_num);
3898 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
3899 get_arg(arg, sizeof(arg), &p);
3901 video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
3903 } else if (!strcasecmp(cmd, "VideoGopSize")) {
3904 get_arg(arg, sizeof(arg), &p);
3906 video_enc.gop_size = atoi(arg);
3908 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
3910 video_enc.gop_size = 1;
3912 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
3914 video_enc.flags |= CODEC_FLAG_HQ;
3916 } else if (!strcasecmp(cmd, "VideoQDiff")) {
3917 get_arg(arg, sizeof(arg), &p);
3919 video_enc.max_qdiff = atoi(arg);
3920 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
3921 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
3922 filename, line_num);
3926 } else if (!strcasecmp(cmd, "VideoQMax")) {
3927 get_arg(arg, sizeof(arg), &p);
3929 video_enc.qmax = atoi(arg);
3930 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
3931 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
3932 filename, line_num);
3936 } else if (!strcasecmp(cmd, "VideoQMin")) {
3937 get_arg(arg, sizeof(arg), &p);
3939 video_enc.qmin = atoi(arg);
3940 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
3941 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
3942 filename, line_num);
3946 } else if (!strcasecmp(cmd, "LumaElim")) {
3947 get_arg(arg, sizeof(arg), &p);
3949 video_enc.luma_elim_threshold = atoi(arg);
3951 } else if (!strcasecmp(cmd, "ChromaElim")) {
3952 get_arg(arg, sizeof(arg), &p);
3954 video_enc.chroma_elim_threshold = atoi(arg);
3956 } else if (!strcasecmp(cmd, "LumiMask")) {
3957 get_arg(arg, sizeof(arg), &p);
3959 video_enc.lumi_masking = atof(arg);
3961 } else if (!strcasecmp(cmd, "DarkMask")) {
3962 get_arg(arg, sizeof(arg), &p);
3964 video_enc.dark_masking = atof(arg);
3966 } else if (!strcasecmp(cmd, "NoVideo")) {
3967 video_id = CODEC_ID_NONE;
3968 } else if (!strcasecmp(cmd, "NoAudio")) {
3969 audio_id = CODEC_ID_NONE;
3970 } else if (!strcasecmp(cmd, "ACL")) {
3974 get_arg(arg, sizeof(arg), &p);
3975 if (strcasecmp(arg, "allow") == 0) {
3976 acl.action = IP_ALLOW;
3977 } else if (strcasecmp(arg, "deny") == 0) {
3978 acl.action = IP_DENY;
3980 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
3981 filename, line_num, arg);
3985 get_arg(arg, sizeof(arg), &p);
3987 he = gethostbyname(arg);
3989 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
3990 filename, line_num, arg);
3993 /* Only take the first */
3994 acl.first = *(struct in_addr *) he->h_addr_list[0];
3995 acl.last = acl.first;
3998 get_arg(arg, sizeof(arg), &p);
4001 he = gethostbyname(arg);
4003 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4004 filename, line_num, arg);
4007 /* Only take the first */
4008 acl.last = *(struct in_addr *) he->h_addr_list[0];
4013 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4014 IPAddressACL **naclp = 0;
4020 naclp = &stream->acl;
4024 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4025 filename, line_num);
4031 naclp = &(*naclp)->next;
4036 } else if (!strcasecmp(cmd, "RTSPOption")) {
4037 get_arg(arg, sizeof(arg), &p);
4039 av_freep(&stream->rtsp_option);
4040 /* XXX: av_strdup ? */
4041 stream->rtsp_option = av_malloc(strlen(arg) + 1);
4042 if (stream->rtsp_option) {
4043 strcpy(stream->rtsp_option, arg);
4046 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4047 get_arg(arg, sizeof(arg), &p);
4049 if (!inet_aton(arg, &stream->multicast_ip)) {
4050 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
4051 filename, line_num, arg);
4054 stream->is_multicast = 1;
4055 stream->loop = 1; /* default is looping */
4057 } else if (!strcasecmp(cmd, "MulticastPort")) {
4058 get_arg(arg, sizeof(arg), &p);
4060 stream->multicast_port = atoi(arg);
4062 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4063 get_arg(arg, sizeof(arg), &p);
4065 stream->multicast_ttl = atoi(arg);
4067 } else if (!strcasecmp(cmd, "NoLoop")) {
4071 } else if (!strcasecmp(cmd, "</Stream>")) {
4073 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4074 filename, line_num);
4077 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4078 if (audio_id != CODEC_ID_NONE) {
4079 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4080 audio_enc.codec_id = audio_id;
4081 add_codec(stream, &audio_enc);
4083 if (video_id != CODEC_ID_NONE) {
4084 video_enc.codec_type = CODEC_TYPE_VIDEO;
4085 video_enc.codec_id = video_id;
4086 add_codec(stream, &video_enc);
4090 } else if (!strcasecmp(cmd, "<Redirect")) {
4091 /*********************************************/
4093 if (stream || feed || redirect) {
4094 fprintf(stderr, "%s:%d: Already in a tag\n",
4095 filename, line_num);
4098 redirect = av_mallocz(sizeof(FFStream));
4099 *last_stream = redirect;
4100 last_stream = &redirect->next;
4102 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4103 q = strrchr(redirect->filename, '>');
4106 redirect->stream_type = STREAM_TYPE_REDIRECT;
4108 } else if (!strcasecmp(cmd, "URL")) {
4110 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4112 } else if (!strcasecmp(cmd, "</Redirect>")) {
4114 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4115 filename, line_num);
4118 if (!redirect->feed_filename[0]) {
4119 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4120 filename, line_num);
4124 } else if (!strcasecmp(cmd, "LoadModule")) {
4125 get_arg(arg, sizeof(arg), &p);
4128 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4129 filename, line_num, cmd);
4143 static void write_packet(FFCodec *ffenc,
4144 UINT8 *buf, int size)
4147 AVCodecContext *enc = &ffenc->enc;
4149 mk_header(&hdr, enc, size);
4150 wptr = http_fifo.wptr;
4151 fifo_write(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &wptr);
4152 fifo_write(&http_fifo, buf, size, &wptr);
4153 /* atomic modification of wptr */
4154 http_fifo.wptr = wptr;
4155 ffenc->data_count += size;
4156 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4162 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4163 "usage: ffserver [-L] [-h] [-f configfile]\n"
4164 "Hyper fast multi format Audio/Video streaming server\n"
4166 "-L : print the LICENCE\n"
4168 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4175 "ffserver version " FFMPEG_VERSION "\n"
4176 "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4177 "This library is free software; you can redistribute it and/or\n"
4178 "modify it under the terms of the GNU Lesser General Public\n"
4179 "License as published by the Free Software Foundation; either\n"
4180 "version 2 of the License, or (at your option) any later version.\n"
4182 "This library is distributed in the hope that it will be useful,\n"
4183 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4184 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4185 "Lesser General Public License for more details.\n"
4187 "You should have received a copy of the GNU Lesser General Public\n"
4188 "License along with this library; if not, write to the Free Software\n"
4189 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
4193 static void handle_child_exit(int sig)
4198 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4201 for (feed = first_feed; feed; feed = feed->next) {
4202 if (feed->pid == pid) {
4203 int uptime = time(0) - feed->pid_start;
4206 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4209 /* Turn off any more restarts */
4210 feed->child_argv = 0;
4216 need_to_start_children = 1;
4219 int main(int argc, char **argv)
4221 const char *config_filename;
4223 struct sigaction sigact;
4227 config_filename = "/etc/ffserver.conf";
4229 my_program_name = argv[0];
4230 my_program_dir = getcwd(0, 0);
4231 ffserver_daemon = 1;
4234 c = getopt(argc, argv, "ndLh?f:");