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 */
124 DataRateData datarate;
131 int is_packetized; /* if true, the stream is packetized */
132 int packet_stream_index; /* current stream for output in state machine */
134 /* RTSP state specific */
135 UINT8 *pb_buffer; /* XXX: use that in all the code */
137 int seq; /* RTSP sequence number */
139 /* RTP state specific */
140 enum RTSPProtocol rtp_protocol;
141 char session_id[32]; /* session id */
142 AVFormatContext *rtp_ctx[MAX_STREAMS];
143 URLContext *rtp_handles[MAX_STREAMS];
144 /* RTP short term bandwidth limitation */
145 int packet_byte_count;
146 int packet_start_time_us; /* used for short durations (a few
150 /* each generated stream is described here */
154 STREAM_TYPE_REDIRECT,
157 enum IPAddressAction {
162 typedef struct IPAddressACL {
163 struct IPAddressACL *next;
164 enum IPAddressAction action;
165 struct in_addr first;
169 /* description of each stream of the ffserver.conf file */
170 typedef struct FFStream {
171 enum StreamType stream_type;
172 char filename[1024]; /* stream filename */
173 struct FFStream *feed; /* feed we are using (can be null if
178 int prebuffer; /* Number of millseconds early to start */
179 long max_time; /* Number of milliseconds to run */
181 AVStream *streams[MAX_STREAMS];
182 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
183 char feed_filename[1024]; /* file name of the feed storage, or
184 input file name for a stream */
189 pid_t pid; /* Of ffmpeg process */
190 time_t pid_start; /* Of ffmpeg process */
192 struct FFStream *next;
196 int feed_opened; /* true if someone is writing to the feed */
197 int is_feed; /* true if it is a feed */
200 INT64 feed_max_size; /* maximum storage size */
201 INT64 feed_write_index; /* current write position in feed (it wraps round) */
202 INT64 feed_size; /* current size of feed */
203 struct FFStream *next_feed;
206 typedef struct FeedData {
207 long long data_count;
208 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
211 struct sockaddr_in my_http_addr;
212 struct sockaddr_in my_rtsp_addr;
214 char logfilename[1024];
215 HTTPContext *first_http_ctx;
216 FFStream *first_feed; /* contains only feeds */
217 FFStream *first_stream; /* contains all streams, including feeds */
219 static void new_connection(int server_fd, int is_rtsp);
220 static void close_connection(HTTPContext *c);
223 static int handle_connection(HTTPContext *c);
224 static int http_parse_request(HTTPContext *c);
225 static int http_send_data(HTTPContext *c);
226 static void compute_stats(HTTPContext *c);
227 static int open_input_stream(HTTPContext *c, const char *info);
228 static int http_start_receive_data(HTTPContext *c);
229 static int http_receive_data(HTTPContext *c);
230 static int compute_send_delay(HTTPContext *c);
233 static int rtsp_parse_request(HTTPContext *c);
234 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
235 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
236 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
237 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
238 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
241 static HTTPContext *rtp_new_connection(HTTPContext *rtsp_c,
242 FFStream *stream, const char *session_id);
243 static int rtp_new_av_stream(HTTPContext *c,
244 int stream_index, struct sockaddr_in *dest_addr);
246 static const char *my_program_name;
248 static int ffserver_debug;
249 static int ffserver_daemon;
250 static int no_launch;
251 static int need_to_start_children;
253 int nb_max_connections;
256 int nb_max_bandwidth;
259 static long cur_time; // Making this global saves on passing it around everywhere
261 static long gettime_ms(void)
265 gettimeofday(&tv,NULL);
266 return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
269 static FILE *logfile = NULL;
271 static void http_log(char *fmt, ...)
277 vfprintf(logfile, fmt, ap);
283 static void log_connection(HTTPContext *c)
285 char buf1[32], buf2[32], *p;
291 /* XXX: reentrant function ? */
292 p = inet_ntoa(c->from_addr.sin_addr);
297 p = buf2 + strlen(p) - 1;
300 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
301 buf1, buf2, c->method, c->url, c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
304 static void update_datarate(DataRateData *drd, INT64 count)
306 if (!drd->time1 && !drd->count1) {
307 drd->time1 = drd->time2 = cur_time;
308 drd->count1 = drd->count2 = count;
310 if (cur_time - drd->time2 > 5000) {
311 drd->time1 = drd->time2;
312 drd->count1 = drd->count2;
313 drd->time2 = cur_time;
319 /* In bytes per second */
320 static int compute_datarate(DataRateData *drd, INT64 count)
322 if (cur_time == drd->time1)
325 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
328 static void start_children(FFStream *feed)
333 for (; feed; feed = feed->next) {
334 if (feed->child_argv && !feed->pid) {
335 feed->pid_start = time(0);
340 fprintf(stderr, "Unable to create children\n");
349 for (i = 3; i < 256; i++) {
353 if (!ffserver_debug) {
354 i = open("/dev/null", O_RDWR);
363 pstrcpy(pathname, sizeof(pathname), my_program_name);
365 slash = strrchr(pathname, '/');
371 strcpy(slash, "ffmpeg");
373 execvp(pathname, feed->child_argv);
381 /* open a listening socket */
382 static int socket_open_listen(struct sockaddr_in *my_addr)
386 server_fd = socket(AF_INET,SOCK_STREAM,0);
393 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
395 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
401 if (listen (server_fd, 5) < 0) {
406 fcntl(server_fd, F_SETFL, O_NONBLOCK);
412 /* main loop of the http server */
413 static int http_server(void)
415 int server_fd, ret, rtsp_server_fd, delay, delay1;
416 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
417 HTTPContext *c, *c_next;
419 server_fd = socket_open_listen(&my_http_addr);
423 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
424 if (rtsp_server_fd < 0)
427 http_log("ffserver started.\n");
429 start_children(first_feed);
431 first_http_ctx = NULL;
433 first_http_ctx = NULL;
435 poll_entry = poll_table;
436 poll_entry->fd = server_fd;
437 poll_entry->events = POLLIN;
440 poll_entry->fd = rtsp_server_fd;
441 poll_entry->events = POLLIN;
444 /* wait for events on each HTTP handle */
451 case HTTPSTATE_SEND_HEADER:
452 case RTSPSTATE_SEND_REPLY:
453 c->poll_entry = poll_entry;
455 poll_entry->events = POLLOUT;
458 case HTTPSTATE_SEND_DATA_HEADER:
459 case HTTPSTATE_SEND_DATA:
460 case HTTPSTATE_SEND_DATA_TRAILER:
461 if (!c->is_packetized) {
462 /* for TCP, we output as much as we can (may need to put a limit) */
463 c->poll_entry = poll_entry;
465 poll_entry->events = POLLOUT;
468 /* not strictly correct, but currently cannot add
469 more than one fd in poll entry */
473 case HTTPSTATE_WAIT_REQUEST:
474 case HTTPSTATE_RECEIVE_DATA:
475 case HTTPSTATE_WAIT_FEED:
476 case RTSPSTATE_WAIT_REQUEST:
477 /* need to catch errors */
478 c->poll_entry = poll_entry;
480 poll_entry->events = POLLIN;/* Maybe this will work */
484 c->poll_entry = NULL;
485 delay1 = compute_send_delay(c);
489 case HTTPSTATE_WAIT_SHORT:
490 c->poll_entry = NULL;
491 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
496 c->poll_entry = NULL;
502 /* wait for an event on one connection. We poll at least every
503 second to handle timeouts */
505 ret = poll(poll_table, poll_entry - poll_table, delay);
508 cur_time = gettime_ms();
510 if (need_to_start_children) {
511 need_to_start_children = 0;
512 start_children(first_feed);
515 /* now handle the events */
516 for(c = first_http_ctx; c != NULL; c = c_next) {
518 if (handle_connection(c) < 0) {
519 /* close and free the connection */
525 poll_entry = poll_table;
526 /* new HTTP connection request ? */
527 if (poll_entry->revents & POLLIN) {
528 new_connection(server_fd, 0);
531 /* new RTSP connection request ? */
532 if (poll_entry->revents & POLLIN) {
533 new_connection(rtsp_server_fd, 1);
538 /* start waiting for a new HTTP/RTSP request */
539 static void start_wait_request(HTTPContext *c, int is_rtsp)
541 c->buffer_ptr = c->buffer;
542 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
545 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
546 c->state = RTSPSTATE_WAIT_REQUEST;
548 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
549 c->state = HTTPSTATE_WAIT_REQUEST;
553 static void new_connection(int server_fd, int is_rtsp)
555 struct sockaddr_in from_addr;
557 HTTPContext *c = NULL;
559 len = sizeof(from_addr);
560 fd = accept(server_fd, (struct sockaddr *)&from_addr,
564 fcntl(fd, F_SETFL, O_NONBLOCK);
566 /* XXX: should output a warning page when coming
567 close to the connection limit */
568 if (nb_connections >= nb_max_connections)
571 /* add a new connection */
572 c = av_mallocz(sizeof(HTTPContext));
576 c->next = first_http_ctx;
579 c->poll_entry = NULL;
580 c->from_addr = from_addr;
581 c->buffer_size = IOBUFFER_INIT_SIZE;
582 c->buffer = av_malloc(c->buffer_size);
587 start_wait_request(c, is_rtsp);
599 static void close_connection(HTTPContext *c)
601 HTTPContext **cp, *c1;
603 AVFormatContext *ctx;
607 /* remove connection from list */
608 cp = &first_http_ctx;
609 while ((*cp) != NULL) {
618 /* remove connection associated resources */
622 /* close each frame parser */
623 for(i=0;i<c->fmt_in->nb_streams;i++) {
624 st = c->fmt_in->streams[i];
625 if (st->codec.codec) {
626 avcodec_close(&st->codec);
629 av_close_input_file(c->fmt_in);
632 /* free RTP output streams if any */
635 nb_streams = c->stream->nb_streams;
637 for(i=0;i<nb_streams;i++) {
640 av_write_trailer(ctx);
643 h = c->rtp_handles[i];
649 nb_bandwidth -= c->bandwidth;
650 av_freep(&c->pb_buffer);
656 static int handle_connection(HTTPContext *c)
661 case HTTPSTATE_WAIT_REQUEST:
662 case RTSPSTATE_WAIT_REQUEST:
664 if ((c->timeout - cur_time) < 0)
666 if (c->poll_entry->revents & (POLLERR | POLLHUP))
669 /* no need to read if no events */
670 if (!(c->poll_entry->revents & POLLIN))
673 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
675 if (errno != EAGAIN && errno != EINTR)
677 } else if (len == 0) {
680 /* search for end of request. XXX: not fully correct since garbage could come after the end */
682 c->buffer_ptr += len;
684 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
685 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
686 /* request found : parse it and reply */
687 if (c->state == HTTPSTATE_WAIT_REQUEST) {
688 ret = http_parse_request(c);
690 ret = rtsp_parse_request(c);
694 } else if (ptr >= c->buffer_end) {
695 /* request too long: cannot do anything */
701 case HTTPSTATE_SEND_HEADER:
702 if (c->poll_entry->revents & (POLLERR | POLLHUP))
705 /* no need to write if no events */
706 if (!(c->poll_entry->revents & POLLOUT))
708 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
710 if (errno != EAGAIN && errno != EINTR) {
711 /* error : close connection */
712 av_freep(&c->pb_buffer);
716 c->buffer_ptr += len;
718 c->stream->bytes_served += len;
719 c->data_count += len;
720 if (c->buffer_ptr >= c->buffer_end) {
721 av_freep(&c->pb_buffer);
726 /* all the buffer was sent : synchronize to the incoming stream */
727 c->state = HTTPSTATE_SEND_DATA_HEADER;
728 c->buffer_ptr = c->buffer_end = c->buffer;
733 case HTTPSTATE_SEND_DATA:
734 case HTTPSTATE_SEND_DATA_HEADER:
735 case HTTPSTATE_SEND_DATA_TRAILER:
736 /* for packetized output, we consider we can always write (the
737 input streams sets the speed). It may be better to verify
738 that we do not rely too much on the kernel queues */
739 if (!c->is_packetized) {
740 if (c->poll_entry->revents & (POLLERR | POLLHUP))
743 /* no need to read if no events */
744 if (!(c->poll_entry->revents & POLLOUT))
747 if (http_send_data(c) < 0)
750 case HTTPSTATE_RECEIVE_DATA:
751 /* no need to read if no events */
752 if (c->poll_entry->revents & (POLLERR | POLLHUP))
754 if (!(c->poll_entry->revents & POLLIN))
756 if (http_receive_data(c) < 0)
759 case HTTPSTATE_WAIT_FEED:
760 /* no need to read if no events */
761 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
764 /* nothing to do, we'll be waken up by incoming feed packets */
768 /* if the delay expired, we can send new packets */
769 if (compute_send_delay(c) <= 0)
770 c->state = HTTPSTATE_SEND_DATA;
772 case HTTPSTATE_WAIT_SHORT:
773 /* just return back to send data */
774 c->state = HTTPSTATE_SEND_DATA;
777 case RTSPSTATE_SEND_REPLY:
778 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
779 av_freep(&c->pb_buffer);
782 /* no need to write if no events */
783 if (!(c->poll_entry->revents & POLLOUT))
785 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
787 if (errno != EAGAIN && errno != EINTR) {
788 /* error : close connection */
789 av_freep(&c->pb_buffer);
793 c->buffer_ptr += len;
794 c->data_count += len;
795 if (c->buffer_ptr >= c->buffer_end) {
796 /* all the buffer was sent : wait for a new request */
797 av_freep(&c->pb_buffer);
798 start_wait_request(c, 1);
802 case HTTPSTATE_READY:
811 static int extract_rates(char *rates, int ratelen, const char *request)
815 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
816 if (strncasecmp(p, "Pragma:", 7) == 0) {
817 const char *q = p + 7;
819 while (*q && *q != '\n' && isspace(*q))
822 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
828 memset(rates, 0xff, ratelen);
831 while (*q && *q != '\n' && *q != ':')
834 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
838 if (stream_no < ratelen && stream_no >= 0) {
839 rates[stream_no] = rate_no;
842 while (*q && *q != '\n' && !isspace(*q))
859 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
862 int best_bitrate = 100000000;
865 for (i = 0; i < feed->nb_streams; i++) {
866 AVCodecContext *feed_codec = &feed->streams[i]->codec;
868 if (feed_codec->codec_id != codec->codec_id ||
869 feed_codec->sample_rate != codec->sample_rate ||
870 feed_codec->width != codec->width ||
871 feed_codec->height != codec->height) {
875 /* Potential stream */
877 /* We want the fastest stream less than bit_rate, or the slowest
878 * faster than bit_rate
881 if (feed_codec->bit_rate <= bit_rate) {
882 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
883 best_bitrate = feed_codec->bit_rate;
887 if (feed_codec->bit_rate < best_bitrate) {
888 best_bitrate = feed_codec->bit_rate;
897 static int modify_current_stream(HTTPContext *c, char *rates)
900 FFStream *req = c->stream;
901 int action_required = 0;
903 for (i = 0; i < req->nb_streams; i++) {
904 AVCodecContext *codec = &req->streams[i]->codec;
908 c->switch_feed_streams[i] = req->feed_streams[i];
911 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
914 /* Wants off or slow */
915 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
917 /* This doesn't work well when it turns off the only stream! */
918 c->switch_feed_streams[i] = -2;
919 c->feed_streams[i] = -2;
924 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
928 return action_required;
932 static void do_switch_stream(HTTPContext *c, int i)
934 if (c->switch_feed_streams[i] >= 0) {
936 c->feed_streams[i] = c->switch_feed_streams[i];
939 /* Now update the stream */
941 c->switch_feed_streams[i] = -1;
944 /* XXX: factorize in utils.c ? */
945 /* XXX: take care with different space meaning */
946 static void skip_spaces(const char **pp)
950 while (*p == ' ' || *p == '\t')
955 static void get_word(char *buf, int buf_size, const char **pp)
963 while (!isspace(*p) && *p != '\0') {
964 if ((q - buf) < buf_size - 1)
973 static int validate_acl(FFStream *stream, HTTPContext *c)
975 enum IPAddressAction last_action = IP_DENY;
977 struct in_addr *src = &c->from_addr.sin_addr;
979 for (acl = stream->acl; acl; acl = acl->next) {
980 if (src->s_addr >= acl->first.s_addr && src->s_addr <= acl->last.s_addr) {
981 return (acl->action == IP_ALLOW) ? 1 : 0;
983 last_action = acl->action;
986 /* Nothing matched, so return not the last action */
987 return (last_action == IP_DENY) ? 1 : 0;
990 /* parse http request and prepare header */
991 static int http_parse_request(HTTPContext *c)
996 int doing_asf_redirector;
998 int doing_rtsp_redirector;
1000 char info[1024], *filename;
1004 const char *mime_type;
1008 char *useragent = 0;
1011 get_word(cmd, sizeof(cmd), (const char **)&p);
1012 pstrcpy(c->method, sizeof(c->method), cmd);
1014 if (!strcmp(cmd, "GET"))
1016 else if (!strcmp(cmd, "POST"))
1021 get_word(url, sizeof(url), (const char **)&p);
1022 pstrcpy(c->url, sizeof(c->url), url);
1024 get_word(protocol, sizeof(protocol), (const char **)&p);
1025 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1028 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
1030 /* find the filename and the optional info string in the request */
1037 pstrcpy(info, sizeof(info), p);
1043 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1044 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1046 if (*useragent && *useragent != '\n' && isspace(*useragent))
1050 p = strchr(p, '\n');
1057 if (strlen(filename) > 4 && strcmp(".asx", filename + strlen(filename) - 4) == 0) {
1059 filename[strlen(filename)-1] = 'f';
1064 if (strlen(filename) > 4 && strcmp(".asf", filename + strlen(filename) - 4) == 0 &&
1065 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1066 /* if this isn't WMP or lookalike, return the redirector file */
1067 doing_asf_redirector = 1;
1069 doing_asf_redirector = 0;
1072 if (strlen(filename) > 4 &&
1073 (strcmp(".rpm", filename + strlen(filename) - 4) == 0 ||
1074 strcmp(".ram", filename + strlen(filename) - 4) == 0)) {
1076 strcpy(filename + strlen(filename)-2, "m");
1081 if (strlen(filename) > 5 &&
1082 strcmp(".rtsp", filename + strlen(filename) - 5) == 0) {
1087 doing_rtsp_redirector = 1;
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(url, sizeof(url), stream->filename);
1105 doing_rtsp_redirector = 0;
1108 stream = first_stream;
1109 while (stream != NULL) {
1110 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1112 stream = stream->next;
1114 if (stream == NULL) {
1115 sprintf(msg, "File '%s' not found", url);
1120 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1121 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1123 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1124 c->http_error = 301;
1126 q += sprintf(q, "HTTP/1.0 301 Moved\r\n");
1127 q += sprintf(q, "Location: %s\r\n", stream->feed_filename);
1128 q += sprintf(q, "Content-type: text/html\r\n");
1129 q += sprintf(q, "\r\n");
1130 q += sprintf(q, "<html><head><title>Moved</title></head><body>\r\n");
1131 q += sprintf(q, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1132 q += sprintf(q, "</body></html>\r\n");
1134 /* prepare output buffer */
1135 c->buffer_ptr = c->buffer;
1137 c->state = HTTPSTATE_SEND_HEADER;
1141 /* If this is WMP, get the rate information */
1142 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1143 if (modify_current_stream(c, ratebuf)) {
1144 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1145 if (c->switch_feed_streams[i] >= 0)
1146 do_switch_stream(c, i);
1151 if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1152 /* See if we meet the bandwidth requirements */
1153 for(i=0;i<stream->nb_streams;i++) {
1154 AVStream *st = stream->streams[i];
1155 switch(st->codec.codec_type) {
1156 case CODEC_TYPE_AUDIO:
1157 c->bandwidth += st->codec.bit_rate;
1159 case CODEC_TYPE_VIDEO:
1160 c->bandwidth += st->codec.bit_rate;
1168 c->bandwidth /= 1000;
1169 nb_bandwidth += c->bandwidth;
1171 if (post == 0 && nb_max_bandwidth < nb_bandwidth) {
1172 c->http_error = 200;
1174 q += sprintf(q, "HTTP/1.0 200 Server too busy\r\n");
1175 q += sprintf(q, "Content-type: text/html\r\n");
1176 q += sprintf(q, "\r\n");
1177 q += sprintf(q, "<html><head><title>Too busy</title></head><body>\r\n");
1178 q += sprintf(q, "The server is too busy to serve your request at this time.<p>\r\n");
1179 q += sprintf(q, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
1180 nb_bandwidth, nb_max_bandwidth);
1181 q += sprintf(q, "</body></html>\r\n");
1183 /* prepare output buffer */
1184 c->buffer_ptr = c->buffer;
1186 c->state = HTTPSTATE_SEND_HEADER;
1190 if (doing_asx || doing_ram || doing_asf_redirector ||
1191 doing_rtsp_redirector) {
1194 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1195 if (strncasecmp(p, "Host:", 5) == 0) {
1199 p = strchr(p, '\n');
1210 while (isspace(*hostinfo))
1213 eoh = strchr(hostinfo, '\n');
1215 if (eoh[-1] == '\r')
1218 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1219 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1220 hostbuf[eoh - hostinfo] = 0;
1222 c->http_error = 200;
1225 q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n");
1226 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1227 q += sprintf(q, "\r\n");
1228 q += sprintf(q, "<ASX Version=\"3\">\r\n");
1229 q += sprintf(q, "<!-- Autogenerated by ffserver -->\r\n");
1230 q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1231 hostbuf, filename, info);
1232 q += sprintf(q, "</ASX>\r\n");
1233 } else if (doing_ram) {
1234 q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n");
1235 q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n");
1236 q += sprintf(q, "\r\n");
1237 q += sprintf(q, "# Autogenerated by ffserver\r\n");
1238 q += sprintf(q, "http://%s/%s%s\r\n",
1239 hostbuf, filename, info);
1240 } else if (doing_asf_redirector) {
1241 q += sprintf(q, "HTTP/1.0 200 ASF Redirect follows\r\n");
1242 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1243 q += sprintf(q, "\r\n");
1244 q += sprintf(q, "[Reference]\r\n");
1245 q += sprintf(q, "Ref1=http://%s/%s%s\r\n",
1246 hostbuf, filename, info);
1247 } else if (doing_rtsp_redirector) {
1248 char hostname[256], *p;
1249 /* extract only hostname */
1250 pstrcpy(hostname, sizeof(hostname), hostbuf);
1251 p = strrchr(hostname, ':');
1254 q += sprintf(q, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1255 /* XXX: incorrect mime type ? */
1256 q += sprintf(q, "Content-type: application/x-rtsp\r\n");
1257 q += sprintf(q, "\r\n");
1258 q += sprintf(q, "rtsp://%s:%d/%s\r\n",
1259 hostname, ntohs(my_rtsp_addr.sin_port),
1265 /* prepare output buffer */
1266 c->buffer_ptr = c->buffer;
1268 c->state = HTTPSTATE_SEND_HEADER;
1274 sprintf(msg, "ASX/RAM file not handled");
1278 stream->conns_served++;
1280 /* XXX: add there authenticate and IP match */
1283 /* if post, it means a feed is being sent */
1284 if (!stream->is_feed) {
1285 /* However it might be a status report from WMP! Lets log the data
1286 * as it might come in handy one day
1291 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1292 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1296 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1297 client_id = strtol(p + 18, 0, 10);
1299 p = strchr(p, '\n');
1307 char *eol = strchr(logline, '\n');
1312 if (eol[-1] == '\r')
1314 http_log("%.*s\n", eol - logline, logline);
1315 c->suppress_log = 1;
1320 http_log("\nGot request:\n%s\n", c->buffer);
1323 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1326 /* Now we have to find the client_id */
1327 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1328 if (wmpc->wmp_client_id == client_id)
1333 if (modify_current_stream(wmpc, ratebuf)) {
1334 wmpc->switch_pending = 1;
1339 sprintf(msg, "POST command not handled");
1342 if (http_start_receive_data(c) < 0) {
1343 sprintf(msg, "could not open feed");
1347 c->state = HTTPSTATE_RECEIVE_DATA;
1352 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1353 http_log("\nGot request:\n%s\n", c->buffer);
1357 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1360 /* open input stream */
1361 if (open_input_stream(c, info) < 0) {
1362 sprintf(msg, "Input stream corresponding to '%s' not found", url);
1366 /* prepare http header */
1368 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1369 mime_type = c->stream->fmt->mime_type;
1371 mime_type = "application/x-octet_stream";
1372 q += sprintf(q, "Pragma: no-cache\r\n");
1374 /* for asf, we need extra headers */
1375 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1376 /* Need to allocate a client id */
1378 c->wmp_client_id = random() & 0x7fffffff;
1380 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);
1381 mime_type = "application/octet-stream";
1383 q += sprintf(q, "Content-Type: %s\r\n", mime_type);
1384 q += sprintf(q, "\r\n");
1386 /* prepare output buffer */
1388 c->buffer_ptr = c->buffer;
1390 c->state = HTTPSTATE_SEND_HEADER;
1393 c->http_error = 404;
1395 q += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
1396 q += sprintf(q, "Content-type: %s\r\n", "text/html");
1397 q += sprintf(q, "\r\n");
1398 q += sprintf(q, "<HTML>\n");
1399 q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1400 q += sprintf(q, "<BODY>%s</BODY>\n", msg);
1401 q += sprintf(q, "</HTML>\n");
1403 /* prepare output buffer */
1404 c->buffer_ptr = c->buffer;
1406 c->state = HTTPSTATE_SEND_HEADER;
1410 c->http_error = 200; /* horrible : we use this value to avoid
1411 going to the send data state */
1412 c->state = HTTPSTATE_SEND_HEADER;
1416 static void fmt_bytecount(ByteIOContext *pb, INT64 count)
1418 static const char *suffix = " kMGTP";
1421 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1424 url_fprintf(pb, "%lld%c", count, *s);
1427 static void compute_stats(HTTPContext *c)
1434 ByteIOContext pb1, *pb = &pb1;
1436 if (url_open_dyn_buf(pb) < 0) {
1437 /* XXX: return an error ? */
1438 c->buffer_ptr = c->buffer;
1439 c->buffer_end = c->buffer;
1443 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1444 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1445 url_fprintf(pb, "Pragma: no-cache\r\n");
1446 url_fprintf(pb, "\r\n");
1448 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1449 if (c->stream->feed_filename) {
1450 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1452 url_fprintf(pb, "</HEAD>\n<BODY>");
1453 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1455 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1456 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1457 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");
1458 stream = first_stream;
1459 while (stream != NULL) {
1460 char sfilename[1024];
1463 if (stream->feed != stream) {
1464 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1465 eosf = sfilename + strlen(sfilename);
1466 if (eosf - sfilename >= 4) {
1467 if (strcmp(eosf - 4, ".asf") == 0) {
1468 strcpy(eosf - 4, ".asx");
1469 } else if (strcmp(eosf - 3, ".rm") == 0) {
1470 strcpy(eosf - 3, ".ram");
1471 } else if (stream->fmt == &rtp_mux) {
1472 /* generate a sample RTSP director - maybe should
1473 generate a .sdp file ? */
1474 eosf = strrchr(sfilename, '.');
1476 eosf = sfilename + strlen(sfilename);
1477 strcpy(eosf, ".rtsp");
1481 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1482 sfilename, stream->filename);
1483 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1484 stream->conns_served);
1485 fmt_bytecount(pb, stream->bytes_served);
1486 switch(stream->stream_type) {
1487 case STREAM_TYPE_LIVE:
1489 int audio_bit_rate = 0;
1490 int video_bit_rate = 0;
1491 char *audio_codec_name = "";
1492 char *video_codec_name = "";
1493 char *audio_codec_name_extra = "";
1494 char *video_codec_name_extra = "";
1496 for(i=0;i<stream->nb_streams;i++) {
1497 AVStream *st = stream->streams[i];
1498 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1499 switch(st->codec.codec_type) {
1500 case CODEC_TYPE_AUDIO:
1501 audio_bit_rate += st->codec.bit_rate;
1503 if (*audio_codec_name)
1504 audio_codec_name_extra = "...";
1505 audio_codec_name = codec->name;
1508 case CODEC_TYPE_VIDEO:
1509 video_bit_rate += st->codec.bit_rate;
1511 if (*video_codec_name)
1512 video_codec_name_extra = "...";
1513 video_codec_name = codec->name;
1520 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",
1522 (audio_bit_rate + video_bit_rate) / 1000,
1523 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1524 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1526 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1528 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1530 url_fprintf(pb, "\n");
1534 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1538 stream = stream->next;
1540 url_fprintf(pb, "</TABLE>\n");
1542 stream = first_stream;
1543 while (stream != NULL) {
1544 if (stream->feed == stream) {
1545 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1547 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1549 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1554 /* This is somewhat linux specific I guess */
1555 snprintf(ps_cmd, sizeof(ps_cmd),
1556 "ps -o \"%%cpu,cputime\" --no-headers %d",
1559 pid_stat = popen(ps_cmd, "r");
1564 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1566 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1574 url_fprintf(pb, "<p>");
1576 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");
1578 for (i = 0; i < stream->nb_streams; i++) {
1579 AVStream *st = stream->streams[i];
1580 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1581 char *type = "unknown";
1582 char parameters[64];
1586 switch(st->codec.codec_type) {
1587 case CODEC_TYPE_AUDIO:
1590 case CODEC_TYPE_VIDEO:
1592 sprintf(parameters, "%dx%d, q=%d-%d, fps=%d", st->codec.width, st->codec.height,
1593 st->codec.qmin, st->codec.qmax, st->codec.frame_rate / FRAME_RATE_BASE);
1598 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1599 i, type, st->codec.bit_rate/1000, codec ? codec->name : "", parameters);
1601 url_fprintf(pb, "</table>\n");
1604 stream = stream->next;
1610 AVCodecContext *enc;
1614 stream = first_feed;
1615 while (stream != NULL) {
1616 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1617 url_fprintf(pb, "<TABLE>\n");
1618 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1619 for(i=0;i<stream->nb_streams;i++) {
1620 AVStream *st = stream->streams[i];
1621 FeedData *fdata = st->priv_data;
1624 avcodec_string(buf, sizeof(buf), enc);
1625 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1626 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1627 avg /= enc->frame_size;
1628 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1629 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1631 url_fprintf(pb, "</TABLE>\n");
1632 stream = stream->next_feed;
1637 /* connection status */
1638 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1640 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1641 nb_connections, nb_max_connections);
1643 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1644 nb_bandwidth, nb_max_bandwidth);
1646 url_fprintf(pb, "<TABLE>\n");
1647 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");
1648 c1 = first_http_ctx;
1650 while (c1 != NULL) {
1656 for (j = 0; j < c1->stream->nb_streams; j++) {
1657 if (!c1->stream->feed) {
1658 bitrate += c1->stream->streams[j]->codec.bit_rate;
1660 if (c1->feed_streams[j] >= 0) {
1661 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec.bit_rate;
1668 p = inet_ntoa(c1->from_addr.sin_addr);
1669 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1671 c1->stream ? c1->stream->filename : "",
1672 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1675 http_state[c1->state]);
1676 fmt_bytecount(pb, bitrate);
1677 url_fprintf(pb, "<td align=right>");
1678 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1679 url_fprintf(pb, "<td align=right>");
1680 fmt_bytecount(pb, c1->data_count);
1681 url_fprintf(pb, "\n");
1684 url_fprintf(pb, "</TABLE>\n");
1689 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1690 url_fprintf(pb, "</BODY>\n</HTML>\n");
1692 len = url_close_dyn_buf(pb, &c->pb_buffer);
1693 c->buffer_ptr = c->pb_buffer;
1694 c->buffer_end = c->pb_buffer + len;
1697 /* check if the parser needs to be opened for stream i */
1698 static void open_parser(AVFormatContext *s, int i)
1700 AVStream *st = s->streams[i];
1703 if (!st->codec.codec) {
1704 codec = avcodec_find_decoder(st->codec.codec_id);
1705 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1706 st->codec.parse_only = 1;
1707 if (avcodec_open(&st->codec, codec) < 0) {
1708 st->codec.parse_only = 0;
1714 static int open_input_stream(HTTPContext *c, const char *info)
1717 char input_filename[1024];
1722 /* find file name */
1723 if (c->stream->feed) {
1724 strcpy(input_filename, c->stream->feed->feed_filename);
1725 buf_size = FFM_PACKET_SIZE;
1726 /* compute position (absolute time) */
1727 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1728 stream_pos = parse_date(buf, 0);
1729 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1730 int prebuffer = strtol(buf, 0, 10);
1731 stream_pos = av_gettime() - prebuffer * 1000000;
1733 stream_pos = av_gettime() - c->stream->prebuffer * 1000;
1736 strcpy(input_filename, c->stream->feed_filename);
1738 /* compute position (relative time) */
1739 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1740 stream_pos = parse_date(buf, 1);
1745 if (input_filename[0] == '\0')
1749 { time_t when = stream_pos / 1000000;
1750 http_log("Stream pos = %lld, time=%s", stream_pos, ctime(&when));
1755 if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0) {
1756 http_log("%s not found", input_filename);
1761 /* open each parser */
1762 for(i=0;i<s->nb_streams;i++)
1765 /* choose stream as clock source (we favorize video stream if
1766 present) for packet sending */
1767 c->pts_stream_index = 0;
1768 for(i=0;i<c->stream->nb_streams;i++) {
1769 if (c->pts_stream_index == 0 &&
1770 c->stream->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO) {
1771 c->pts_stream_index = i;
1775 if (c->fmt_in->iformat->read_seek) {
1776 c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
1778 /* set the start time (needed for maxtime and RTP packet timing) */
1779 c->start_time = cur_time;
1780 c->first_pts = AV_NOPTS_VALUE;
1781 printf("stream %s opened pos=%0.6f\n", input_filename, stream_pos / 1000000.0);
1785 /* currently desactivated because the new PTS handling is not
1787 //#define AV_READ_FRAME
1788 #ifdef AV_READ_FRAME
1790 /* XXX: generalize that in ffmpeg for picture/audio/data. Currently
1791 the return packet MUST NOT be freed */
1792 int av_read_frame(AVFormatContext *s, AVPacket *pkt)
1795 int len, ret, old_nb_streams, i;
1797 /* see if remaining frames must be parsed */
1799 if (s->cur_len > 0) {
1800 st = s->streams[s->cur_pkt.stream_index];
1801 len = avcodec_parse_frame(&st->codec, &pkt->data, &pkt->size,
1802 s->cur_ptr, s->cur_len);
1804 /* error: get next packet */
1810 /* init pts counter if not done */
1811 if (st->pts.den == 0) {
1812 switch(st->codec.codec_type) {
1813 case CODEC_TYPE_AUDIO:
1814 st->pts_incr = (INT64)s->pts_den;
1815 av_frac_init(&st->pts, st->pts.val, 0,
1816 (INT64)s->pts_num * st->codec.sample_rate);
1818 case CODEC_TYPE_VIDEO:
1819 st->pts_incr = (INT64)s->pts_den * FRAME_RATE_BASE;
1820 av_frac_init(&st->pts, st->pts.val, 0,
1821 (INT64)s->pts_num * st->codec.frame_rate);
1828 /* a frame was read: return it */
1829 pkt->pts = st->pts.val;
1831 printf("add pts=%Lx num=%Lx den=%Lx incr=%Lx\n",
1832 st->pts.val, st->pts.num, st->pts.den, st->pts_incr);
1834 switch(st->codec.codec_type) {
1835 case CODEC_TYPE_AUDIO:
1836 av_frac_add(&st->pts, st->pts_incr * st->codec.frame_size);
1838 case CODEC_TYPE_VIDEO:
1839 av_frac_add(&st->pts, st->pts_incr);
1844 pkt->stream_index = s->cur_pkt.stream_index;
1845 /* we use the codec indication because it is
1846 more accurate than the demux flags */
1848 if (st->codec.key_frame)
1849 pkt->flags |= PKT_FLAG_KEY;
1854 /* free previous packet */
1855 av_free_packet(&s->cur_pkt);
1857 old_nb_streams = s->nb_streams;
1858 ret = av_read_packet(s, &s->cur_pkt);
1861 /* open parsers for each new streams */
1862 for(i = old_nb_streams; i < s->nb_streams; i++)
1864 st = s->streams[s->cur_pkt.stream_index];
1866 /* update current pts (XXX: dts handling) from packet, or
1867 use current pts if none given */
1868 if (s->cur_pkt.pts != AV_NOPTS_VALUE) {
1869 av_frac_set(&st->pts, s->cur_pkt.pts);
1871 s->cur_pkt.pts = st->pts.val;
1873 if (!st->codec.codec) {
1874 /* no codec opened: just return the raw packet */
1877 /* no codec opened: just update the pts by considering we
1878 have one frame and free the packet */
1879 if (st->pts.den == 0) {
1880 switch(st->codec.codec_type) {
1881 case CODEC_TYPE_AUDIO:
1882 st->pts_incr = (INT64)s->pts_den * st->codec.frame_size;
1883 av_frac_init(&st->pts, st->pts.val, 0,
1884 (INT64)s->pts_num * st->codec.sample_rate);
1886 case CODEC_TYPE_VIDEO:
1887 st->pts_incr = (INT64)s->pts_den * FRAME_RATE_BASE;
1888 av_frac_init(&st->pts, st->pts.val, 0,
1889 (INT64)s->pts_num * st->codec.frame_rate);
1895 av_frac_add(&st->pts, st->pts_incr);
1898 s->cur_ptr = s->cur_pkt.data;
1899 s->cur_len = s->cur_pkt.size;
1905 static int compute_send_delay(HTTPContext *c)
1907 INT64 cur_pts, delta_pts, next_pts;
1910 /* compute current pts value from system time */
1911 cur_pts = ((INT64)(cur_time - c->start_time) * c->fmt_in->pts_den) /
1912 (c->fmt_in->pts_num * 1000LL);
1913 /* compute the delta from the stream we choose as
1914 main clock (we do that to avoid using explicit
1915 buffers to do exact packet reordering for each
1917 /* XXX: really need to fix the number of streams */
1918 if (c->pts_stream_index >= c->fmt_in->nb_streams)
1921 next_pts = c->fmt_in->streams[c->pts_stream_index]->pts.val;
1922 delta_pts = next_pts - cur_pts;
1923 if (delta_pts <= 0) {
1926 delay1 = (delta_pts * 1000 * c->fmt_in->pts_num) / c->fmt_in->pts_den;
1932 /* just fall backs */
1933 int av_read_frame(AVFormatContext *s, AVPacket *pkt)
1935 return av_read_packet(s, pkt);
1938 static int compute_send_delay(HTTPContext *c)
1945 static int http_prepare_data(HTTPContext *c)
1948 AVFormatContext *ctx;
1951 case HTTPSTATE_SEND_DATA_HEADER:
1952 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
1953 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
1955 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
1956 c->stream->comment);
1957 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
1958 c->stream->copyright);
1959 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
1962 /* open output stream by using specified codecs */
1963 c->fmt_ctx.oformat = c->stream->fmt;
1964 c->fmt_ctx.nb_streams = c->stream->nb_streams;
1965 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
1967 st = av_mallocz(sizeof(AVStream));
1968 c->fmt_ctx.streams[i] = st;
1969 /* if file or feed, then just take streams from FFStream struct */
1970 if (!c->stream->feed ||
1971 c->stream->feed == c->stream)
1972 memcpy(st, c->stream->streams[i], sizeof(AVStream));
1974 memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]],
1976 st->codec.frame_number = 0; /* XXX: should be done in
1977 AVStream, not in codec */
1979 c->got_key_frame = 0;
1981 /* prepare header and save header data in a stream */
1982 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
1983 /* XXX: potential leak */
1986 c->fmt_ctx.pb.is_streamed = 1;
1988 av_write_header(&c->fmt_ctx);
1990 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
1991 c->buffer_ptr = c->pb_buffer;
1992 c->buffer_end = c->pb_buffer + len;
1994 c->state = HTTPSTATE_SEND_DATA;
1995 c->last_packet_sent = 0;
1997 case HTTPSTATE_SEND_DATA:
1998 /* find a new packet */
2002 /* read a packet from the input stream */
2003 if (c->stream->feed) {
2004 ffm_set_write_index(c->fmt_in,
2005 c->stream->feed->feed_write_index,
2006 c->stream->feed->feed_size);
2009 if (c->stream->max_time &&
2010 c->stream->max_time + c->start_time - cur_time < 0) {
2011 /* We have timed out */
2012 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2014 if (c->is_packetized) {
2015 if (compute_send_delay(c) > 0) {
2016 c->state = HTTPSTATE_WAIT;
2017 return 1; /* state changed */
2020 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2021 if (c->stream->feed && c->stream->feed->feed_opened) {
2022 /* if coming from feed, it means we reached the end of the
2023 ffm file, so must wait for more data */
2024 c->state = HTTPSTATE_WAIT_FEED;
2025 return 1; /* state changed */
2027 /* must send trailer now because eof or error */
2028 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2031 /* update first pts if needed */
2032 if (c->first_pts == AV_NOPTS_VALUE)
2033 c->first_pts = pkt.pts;
2035 /* send it to the appropriate stream */
2036 if (c->stream->feed) {
2037 /* if coming from a feed, select the right stream */
2038 if (c->switch_pending) {
2039 c->switch_pending = 0;
2040 for(i=0;i<c->stream->nb_streams;i++) {
2041 if (c->switch_feed_streams[i] == pkt.stream_index) {
2042 if (pkt.flags & PKT_FLAG_KEY) {
2043 do_switch_stream(c, i);
2046 if (c->switch_feed_streams[i] >= 0) {
2047 c->switch_pending = 1;
2051 for(i=0;i<c->stream->nb_streams;i++) {
2052 if (c->feed_streams[i] == pkt.stream_index) {
2053 pkt.stream_index = i;
2054 if (pkt.flags & PKT_FLAG_KEY) {
2055 c->got_key_frame |= 1 << i;
2057 /* See if we have all the key frames, then
2058 * we start to send. This logic is not quite
2059 * right, but it works for the case of a
2060 * single video stream with one or more
2061 * audio streams (for which every frame is
2062 * typically a key frame).
2064 if (!c->stream->send_on_key ||
2065 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2071 AVCodecContext *codec;
2074 /* specific handling for RTP: we use several
2075 output stream (one for each RTP
2076 connection). XXX: need more abstract handling */
2077 if (c->is_packetized) {
2078 c->packet_stream_index = pkt.stream_index;
2079 ctx = c->rtp_ctx[c->packet_stream_index];
2080 codec = &ctx->streams[0]->codec;
2084 codec = &ctx->streams[pkt.stream_index]->codec;
2087 codec->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2090 if (codec->codec_type == CODEC_TYPE_AUDIO) {
2091 codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
2092 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
2096 if (c->is_packetized) {
2097 ret = url_open_dyn_packet_buf(&ctx->pb,
2098 url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]));
2099 c->packet_byte_count = 0;
2100 c->packet_start_time_us = av_gettime();
2102 ret = url_open_dyn_buf(&ctx->pb);
2105 /* XXX: potential leak */
2108 if (av_write_packet(ctx, &pkt, pkt.pts)) {
2109 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2112 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2113 c->buffer_ptr = c->pb_buffer;
2114 c->buffer_end = c->pb_buffer + len;
2116 codec->frame_number++;
2118 #ifndef AV_READ_FRAME
2119 av_free_packet(&pkt);
2126 case HTTPSTATE_SEND_DATA_TRAILER:
2127 /* last packet test ? */
2128 if (c->last_packet_sent || c->is_packetized)
2131 /* prepare header */
2132 if (url_open_dyn_buf(&ctx->pb) < 0) {
2133 /* XXX: potential leak */
2136 av_write_trailer(ctx);
2137 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2138 c->buffer_ptr = c->pb_buffer;
2139 c->buffer_end = c->pb_buffer + len;
2141 c->last_packet_sent = 1;
2148 #define SHORT_TERM_BANDWIDTH 8000000
2150 /* should convert the format at the same time */
2151 static int http_send_data(HTTPContext *c)
2155 while (c->buffer_ptr >= c->buffer_end) {
2156 av_freep(&c->pb_buffer);
2157 ret = http_prepare_data(c);
2160 else if (ret == 0) {
2163 /* state change requested */
2168 if (c->buffer_ptr < c->buffer_end) {
2169 if (c->is_packetized) {
2170 /* RTP/UDP data output */
2171 len = c->buffer_end - c->buffer_ptr;
2173 /* fail safe - should never happen */
2175 c->buffer_ptr = c->buffer_end;
2178 len = (c->buffer_ptr[0] << 24) |
2179 (c->buffer_ptr[1] << 16) |
2180 (c->buffer_ptr[2] << 8) |
2182 if (len > (c->buffer_end - c->buffer_ptr))
2185 /* short term bandwidth limitation */
2186 dt = av_gettime() - c->packet_start_time_us;
2190 if ((c->packet_byte_count + len) * (INT64)1000000 >=
2191 (SHORT_TERM_BANDWIDTH / 8) * (INT64)dt) {
2192 /* bandwidth overflow : wait at most one tick and retry */
2193 c->state = HTTPSTATE_WAIT_SHORT;
2198 url_write(c->rtp_handles[c->packet_stream_index],
2199 c->buffer_ptr, len);
2200 c->buffer_ptr += len;
2201 c->packet_byte_count += len;
2203 /* TCP data output */
2204 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2206 if (errno != EAGAIN && errno != EINTR) {
2207 /* error : close connection */
2213 c->buffer_ptr += len;
2216 c->data_count += len;
2217 update_datarate(&c->datarate, c->data_count);
2219 c->stream->bytes_served += len;
2224 static int http_start_receive_data(HTTPContext *c)
2228 if (c->stream->feed_opened)
2232 fd = open(c->stream->feed_filename, O_RDWR);
2237 c->stream->feed_write_index = ffm_read_write_index(fd);
2238 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2239 lseek(fd, 0, SEEK_SET);
2241 /* init buffer input */
2242 c->buffer_ptr = c->buffer;
2243 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2244 c->stream->feed_opened = 1;
2248 static int http_receive_data(HTTPContext *c)
2252 if (c->buffer_end > c->buffer_ptr) {
2255 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2257 if (errno != EAGAIN && errno != EINTR) {
2258 /* error : close connection */
2261 } else if (len == 0) {
2262 /* end of connection : close it */
2265 c->buffer_ptr += len;
2266 c->data_count += len;
2267 update_datarate(&c->datarate, c->data_count);
2271 if (c->buffer_ptr >= c->buffer_end) {
2272 FFStream *feed = c->stream;
2273 /* a packet has been received : write it in the store, except
2275 if (c->data_count > FFM_PACKET_SIZE) {
2277 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2278 /* XXX: use llseek or url_seek */
2279 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2280 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2282 feed->feed_write_index += FFM_PACKET_SIZE;
2283 /* update file size */
2284 if (feed->feed_write_index > c->stream->feed_size)
2285 feed->feed_size = feed->feed_write_index;
2287 /* handle wrap around if max file size reached */
2288 if (feed->feed_write_index >= c->stream->feed_max_size)
2289 feed->feed_write_index = FFM_PACKET_SIZE;
2292 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2294 /* wake up any waiting connections */
2295 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2296 if (c1->state == HTTPSTATE_WAIT_FEED &&
2297 c1->stream->feed == c->stream->feed) {
2298 c1->state = HTTPSTATE_SEND_DATA;
2302 /* We have a header in our hands that contains useful data */
2304 AVInputFormat *fmt_in;
2305 ByteIOContext *pb = &s.pb;
2308 memset(&s, 0, sizeof(s));
2310 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2311 pb->buf_end = c->buffer_end; /* ?? */
2312 pb->is_streamed = 1;
2314 /* use feed output format name to find corresponding input format */
2315 fmt_in = av_find_input_format(feed->fmt->name);
2319 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2323 if (fmt_in->read_header(&s, 0) < 0) {
2324 av_freep(&s.priv_data);
2328 /* Now we have the actual streams */
2329 if (s.nb_streams != feed->nb_streams) {
2330 av_freep(&s.priv_data);
2333 for (i = 0; i < s.nb_streams; i++) {
2334 memcpy(&feed->streams[i]->codec,
2335 &s.streams[i]->codec, sizeof(AVCodecContext));
2337 av_freep(&s.priv_data);
2339 c->buffer_ptr = c->buffer;
2344 c->stream->feed_opened = 0;
2349 /********************************************************************/
2352 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2359 switch(error_number) {
2360 #define DEF(n, c, s) case c: str = s; break;
2361 #include "rtspcodes.h"
2364 str = "Unknown Error";
2368 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2369 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2371 /* output GMT time */
2375 p = buf2 + strlen(p) - 1;
2378 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2381 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2383 rtsp_reply_header(c, error_number);
2384 url_fprintf(c->pb, "\r\n");
2387 static int rtsp_parse_request(HTTPContext *c)
2389 const char *p, *p1, *p2;
2396 RTSPHeader header1, *header = &header1;
2398 c->buffer_ptr[0] = '\0';
2401 get_word(cmd, sizeof(cmd), &p);
2402 get_word(url, sizeof(url), &p);
2403 get_word(protocol, sizeof(protocol), &p);
2405 pstrcpy(c->method, sizeof(c->method), cmd);
2406 pstrcpy(c->url, sizeof(c->url), url);
2407 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2410 if (url_open_dyn_buf(c->pb) < 0) {
2411 /* XXX: cannot do more */
2412 c->pb = NULL; /* safety */
2416 /* check version name */
2417 if (strcmp(protocol, "RTSP/1.0") != 0) {
2418 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2422 /* parse each header line */
2423 memset(header, 0, sizeof(RTSPHeader));
2424 /* skip to next line */
2425 while (*p != '\n' && *p != '\0')
2429 while (*p != '\0') {
2430 p1 = strchr(p, '\n');
2434 if (p2 > p && p2[-1] == '\r')
2436 /* skip empty line */
2440 if (len > sizeof(line) - 1)
2441 len = sizeof(line) - 1;
2442 memcpy(line, p, len);
2444 rtsp_parse_line(header, line);
2448 /* handle sequence number */
2449 c->seq = header->seq;
2451 if (!strcmp(cmd, "DESCRIBE")) {
2452 rtsp_cmd_describe(c, url);
2453 } else if (!strcmp(cmd, "SETUP")) {
2454 rtsp_cmd_setup(c, url, header);
2455 } else if (!strcmp(cmd, "PLAY")) {
2456 rtsp_cmd_play(c, url, header);
2457 } else if (!strcmp(cmd, "PAUSE")) {
2458 rtsp_cmd_pause(c, url, header);
2459 } else if (!strcmp(cmd, "TEARDOWN")) {
2460 rtsp_cmd_teardown(c, url, header);
2462 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2465 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2466 c->pb = NULL; /* safety */
2468 /* XXX: cannot do more */
2471 c->buffer_ptr = c->pb_buffer;
2472 c->buffer_end = c->pb_buffer + len;
2473 c->state = RTSPSTATE_SEND_REPLY;
2477 static int prepare_sdp_description(HTTPContext *c,
2478 FFStream *stream, UINT8 **pbuffer)
2480 ByteIOContext pb1, *pb = &pb1;
2481 struct sockaddr_in my_addr;
2482 int len, i, payload_type;
2483 const char *ipstr, *title, *mediatype;
2486 len = sizeof(my_addr);
2487 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2488 ipstr = inet_ntoa(my_addr.sin_addr);
2490 if (url_open_dyn_buf(pb) < 0)
2493 /* general media info */
2495 url_fprintf(pb, "v=0\n");
2496 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2497 title = stream->title;
2498 if (title[0] == '\0')
2500 url_fprintf(pb, "s=%s\n", title);
2501 if (stream->comment[0] != '\0')
2502 url_fprintf(pb, "i=%s\n", stream->comment);
2504 /* for each stream, we output the necessary info */
2505 for(i = 0; i < stream->nb_streams; i++) {
2506 st = stream->streams[i];
2507 switch(st->codec.codec_type) {
2508 case CODEC_TYPE_AUDIO:
2509 mediatype = "audio";
2511 case CODEC_TYPE_VIDEO:
2512 mediatype = "video";
2515 mediatype = "application";
2518 /* XXX: the port indication is not correct (but should be correct
2520 payload_type = rtp_get_payload_type(&st->codec);
2522 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2523 mediatype, 0, payload_type);
2524 url_fprintf(pb, "a=control:streamid=%d\n", i);
2526 return url_close_dyn_buf(pb, pbuffer);
2529 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2537 /* find which url is asked */
2538 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2543 for(stream = first_stream; stream != NULL; stream = stream->next) {
2544 if (!stream->is_feed && stream->fmt == &rtp_mux &&
2545 !strcmp(path, stream->filename)) {
2549 /* no stream found */
2550 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2554 /* prepare the media description in sdp format */
2555 content_length = prepare_sdp_description(c, stream, &content);
2556 if (content_length < 0) {
2557 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2560 rtsp_reply_header(c, RTSP_STATUS_OK);
2561 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2562 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2563 url_fprintf(c->pb, "\r\n");
2564 put_buffer(c->pb, content, content_length);
2567 static HTTPContext *find_rtp_session(const char *session_id)
2571 if (session_id[0] == '\0')
2574 for(c = first_http_ctx; c != NULL; c = c->next) {
2575 if (!strcmp(c->session_id, session_id))
2581 RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2583 RTSPTransportField *th;
2586 for(i=0;i<h->nb_transports;i++) {
2587 th = &h->transports[i];
2588 if (th->protocol == protocol)
2594 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2598 int stream_index, port;
2603 RTSPTransportField *th;
2604 struct sockaddr_in dest_addr;
2605 RTSPActionServerSetup setup;
2607 /* find which url is asked */
2608 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2613 /* now check each stream */
2614 for(stream = first_stream; stream != NULL; stream = stream->next) {
2615 if (!stream->is_feed && stream->fmt == &rtp_mux) {
2616 /* accept aggregate filenames only if single stream */
2617 if (!strcmp(path, stream->filename)) {
2618 if (stream->nb_streams != 1) {
2619 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2626 for(stream_index = 0; stream_index < stream->nb_streams;
2628 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2629 stream->filename, stream_index);
2630 if (!strcmp(path, buf))
2635 /* no stream found */
2636 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2640 /* generate session id if needed */
2641 if (h->session_id[0] == '\0') {
2642 snprintf(h->session_id, sizeof(h->session_id),
2643 "%08x%08x", (int)random(), (int)random());
2646 /* find rtp session, and create it if none found */
2647 rtp_c = find_rtp_session(h->session_id);
2649 rtp_c = rtp_new_connection(c, stream, h->session_id);
2651 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2655 /* open input stream */
2656 if (open_input_stream(rtp_c, "") < 0) {
2657 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2661 /* always prefer UDP */
2662 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2664 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2666 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2670 rtp_c->rtp_protocol = th->protocol;
2673 /* test if stream is OK (test needed because several SETUP needs
2674 to be done for a given file) */
2675 if (rtp_c->stream != stream) {
2676 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2680 /* test if stream is already set up */
2681 if (rtp_c->rtp_ctx[stream_index]) {
2682 rtsp_reply_error(c, RTSP_STATUS_STATE);
2686 /* check transport */
2687 th = find_transport(h, rtp_c->rtp_protocol);
2688 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2689 th->client_port_min <= 0)) {
2690 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2694 /* setup default options */
2695 setup.transport_option[0] = '\0';
2696 dest_addr = rtp_c->from_addr;
2697 dest_addr.sin_port = htons(th->client_port_min);
2699 /* add transport option if needed */
2700 if (ff_rtsp_callback) {
2701 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2702 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2703 (char *)&setup, sizeof(setup),
2704 stream->rtsp_option) < 0) {
2705 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2708 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2712 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
2713 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2717 /* now everything is OK, so we can send the connection parameters */
2718 rtsp_reply_header(c, RTSP_STATUS_OK);
2720 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2722 switch(rtp_c->rtp_protocol) {
2723 case RTSP_PROTOCOL_RTP_UDP:
2724 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2725 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2726 "client_port=%d-%d;server_port=%d-%d",
2727 th->client_port_min, th->client_port_min + 1,
2730 case RTSP_PROTOCOL_RTP_TCP:
2731 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2732 stream_index * 2, stream_index * 2 + 1);
2737 if (setup.transport_option[0] != '\0') {
2738 url_fprintf(c->pb, ";%s", setup.transport_option);
2740 url_fprintf(c->pb, "\r\n");
2743 url_fprintf(c->pb, "\r\n");
2747 /* find an rtp connection by using the session ID. Check consistency
2749 static HTTPContext *find_rtp_session_with_url(const char *url,
2750 const char *session_id)
2756 rtp_c = find_rtp_session(session_id);
2760 /* find which url is asked */
2761 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2765 if (strcmp(path, rtp_c->stream->filename) != 0)
2770 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2774 rtp_c = find_rtp_session_with_url(url, h->session_id);
2776 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2780 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2781 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2782 rtp_c->state != HTTPSTATE_READY) {
2783 rtsp_reply_error(c, RTSP_STATUS_STATE);
2787 rtp_c->state = HTTPSTATE_SEND_DATA;
2789 /* now everything is OK, so we can send the connection parameters */
2790 rtsp_reply_header(c, RTSP_STATUS_OK);
2792 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2793 url_fprintf(c->pb, "\r\n");
2796 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
2800 rtp_c = find_rtp_session_with_url(url, h->session_id);
2802 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2806 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2807 rtp_c->state != HTTPSTATE_WAIT_FEED) {
2808 rtsp_reply_error(c, RTSP_STATUS_STATE);
2812 rtp_c->state = HTTPSTATE_READY;
2814 /* now everything is OK, so we can send the connection parameters */
2815 rtsp_reply_header(c, RTSP_STATUS_OK);
2817 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2818 url_fprintf(c->pb, "\r\n");
2821 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
2825 rtp_c = find_rtp_session_with_url(url, h->session_id);
2827 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2831 /* abort the session */
2832 close_connection(rtp_c);
2834 if (ff_rtsp_callback) {
2835 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
2837 rtp_c->stream->rtsp_option);
2840 /* now everything is OK, so we can send the connection parameters */
2841 rtsp_reply_header(c, RTSP_STATUS_OK);
2843 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2844 url_fprintf(c->pb, "\r\n");
2848 /********************************************************************/
2851 static HTTPContext *rtp_new_connection(HTTPContext *rtsp_c,
2852 FFStream *stream, const char *session_id)
2854 HTTPContext *c = NULL;
2856 /* XXX: should output a warning page when coming
2857 close to the connection limit */
2858 if (nb_connections >= nb_max_connections)
2861 /* add a new connection */
2862 c = av_mallocz(sizeof(HTTPContext));
2867 c->poll_entry = NULL;
2868 c->from_addr = rtsp_c->from_addr;
2869 c->buffer_size = IOBUFFER_INIT_SIZE;
2870 c->buffer = av_malloc(c->buffer_size);
2875 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
2876 c->state = HTTPSTATE_READY;
2877 c->is_packetized = 1;
2878 /* protocol is shown in statistics */
2879 pstrcpy(c->protocol, sizeof(c->protocol), "RTP");
2881 c->next = first_http_ctx;
2893 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
2894 command). if dest_addr is NULL, then TCP tunneling in RTSP is
2896 static int rtp_new_av_stream(HTTPContext *c,
2897 int stream_index, struct sockaddr_in *dest_addr)
2899 AVFormatContext *ctx;
2905 /* now we can open the relevant output stream */
2906 ctx = av_mallocz(sizeof(AVFormatContext));
2909 ctx->oformat = &rtp_mux;
2911 st = av_mallocz(sizeof(AVStream));
2914 ctx->nb_streams = 1;
2915 ctx->streams[0] = st;
2917 if (!c->stream->feed ||
2918 c->stream->feed == c->stream) {
2919 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
2922 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
2927 /* build destination RTP address */
2928 ipaddr = inet_ntoa(dest_addr->sin_addr);
2930 snprintf(ctx->filename, sizeof(ctx->filename),
2931 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
2933 printf("open %s\n", ctx->filename);
2935 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
2937 c->rtp_handles[stream_index] = h;
2942 /* normally, no packets should be output here, but the packet size may be checked */
2943 if (url_open_dyn_packet_buf(&ctx->pb,
2944 url_get_max_packet_size(h)) < 0) {
2945 /* XXX: close stream */
2948 if (av_write_header(ctx) < 0) {
2955 url_close_dyn_buf(&ctx->pb, &dummy_buf);
2958 c->rtp_ctx[stream_index] = ctx;
2962 /********************************************************************/
2963 /* ffserver initialization */
2965 AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
2969 fst = av_mallocz(sizeof(AVStream));
2972 fst->priv_data = av_mallocz(sizeof(FeedData));
2973 memcpy(&fst->codec, codec, sizeof(AVCodecContext));
2974 stream->streams[stream->nb_streams++] = fst;
2978 /* return the stream number in the feed */
2979 int add_av_stream(FFStream *feed,
2983 AVCodecContext *av, *av1;
2987 for(i=0;i<feed->nb_streams;i++) {
2988 st = feed->streams[i];
2990 if (av1->codec_id == av->codec_id &&
2991 av1->codec_type == av->codec_type &&
2992 av1->bit_rate == av->bit_rate) {
2994 switch(av->codec_type) {
2995 case CODEC_TYPE_AUDIO:
2996 if (av1->channels == av->channels &&
2997 av1->sample_rate == av->sample_rate)
3000 case CODEC_TYPE_VIDEO:
3001 if (av1->width == av->width &&
3002 av1->height == av->height &&
3003 av1->frame_rate == av->frame_rate &&
3004 av1->gop_size == av->gop_size)
3013 fst = add_av_stream1(feed, av);
3016 return feed->nb_streams - 1;
3021 void remove_stream(FFStream *stream)
3025 while (*ps != NULL) {
3026 if (*ps == stream) {
3034 /* compute the needed AVStream for each file */
3035 void build_file_streams(void)
3037 FFStream *stream, *stream_next;
3038 AVFormatContext *infile;
3041 /* gather all streams */
3042 for(stream = first_stream; stream != NULL; stream = stream_next) {
3043 stream_next = stream->next;
3044 if (stream->stream_type == STREAM_TYPE_LIVE &&
3046 /* the stream comes from a file */
3047 /* try to open the file */
3049 if (av_open_input_file(&infile, stream->feed_filename,
3050 NULL, 0, NULL) < 0) {
3051 http_log("%s not found", stream->feed_filename);
3052 /* remove stream (no need to spend more time on it) */
3054 remove_stream(stream);
3056 /* find all the AVStreams inside and reference them in
3058 if (av_find_stream_info(infile) < 0) {
3059 http_log("Could not find codec parameters from '%s'",
3060 stream->feed_filename);
3061 av_close_input_file(infile);
3064 for(i=0;i<infile->nb_streams;i++) {
3065 add_av_stream1(stream, &infile->streams[i]->codec);
3067 av_close_input_file(infile);
3073 /* compute the needed AVStream for each feed */
3074 void build_feed_streams(void)
3076 FFStream *stream, *feed;
3079 /* gather all streams */
3080 for(stream = first_stream; stream != NULL; stream = stream->next) {
3081 feed = stream->feed;
3083 if (!stream->is_feed) {
3084 /* we handle a stream coming from a feed */
3085 for(i=0;i<stream->nb_streams;i++) {
3086 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3092 /* gather all streams */
3093 for(stream = first_stream; stream != NULL; stream = stream->next) {
3094 feed = stream->feed;
3096 if (stream->is_feed) {
3097 for(i=0;i<stream->nb_streams;i++) {
3098 stream->feed_streams[i] = i;
3104 /* create feed files if needed */
3105 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3108 if (!url_exist(feed->feed_filename)) {
3109 AVFormatContext s1, *s = &s1;
3111 /* only write the header of the ffm file */
3112 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3113 fprintf(stderr, "Could not open output feed file '%s'\n",
3114 feed->feed_filename);
3117 s->oformat = feed->fmt;
3118 s->nb_streams = feed->nb_streams;
3119 for(i=0;i<s->nb_streams;i++) {
3121 st = feed->streams[i];
3125 /* XXX: need better api */
3126 av_freep(&s->priv_data);
3129 /* get feed size and write index */
3130 fd = open(feed->feed_filename, O_RDONLY);
3132 fprintf(stderr, "Could not open output feed file '%s'\n",
3133 feed->feed_filename);
3137 feed->feed_write_index = ffm_read_write_index(fd);
3138 feed->feed_size = lseek(fd, 0, SEEK_END);
3139 /* ensure that we do not wrap before the end of file */
3140 if (feed->feed_max_size < feed->feed_size)
3141 feed->feed_max_size = feed->feed_size;
3147 static void get_arg(char *buf, int buf_size, const char **pp)
3154 while (isspace(*p)) p++;
3157 if (*p == '\"' || *p == '\'')
3169 if ((q - buf) < buf_size - 1)
3174 if (quote && *p == quote)
3179 /* add a codec and set the default parameters */
3180 void add_codec(FFStream *stream, AVCodecContext *av)
3184 /* compute default parameters */
3185 switch(av->codec_type) {
3186 case CODEC_TYPE_AUDIO:
3187 if (av->bit_rate == 0)
3188 av->bit_rate = 64000;
3189 if (av->sample_rate == 0)
3190 av->sample_rate = 22050;
3191 if (av->channels == 0)
3194 case CODEC_TYPE_VIDEO:
3195 if (av->bit_rate == 0)
3196 av->bit_rate = 64000;
3197 if (av->frame_rate == 0)
3198 av->frame_rate = 5 * FRAME_RATE_BASE;
3199 if (av->width == 0 || av->height == 0) {
3203 /* Bitrate tolerance is less for streaming */
3204 if (av->bit_rate_tolerance == 0)
3205 av->bit_rate_tolerance = av->bit_rate / 4;
3210 if (av->max_qdiff == 0)
3212 av->qcompress = 0.5;
3220 st = av_mallocz(sizeof(AVStream));
3223 stream->streams[stream->nb_streams++] = st;
3224 memcpy(&st->codec, av, sizeof(AVCodecContext));
3227 int opt_audio_codec(const char *arg)
3233 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3238 return CODEC_ID_NONE;
3244 int opt_video_codec(const char *arg)
3250 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3255 return CODEC_ID_NONE;
3261 /* simplistic plugin support */
3263 void load_module(const char *filename)
3266 void (*init_func)(void);
3267 dll = dlopen(filename, RTLD_NOW);
3269 fprintf(stderr, "Could not load module '%s' - %s\n",
3270 filename, dlerror());
3274 init_func = dlsym(dll, "ffserver_module_init");
3277 "%s: init function 'ffserver_module_init()' not found\n",
3285 int parse_ffconfig(const char *filename)
3292 int val, errors, line_num;
3293 FFStream **last_stream, *stream, *redirect;
3294 FFStream **last_feed, *feed;
3295 AVCodecContext audio_enc, video_enc;
3296 int audio_id, video_id;
3298 f = fopen(filename, "r");
3306 first_stream = NULL;
3307 last_stream = &first_stream;
3309 last_feed = &first_feed;
3313 audio_id = CODEC_ID_NONE;
3314 video_id = CODEC_ID_NONE;
3316 if (fgets(line, sizeof(line), f) == NULL)
3322 if (*p == '\0' || *p == '#')
3325 get_arg(cmd, sizeof(cmd), &p);
3327 if (!strcasecmp(cmd, "Port")) {
3328 get_arg(arg, sizeof(arg), &p);
3329 my_http_addr.sin_port = htons (atoi(arg));
3330 } else if (!strcasecmp(cmd, "BindAddress")) {
3331 get_arg(arg, sizeof(arg), &p);
3332 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3333 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3334 filename, line_num, arg);
3337 } else if (!strcasecmp(cmd, "NoDaemon")) {
3338 ffserver_daemon = 0;
3339 } else if (!strcasecmp(cmd, "RTSPPort")) {
3340 get_arg(arg, sizeof(arg), &p);
3341 my_rtsp_addr.sin_port = htons (atoi(arg));
3342 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3343 get_arg(arg, sizeof(arg), &p);
3344 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3345 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3346 filename, line_num, arg);
3349 } else if (!strcasecmp(cmd, "MaxClients")) {
3350 get_arg(arg, sizeof(arg), &p);
3352 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3353 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3354 filename, line_num, arg);
3357 nb_max_connections = val;
3359 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3360 get_arg(arg, sizeof(arg), &p);
3362 if (val < 10 || val > 100000) {
3363 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3364 filename, line_num, arg);
3367 nb_max_bandwidth = val;
3369 } else if (!strcasecmp(cmd, "CustomLog")) {
3370 get_arg(logfilename, sizeof(logfilename), &p);
3371 } else if (!strcasecmp(cmd, "<Feed")) {
3372 /*********************************************/
3373 /* Feed related options */
3375 if (stream || feed) {
3376 fprintf(stderr, "%s:%d: Already in a tag\n",
3377 filename, line_num);
3379 feed = av_mallocz(sizeof(FFStream));
3380 /* add in stream list */
3381 *last_stream = feed;
3382 last_stream = &feed->next;
3383 /* add in feed list */
3385 last_feed = &feed->next_feed;
3387 get_arg(feed->filename, sizeof(feed->filename), &p);
3388 q = strrchr(feed->filename, '>');
3391 feed->fmt = guess_format("ffm", NULL, NULL);
3392 /* defaut feed file */
3393 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3394 "/tmp/%s.ffm", feed->filename);
3395 feed->feed_max_size = 5 * 1024 * 1024;
3397 feed->feed = feed; /* self feeding :-) */
3399 } else if (!strcasecmp(cmd, "Launch")) {
3403 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3405 feed->child_argv[0] = av_malloc(7);
3406 strcpy(feed->child_argv[0], "ffmpeg");
3408 for (i = 1; i < 62; i++) {
3411 get_arg(argbuf, sizeof(argbuf), &p);
3415 feed->child_argv[i] = av_malloc(strlen(argbuf + 1));
3416 strcpy(feed->child_argv[i], argbuf);
3419 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3421 snprintf(feed->child_argv[i], 256, "http://127.0.0.1:%d/%s",
3422 ntohs(my_http_addr.sin_port), feed->filename);
3424 } else if (!strcasecmp(cmd, "File")) {
3426 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3427 } else if (stream) {
3428 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3430 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3435 get_arg(arg, sizeof(arg), &p);
3437 fsize = strtod(p1, (char **)&p1);
3438 switch(toupper(*p1)) {
3443 fsize *= 1024 * 1024;
3446 fsize *= 1024 * 1024 * 1024;
3449 feed->feed_max_size = (INT64)fsize;
3451 } else if (!strcasecmp(cmd, "</Feed>")) {
3453 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3454 filename, line_num);
3457 /* Make sure that we start out clean */
3458 if (unlink(feed->feed_filename) < 0
3459 && errno != ENOENT) {
3460 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3461 filename, line_num, feed->feed_filename, strerror(errno));
3466 } else if (!strcasecmp(cmd, "<Stream")) {
3467 /*********************************************/
3468 /* Stream related options */
3470 if (stream || feed) {
3471 fprintf(stderr, "%s:%d: Already in a tag\n",
3472 filename, line_num);
3474 stream = av_mallocz(sizeof(FFStream));
3475 *last_stream = stream;
3476 last_stream = &stream->next;
3478 get_arg(stream->filename, sizeof(stream->filename), &p);
3479 q = strrchr(stream->filename, '>');
3482 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3483 memset(&audio_enc, 0, sizeof(AVCodecContext));
3484 memset(&video_enc, 0, sizeof(AVCodecContext));
3485 audio_id = CODEC_ID_NONE;
3486 video_id = CODEC_ID_NONE;
3488 audio_id = stream->fmt->audio_codec;
3489 video_id = stream->fmt->video_codec;
3492 } else if (!strcasecmp(cmd, "Feed")) {
3493 get_arg(arg, sizeof(arg), &p);
3498 while (sfeed != NULL) {
3499 if (!strcmp(sfeed->filename, arg))
3501 sfeed = sfeed->next_feed;
3504 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3505 filename, line_num, arg);
3507 stream->feed = sfeed;
3510 } else if (!strcasecmp(cmd, "Format")) {
3511 get_arg(arg, sizeof(arg), &p);
3512 if (!strcmp(arg, "status")) {
3513 stream->stream_type = STREAM_TYPE_STATUS;
3516 stream->stream_type = STREAM_TYPE_LIVE;
3517 /* jpeg cannot be used here, so use single frame jpeg */
3518 if (!strcmp(arg, "jpeg"))
3519 strcpy(arg, "singlejpeg");
3520 stream->fmt = guess_stream_format(arg, NULL, NULL);
3522 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3523 filename, line_num, arg);
3528 audio_id = stream->fmt->audio_codec;
3529 video_id = stream->fmt->video_codec;
3531 } else if (!strcasecmp(cmd, "FaviconURL")) {
3532 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3533 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3535 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3536 filename, line_num);
3539 } else if (!strcasecmp(cmd, "Author")) {
3541 get_arg(stream->author, sizeof(stream->author), &p);
3543 } else if (!strcasecmp(cmd, "Comment")) {
3545 get_arg(stream->comment, sizeof(stream->comment), &p);
3547 } else if (!strcasecmp(cmd, "Copyright")) {
3549 get_arg(stream->copyright, sizeof(stream->copyright), &p);
3551 } else if (!strcasecmp(cmd, "Title")) {
3553 get_arg(stream->title, sizeof(stream->title), &p);
3555 } else if (!strcasecmp(cmd, "Preroll")) {
3556 get_arg(arg, sizeof(arg), &p);
3558 stream->prebuffer = atof(arg) * 1000;
3560 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
3562 stream->send_on_key = 1;
3564 } else if (!strcasecmp(cmd, "AudioCodec")) {
3565 get_arg(arg, sizeof(arg), &p);
3566 audio_id = opt_audio_codec(arg);
3567 if (audio_id == CODEC_ID_NONE) {
3568 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
3569 filename, line_num, arg);
3572 } else if (!strcasecmp(cmd, "VideoCodec")) {
3573 get_arg(arg, sizeof(arg), &p);
3574 video_id = opt_video_codec(arg);
3575 if (video_id == CODEC_ID_NONE) {
3576 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
3577 filename, line_num, arg);
3580 } else if (!strcasecmp(cmd, "MaxTime")) {
3581 get_arg(arg, sizeof(arg), &p);
3583 stream->max_time = atof(arg) * 1000;
3585 } else if (!strcasecmp(cmd, "AudioBitRate")) {
3586 get_arg(arg, sizeof(arg), &p);
3588 audio_enc.bit_rate = atoi(arg) * 1000;
3590 } else if (!strcasecmp(cmd, "AudioChannels")) {
3591 get_arg(arg, sizeof(arg), &p);
3593 audio_enc.channels = atoi(arg);
3595 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
3596 get_arg(arg, sizeof(arg), &p);
3598 audio_enc.sample_rate = atoi(arg);
3600 } else if (!strcasecmp(cmd, "VideoBitRate")) {
3601 get_arg(arg, sizeof(arg), &p);
3603 video_enc.bit_rate = atoi(arg) * 1000;
3605 } else if (!strcasecmp(cmd, "VideoSize")) {
3606 get_arg(arg, sizeof(arg), &p);
3608 parse_image_size(&video_enc.width, &video_enc.height, arg);
3609 if ((video_enc.width % 16) != 0 ||
3610 (video_enc.height % 16) != 0) {
3611 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
3612 filename, line_num);
3616 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
3617 get_arg(arg, sizeof(arg), &p);
3619 video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
3621 } else if (!strcasecmp(cmd, "VideoGopSize")) {
3622 get_arg(arg, sizeof(arg), &p);
3624 video_enc.gop_size = atoi(arg);
3626 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
3628 video_enc.gop_size = 1;
3630 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
3632 video_enc.flags |= CODEC_FLAG_HQ;
3634 } else if (!strcasecmp(cmd, "VideoQDiff")) {
3636 video_enc.max_qdiff = atoi(arg);
3637 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
3638 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
3639 filename, line_num);
3643 } else if (!strcasecmp(cmd, "VideoQMax")) {
3645 video_enc.qmax = atoi(arg);
3646 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
3647 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
3648 filename, line_num);
3652 } else if (!strcasecmp(cmd, "VideoQMin")) {
3654 video_enc.qmin = atoi(arg);
3655 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
3656 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
3657 filename, line_num);
3661 } else if (!strcasecmp(cmd, "NoVideo")) {
3662 video_id = CODEC_ID_NONE;
3663 } else if (!strcasecmp(cmd, "NoAudio")) {
3664 audio_id = CODEC_ID_NONE;
3665 } else if (!strcasecmp(cmd, "ACL")) {
3669 get_arg(arg, sizeof(arg), &p);
3670 if (strcasecmp(arg, "allow") == 0) {
3671 acl.action = IP_ALLOW;
3672 } else if (strcasecmp(arg, "deny") == 0) {
3673 acl.action = IP_DENY;
3675 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
3676 filename, line_num, arg);
3680 get_arg(arg, sizeof(arg), &p);
3682 he = gethostbyname(arg);
3684 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
3685 filename, line_num, arg);
3688 /* Only take the first */
3689 acl.first = *(struct in_addr *) he->h_addr_list[0];
3690 acl.last = acl.first;
3693 get_arg(arg, sizeof(arg), &p);
3696 he = gethostbyname(arg);
3698 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
3699 filename, line_num, arg);
3702 /* Only take the first */
3703 acl.last = *(struct in_addr *) he->h_addr_list[0];
3708 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
3709 IPAddressACL **naclp = 0;
3715 naclp = &stream->acl;
3719 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
3720 filename, line_num);
3726 naclp = &(*naclp)->next;
3731 } else if (!strcasecmp(cmd, "RTSPOption")) {
3732 get_arg(arg, sizeof(arg), &p);
3734 av_freep(&stream->rtsp_option);
3735 /* XXX: av_strdup ? */
3736 stream->rtsp_option = av_malloc(strlen(arg) + 1);
3737 if (stream->rtsp_option) {
3738 strcpy(stream->rtsp_option, arg);
3741 } else if (!strcasecmp(cmd, "</Stream>")) {
3743 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
3744 filename, line_num);
3747 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
3748 if (audio_id != CODEC_ID_NONE) {
3749 audio_enc.codec_type = CODEC_TYPE_AUDIO;
3750 audio_enc.codec_id = audio_id;
3751 add_codec(stream, &audio_enc);
3753 if (video_id != CODEC_ID_NONE) {
3754 video_enc.codec_type = CODEC_TYPE_VIDEO;
3755 video_enc.codec_id = video_id;
3756 add_codec(stream, &video_enc);
3760 } else if (!strcasecmp(cmd, "<Redirect")) {
3761 /*********************************************/
3763 if (stream || feed || redirect) {
3764 fprintf(stderr, "%s:%d: Already in a tag\n",
3765 filename, line_num);
3768 redirect = av_mallocz(sizeof(FFStream));
3769 *last_stream = redirect;
3770 last_stream = &redirect->next;
3772 get_arg(redirect->filename, sizeof(redirect->filename), &p);
3773 q = strrchr(redirect->filename, '>');
3776 redirect->stream_type = STREAM_TYPE_REDIRECT;
3778 } else if (!strcasecmp(cmd, "URL")) {
3780 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
3782 } else if (!strcasecmp(cmd, "</Redirect>")) {
3784 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
3785 filename, line_num);
3788 if (!redirect->feed_filename[0]) {
3789 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
3790 filename, line_num);
3794 } else if (!strcasecmp(cmd, "LoadModule")) {
3795 get_arg(arg, sizeof(arg), &p);
3798 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
3799 filename, line_num, cmd);
3813 static void write_packet(FFCodec *ffenc,
3814 UINT8 *buf, int size)
3817 AVCodecContext *enc = &ffenc->enc;
3819 mk_header(&hdr, enc, size);
3820 wptr = http_fifo.wptr;
3821 fifo_write(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &wptr);
3822 fifo_write(&http_fifo, buf, size, &wptr);
3823 /* atomic modification of wptr */
3824 http_fifo.wptr = wptr;
3825 ffenc->data_count += size;
3826 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
3832 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
3833 "usage: ffserver [-L] [-h] [-f configfile]\n"
3834 "Hyper fast multi format Audio/Video streaming server\n"
3836 "-L : print the LICENCE\n"
3838 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
3845 "ffserver version " FFMPEG_VERSION "\n"
3846 "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
3847 "This library is free software; you can redistribute it and/or\n"
3848 "modify it under the terms of the GNU Lesser General Public\n"
3849 "License as published by the Free Software Foundation; either\n"
3850 "version 2 of the License, or (at your option) any later version.\n"
3852 "This library is distributed in the hope that it will be useful,\n"
3853 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
3854 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
3855 "Lesser General Public License for more details.\n"
3857 "You should have received a copy of the GNU Lesser General Public\n"
3858 "License along with this library; if not, write to the Free Software\n"
3859 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
3863 static void handle_child_exit(int sig)
3868 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
3871 for (feed = first_feed; feed; feed = feed->next) {
3872 if (feed->pid == pid) {
3873 int uptime = time(0) - feed->pid_start;
3876 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
3879 /* Turn off any more restarts */
3880 feed->child_argv = 0;
3886 need_to_start_children = 1;
3889 int main(int argc, char **argv)
3891 const char *config_filename;
3893 struct sigaction sigact;
3897 config_filename = "/etc/ffserver.conf";
3899 my_program_name = argv[0];
3900 ffserver_daemon = 1;
3903 c = getopt(argc, argv, "ndLh?f:");
3919 ffserver_daemon = 0;
3922 config_filename = optarg;
3929 putenv("http_proxy"); /* Kill the http_proxy */
3931 srandom(gettime_ms() + (getpid() << 16));
3933 /* address on which the server will handle HTTP connections */
3934 my_http_addr.sin_family = AF_INET;
3935 my_http_addr.sin_port = htons (8080);
3936 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
3938 /* address on which the server will handle RTSP connections */
3939 my_rtsp_addr.sin_family = AF_INET;
3940 my_rtsp_addr.sin_port = htons (5454);
3941 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
3943 nb_max_connections = 5;
3944 nb_max_bandwidth = 1000;
3945 first_stream = NULL;
3946 logfilename[0] = '\0';
3948 memset(&sigact, 0, sizeof(sigact));
3949 sigact.sa_handler = handle_child_exit;
3950 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
3951 sigaction(SIGCHLD, &sigact, 0);
3953 if (parse_ffconfig(config_filename) < 0) {
3954 fprintf(stderr, "Incorrect config file - exiting.\n");
3958 build_file_streams();
3960 build_feed_streams();
3962 /* put the process in background and detach it from its TTY */
3963 if (ffserver_daemon) {
3970 } else if (pid > 0) {
3978 open("/dev/null", O_RDWR);
3979 if (!strcmp(logfilename, "-")) {
3989 signal(SIGPIPE, SIG_IGN);
3991 /* open log file if needed */
3992 if (logfilename[0] != '\0') {
3993 if (!strcmp(logfilename, "-"))
3996 logfile = fopen(logfilename, "w");
3999 if (http_server() < 0) {
4000 fprintf(stderr, "Could not start server\n");