2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #define HAVE_AV_CONFIG_H
25 #include <sys/ioctl.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
38 #ifdef CONFIG_HAVE_DLFCN
44 /* maximum number of simultaneous HTTP connections */
45 #define HTTP_MAX_CONNECTIONS 2000
48 HTTPSTATE_WAIT_REQUEST,
49 HTTPSTATE_SEND_HEADER,
50 HTTPSTATE_SEND_DATA_HEADER,
51 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
52 HTTPSTATE_SEND_DATA_TRAILER,
53 HTTPSTATE_RECEIVE_DATA,
54 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
55 HTTPSTATE_WAIT, /* wait before sending next packets */
56 HTTPSTATE_WAIT_SHORT, /* short wait for short term
57 bandwidth limitation */
60 RTSPSTATE_WAIT_REQUEST,
64 const char *http_state[] = {
81 #define IOBUFFER_INIT_SIZE 8192
83 /* coef for exponential mean for bitrate estimation in statistics */
86 /* timeouts are in ms */
87 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
88 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
90 #define SYNC_TIMEOUT (10 * 1000)
97 /* context associated with one connection */
98 typedef struct HTTPContext {
100 int fd; /* socket file descriptor */
101 struct sockaddr_in from_addr; /* origin */
102 struct pollfd *poll_entry; /* used when polling */
104 UINT8 *buffer_ptr, *buffer_end;
106 struct HTTPContext *next;
107 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
111 /* input format handling */
112 AVFormatContext *fmt_in;
113 long start_time; /* In milliseconds - this wraps fairly often */
114 INT64 first_pts; /* initial pts value */
115 int pts_stream_index; /* stream we choose as clock reference */
116 /* output format handling */
117 struct FFStream *stream;
118 /* -1 is invalid stream */
119 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
120 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
122 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
123 int last_packet_sent; /* true if last data packet was sent */
125 DataRateData datarate;
132 int is_packetized; /* if true, the stream is packetized */
133 int packet_stream_index; /* current stream for output in state machine */
135 /* RTSP state specific */
136 UINT8 *pb_buffer; /* XXX: use that in all the code */
138 int seq; /* RTSP sequence number */
140 /* RTP state specific */
141 enum RTSPProtocol rtp_protocol;
142 char session_id[32]; /* session id */
143 AVFormatContext *rtp_ctx[MAX_STREAMS];
144 URLContext *rtp_handles[MAX_STREAMS];
145 /* RTP short term bandwidth limitation */
146 int packet_byte_count;
147 int packet_start_time_us; /* used for short durations (a few
151 static AVFrame dummy_frame;
153 /* each generated stream is described here */
157 STREAM_TYPE_REDIRECT,
160 enum IPAddressAction {
165 typedef struct IPAddressACL {
166 struct IPAddressACL *next;
167 enum IPAddressAction action;
168 struct in_addr first;
172 /* description of each stream of the ffserver.conf file */
173 typedef struct FFStream {
174 enum StreamType stream_type;
175 char filename[1024]; /* stream filename */
176 struct FFStream *feed; /* feed we are using (can be null if
181 int prebuffer; /* Number of millseconds early to start */
182 long max_time; /* Number of milliseconds to run */
184 AVStream *streams[MAX_STREAMS];
185 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
186 char feed_filename[1024]; /* file name of the feed storage, or
187 input file name for a stream */
192 pid_t pid; /* Of ffmpeg process */
193 time_t pid_start; /* Of ffmpeg process */
195 struct FFStream *next;
196 int bandwidth; /* bandwidth, in kbits/s */
199 /* multicast specific */
201 struct in_addr multicast_ip;
202 int multicast_port; /* first port used for multicast */
204 int loop; /* if true, send the stream in loops (only meaningful if file) */
207 int feed_opened; /* true if someone is writing to the feed */
208 int is_feed; /* true if it is a feed */
211 INT64 feed_max_size; /* maximum storage size */
212 INT64 feed_write_index; /* current write position in feed (it wraps round) */
213 INT64 feed_size; /* current size of feed */
214 struct FFStream *next_feed;
217 typedef struct FeedData {
218 long long data_count;
219 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
222 struct sockaddr_in my_http_addr;
223 struct sockaddr_in my_rtsp_addr;
225 char logfilename[1024];
226 HTTPContext *first_http_ctx;
227 FFStream *first_feed; /* contains only feeds */
228 FFStream *first_stream; /* contains all streams, including feeds */
230 static void new_connection(int server_fd, int is_rtsp);
231 static void close_connection(HTTPContext *c);
234 static int handle_connection(HTTPContext *c);
235 static int http_parse_request(HTTPContext *c);
236 static int http_send_data(HTTPContext *c);
237 static void compute_stats(HTTPContext *c);
238 static int open_input_stream(HTTPContext *c, const char *info);
239 static int http_start_receive_data(HTTPContext *c);
240 static int http_receive_data(HTTPContext *c);
241 static int compute_send_delay(HTTPContext *c);
244 static int rtsp_parse_request(HTTPContext *c);
245 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
246 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
247 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
248 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
249 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
252 static int prepare_sdp_description(FFStream *stream, UINT8 **pbuffer,
253 struct in_addr my_ip);
256 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
257 FFStream *stream, const char *session_id);
258 static int rtp_new_av_stream(HTTPContext *c,
259 int stream_index, struct sockaddr_in *dest_addr);
261 static const char *my_program_name;
262 static const char *my_program_dir;
264 static int ffserver_debug;
265 static int ffserver_daemon;
266 static int no_launch;
267 static int need_to_start_children;
269 int nb_max_connections;
273 int current_bandwidth;
275 static long cur_time; // Making this global saves on passing it around everywhere
277 static long gettime_ms(void)
281 gettimeofday(&tv,NULL);
282 return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
285 static FILE *logfile = NULL;
287 static void http_log(char *fmt, ...)
293 vfprintf(logfile, fmt, ap);
299 static char *ctime1(char *buf2)
307 p = buf2 + strlen(p) - 1;
313 static void log_connection(HTTPContext *c)
320 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
321 inet_ntoa(c->from_addr.sin_addr),
322 ctime1(buf2), c->method, c->url,
323 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
326 static void update_datarate(DataRateData *drd, INT64 count)
328 if (!drd->time1 && !drd->count1) {
329 drd->time1 = drd->time2 = cur_time;
330 drd->count1 = drd->count2 = count;
332 if (cur_time - drd->time2 > 5000) {
333 drd->time1 = drd->time2;
334 drd->count1 = drd->count2;
335 drd->time2 = cur_time;
341 /* In bytes per second */
342 static int compute_datarate(DataRateData *drd, INT64 count)
344 if (cur_time == drd->time1)
347 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
350 static int get_longterm_datarate(DataRateData *drd, INT64 count)
352 /* You get the first 3 seconds flat out */
353 if (cur_time - drd->time1 < 3000)
355 return compute_datarate(drd, count);
359 static void start_children(FFStream *feed)
364 for (; feed; feed = feed->next) {
365 if (feed->child_argv && !feed->pid) {
366 feed->pid_start = time(0);
371 fprintf(stderr, "Unable to create children\n");
380 for (i = 3; i < 256; i++) {
384 if (!ffserver_debug) {
385 i = open("/dev/null", O_RDWR);
394 pstrcpy(pathname, sizeof(pathname), my_program_name);
396 slash = strrchr(pathname, '/');
402 strcpy(slash, "ffmpeg");
404 /* This is needed to make relative pathnames work */
405 chdir(my_program_dir);
407 signal(SIGPIPE, SIG_DFL);
409 execvp(pathname, feed->child_argv);
417 /* open a listening socket */
418 static int socket_open_listen(struct sockaddr_in *my_addr)
422 server_fd = socket(AF_INET,SOCK_STREAM,0);
429 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
431 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
433 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
439 if (listen (server_fd, 5) < 0) {
444 fcntl(server_fd, F_SETFL, O_NONBLOCK);
449 /* start all multicast streams */
450 static void start_multicast(void)
455 struct sockaddr_in dest_addr;
456 int default_port, stream_index;
459 for(stream = first_stream; stream != NULL; stream = stream->next) {
460 if (stream->is_multicast) {
461 /* open the RTP connection */
462 snprintf(session_id, sizeof(session_id),
463 "%08x%08x", (int)random(), (int)random());
465 /* choose a port if none given */
466 if (stream->multicast_port == 0) {
467 stream->multicast_port = default_port;
471 dest_addr.sin_family = AF_INET;
472 dest_addr.sin_addr = stream->multicast_ip;
473 dest_addr.sin_port = htons(stream->multicast_port);
475 rtp_c = rtp_new_connection(&dest_addr, stream, session_id);
479 if (open_input_stream(rtp_c, "") < 0) {
480 fprintf(stderr, "Could not open input stream for stream '%s'\n",
485 rtp_c->rtp_protocol = RTSP_PROTOCOL_RTP_UDP_MULTICAST;
487 /* open each RTP stream */
488 for(stream_index = 0; stream_index < stream->nb_streams;
490 dest_addr.sin_port = htons(stream->multicast_port +
492 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
493 fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
494 stream->filename, stream_index);
499 /* change state to send data */
500 rtp_c->state = HTTPSTATE_SEND_DATA;
505 /* main loop of the http server */
506 static int http_server(void)
508 int server_fd, ret, rtsp_server_fd, delay, delay1;
509 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
510 HTTPContext *c, *c_next;
512 server_fd = socket_open_listen(&my_http_addr);
516 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
517 if (rtsp_server_fd < 0)
520 http_log("ffserver started.\n");
522 start_children(first_feed);
524 first_http_ctx = NULL;
526 first_http_ctx = NULL;
531 poll_entry = poll_table;
532 poll_entry->fd = server_fd;
533 poll_entry->events = POLLIN;
536 poll_entry->fd = rtsp_server_fd;
537 poll_entry->events = POLLIN;
540 /* wait for events on each HTTP handle */
547 case HTTPSTATE_SEND_HEADER:
548 case RTSPSTATE_SEND_REPLY:
549 c->poll_entry = poll_entry;
551 poll_entry->events = POLLOUT;
554 case HTTPSTATE_SEND_DATA_HEADER:
555 case HTTPSTATE_SEND_DATA:
556 case HTTPSTATE_SEND_DATA_TRAILER:
557 if (!c->is_packetized) {
558 /* for TCP, we output as much as we can (may need to put a limit) */
559 c->poll_entry = poll_entry;
561 poll_entry->events = POLLOUT;
564 /* not strictly correct, but currently cannot add
565 more than one fd in poll entry */
569 case HTTPSTATE_WAIT_REQUEST:
570 case HTTPSTATE_RECEIVE_DATA:
571 case HTTPSTATE_WAIT_FEED:
572 case RTSPSTATE_WAIT_REQUEST:
573 /* need to catch errors */
574 c->poll_entry = poll_entry;
576 poll_entry->events = POLLIN;/* Maybe this will work */
580 c->poll_entry = NULL;
581 delay1 = compute_send_delay(c);
585 case HTTPSTATE_WAIT_SHORT:
586 c->poll_entry = NULL;
587 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
592 c->poll_entry = NULL;
598 /* wait for an event on one connection. We poll at least every
599 second to handle timeouts */
601 ret = poll(poll_table, poll_entry - poll_table, delay);
604 cur_time = gettime_ms();
606 if (need_to_start_children) {
607 need_to_start_children = 0;
608 start_children(first_feed);
611 /* now handle the events */
612 for(c = first_http_ctx; c != NULL; c = c_next) {
614 if (handle_connection(c) < 0) {
615 /* close and free the connection */
621 poll_entry = poll_table;
622 /* new HTTP connection request ? */
623 if (poll_entry->revents & POLLIN) {
624 new_connection(server_fd, 0);
627 /* new RTSP connection request ? */
628 if (poll_entry->revents & POLLIN) {
629 new_connection(rtsp_server_fd, 1);
634 /* start waiting for a new HTTP/RTSP request */
635 static void start_wait_request(HTTPContext *c, int is_rtsp)
637 c->buffer_ptr = c->buffer;
638 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
641 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
642 c->state = RTSPSTATE_WAIT_REQUEST;
644 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
645 c->state = HTTPSTATE_WAIT_REQUEST;
649 static void new_connection(int server_fd, int is_rtsp)
651 struct sockaddr_in from_addr;
653 HTTPContext *c = NULL;
655 len = sizeof(from_addr);
656 fd = accept(server_fd, (struct sockaddr *)&from_addr,
660 fcntl(fd, F_SETFL, O_NONBLOCK);
662 /* XXX: should output a warning page when coming
663 close to the connection limit */
664 if (nb_connections >= nb_max_connections)
667 /* add a new connection */
668 c = av_mallocz(sizeof(HTTPContext));
672 c->next = first_http_ctx;
675 c->poll_entry = NULL;
676 c->from_addr = from_addr;
677 c->buffer_size = IOBUFFER_INIT_SIZE;
678 c->buffer = av_malloc(c->buffer_size);
683 start_wait_request(c, is_rtsp);
695 static void close_connection(HTTPContext *c)
697 HTTPContext **cp, *c1;
699 AVFormatContext *ctx;
703 /* remove connection from list */
704 cp = &first_http_ctx;
705 while ((*cp) != NULL) {
714 /* remove connection associated resources */
718 /* close each frame parser */
719 for(i=0;i<c->fmt_in->nb_streams;i++) {
720 st = c->fmt_in->streams[i];
721 if (st->codec.codec) {
722 avcodec_close(&st->codec);
725 av_close_input_file(c->fmt_in);
728 /* free RTP output streams if any */
731 nb_streams = c->stream->nb_streams;
733 for(i=0;i<nb_streams;i++) {
736 av_write_trailer(ctx);
739 h = c->rtp_handles[i];
746 current_bandwidth -= c->stream->bandwidth;
747 av_freep(&c->pb_buffer);
753 static int handle_connection(HTTPContext *c)
758 case HTTPSTATE_WAIT_REQUEST:
759 case RTSPSTATE_WAIT_REQUEST:
761 if ((c->timeout - cur_time) < 0)
763 if (c->poll_entry->revents & (POLLERR | POLLHUP))
766 /* no need to read if no events */
767 if (!(c->poll_entry->revents & POLLIN))
770 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
772 if (errno != EAGAIN && errno != EINTR)
774 } else if (len == 0) {
777 /* search for end of request. XXX: not fully correct since garbage could come after the end */
779 c->buffer_ptr += len;
781 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
782 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
783 /* request found : parse it and reply */
784 if (c->state == HTTPSTATE_WAIT_REQUEST) {
785 ret = http_parse_request(c);
787 ret = rtsp_parse_request(c);
791 } else if (ptr >= c->buffer_end) {
792 /* request too long: cannot do anything */
798 case HTTPSTATE_SEND_HEADER:
799 if (c->poll_entry->revents & (POLLERR | POLLHUP))
802 /* no need to write if no events */
803 if (!(c->poll_entry->revents & POLLOUT))
805 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
807 if (errno != EAGAIN && errno != EINTR) {
808 /* error : close connection */
809 av_freep(&c->pb_buffer);
813 c->buffer_ptr += len;
815 c->stream->bytes_served += len;
816 c->data_count += len;
817 if (c->buffer_ptr >= c->buffer_end) {
818 av_freep(&c->pb_buffer);
823 /* all the buffer was sent : synchronize to the incoming stream */
824 c->state = HTTPSTATE_SEND_DATA_HEADER;
825 c->buffer_ptr = c->buffer_end = c->buffer;
830 case HTTPSTATE_SEND_DATA:
831 case HTTPSTATE_SEND_DATA_HEADER:
832 case HTTPSTATE_SEND_DATA_TRAILER:
833 /* for packetized output, we consider we can always write (the
834 input streams sets the speed). It may be better to verify
835 that we do not rely too much on the kernel queues */
836 if (!c->is_packetized) {
837 if (c->poll_entry->revents & (POLLERR | POLLHUP))
840 /* no need to read if no events */
841 if (!(c->poll_entry->revents & POLLOUT))
844 if (http_send_data(c) < 0)
847 case HTTPSTATE_RECEIVE_DATA:
848 /* no need to read if no events */
849 if (c->poll_entry->revents & (POLLERR | POLLHUP))
851 if (!(c->poll_entry->revents & POLLIN))
853 if (http_receive_data(c) < 0)
856 case HTTPSTATE_WAIT_FEED:
857 /* no need to read if no events */
858 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
861 /* nothing to do, we'll be waken up by incoming feed packets */
865 /* if the delay expired, we can send new packets */
866 if (compute_send_delay(c) <= 0)
867 c->state = HTTPSTATE_SEND_DATA;
869 case HTTPSTATE_WAIT_SHORT:
870 /* just return back to send data */
871 c->state = HTTPSTATE_SEND_DATA;
874 case RTSPSTATE_SEND_REPLY:
875 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
876 av_freep(&c->pb_buffer);
879 /* no need to write if no events */
880 if (!(c->poll_entry->revents & POLLOUT))
882 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
884 if (errno != EAGAIN && errno != EINTR) {
885 /* error : close connection */
886 av_freep(&c->pb_buffer);
890 c->buffer_ptr += len;
891 c->data_count += len;
892 if (c->buffer_ptr >= c->buffer_end) {
893 /* all the buffer was sent : wait for a new request */
894 av_freep(&c->pb_buffer);
895 start_wait_request(c, 1);
899 case HTTPSTATE_READY:
908 static int extract_rates(char *rates, int ratelen, const char *request)
912 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
913 if (strncasecmp(p, "Pragma:", 7) == 0) {
914 const char *q = p + 7;
916 while (*q && *q != '\n' && isspace(*q))
919 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
925 memset(rates, 0xff, ratelen);
928 while (*q && *q != '\n' && *q != ':')
931 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
935 if (stream_no < ratelen && stream_no >= 0) {
936 rates[stream_no] = rate_no;
939 while (*q && *q != '\n' && !isspace(*q))
956 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
959 int best_bitrate = 100000000;
962 for (i = 0; i < feed->nb_streams; i++) {
963 AVCodecContext *feed_codec = &feed->streams[i]->codec;
965 if (feed_codec->codec_id != codec->codec_id ||
966 feed_codec->sample_rate != codec->sample_rate ||
967 feed_codec->width != codec->width ||
968 feed_codec->height != codec->height) {
972 /* Potential stream */
974 /* We want the fastest stream less than bit_rate, or the slowest
975 * faster than bit_rate
978 if (feed_codec->bit_rate <= bit_rate) {
979 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
980 best_bitrate = feed_codec->bit_rate;
984 if (feed_codec->bit_rate < best_bitrate) {
985 best_bitrate = feed_codec->bit_rate;
994 static int modify_current_stream(HTTPContext *c, char *rates)
997 FFStream *req = c->stream;
998 int action_required = 0;
1000 /* Not much we can do for a feed */
1004 for (i = 0; i < req->nb_streams; i++) {
1005 AVCodecContext *codec = &req->streams[i]->codec;
1009 c->switch_feed_streams[i] = req->feed_streams[i];
1012 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1015 /* Wants off or slow */
1016 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1018 /* This doesn't work well when it turns off the only stream! */
1019 c->switch_feed_streams[i] = -2;
1020 c->feed_streams[i] = -2;
1025 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1026 action_required = 1;
1029 return action_required;
1033 static void do_switch_stream(HTTPContext *c, int i)
1035 if (c->switch_feed_streams[i] >= 0) {
1037 c->feed_streams[i] = c->switch_feed_streams[i];
1040 /* Now update the stream */
1042 c->switch_feed_streams[i] = -1;
1045 /* XXX: factorize in utils.c ? */
1046 /* XXX: take care with different space meaning */
1047 static void skip_spaces(const char **pp)
1051 while (*p == ' ' || *p == '\t')
1056 static void get_word(char *buf, int buf_size, const char **pp)
1064 while (!isspace(*p) && *p != '\0') {
1065 if ((q - buf) < buf_size - 1)
1074 static int validate_acl(FFStream *stream, HTTPContext *c)
1076 enum IPAddressAction last_action = IP_DENY;
1078 struct in_addr *src = &c->from_addr.sin_addr;
1080 for (acl = stream->acl; acl; acl = acl->next) {
1081 if (src->s_addr >= acl->first.s_addr && src->s_addr <= acl->last.s_addr) {
1082 return (acl->action == IP_ALLOW) ? 1 : 0;
1084 last_action = acl->action;
1087 /* Nothing matched, so return not the last action */
1088 return (last_action == IP_DENY) ? 1 : 0;
1091 /* compute the real filename of a file by matching it without its
1092 extensions to all the stream filenames */
1093 static void compute_real_filename(char *filename, int max_size)
1100 /* compute filename by matching without the file extensions */
1101 pstrcpy(file1, sizeof(file1), filename);
1102 p = strrchr(file1, '.');
1105 for(stream = first_stream; stream != NULL; stream = stream->next) {
1106 pstrcpy(file2, sizeof(file2), stream->filename);
1107 p = strrchr(file2, '.');
1110 if (!strcmp(file1, file2)) {
1111 pstrcpy(filename, max_size, stream->filename);
1126 /* parse http request and prepare header */
1127 static int http_parse_request(HTTPContext *c)
1131 enum RedirType redir_type;
1133 char info[1024], *filename;
1137 const char *mime_type;
1141 char *useragent = 0;
1144 get_word(cmd, sizeof(cmd), (const char **)&p);
1145 pstrcpy(c->method, sizeof(c->method), cmd);
1147 if (!strcmp(cmd, "GET"))
1149 else if (!strcmp(cmd, "POST"))
1154 get_word(url, sizeof(url), (const char **)&p);
1155 pstrcpy(c->url, sizeof(c->url), url);
1157 get_word(protocol, sizeof(protocol), (const char **)&p);
1158 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1161 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
1163 /* find the filename and the optional info string in the request */
1170 pstrcpy(info, sizeof(info), p);
1176 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1177 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1179 if (*useragent && *useragent != '\n' && isspace(*useragent))
1183 p = strchr(p, '\n');
1190 redir_type = REDIR_NONE;
1191 if (match_ext(filename, "asx")) {
1192 redir_type = REDIR_ASX;
1193 filename[strlen(filename)-1] = 'f';
1194 } else if (match_ext(filename, "asf") &&
1195 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1196 /* if this isn't WMP or lookalike, return the redirector file */
1197 redir_type = REDIR_ASF;
1198 } else if (match_ext(filename, "rpm,ram")) {
1199 redir_type = REDIR_RAM;
1200 strcpy(filename + strlen(filename)-2, "m");
1201 } else if (match_ext(filename, "rtsp")) {
1202 redir_type = REDIR_RTSP;
1203 compute_real_filename(filename, sizeof(url) - 1);
1204 } else if (match_ext(filename, "sdp")) {
1205 redir_type = REDIR_SDP;
1206 compute_real_filename(filename, sizeof(url) - 1);
1209 stream = first_stream;
1210 while (stream != NULL) {
1211 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1213 stream = stream->next;
1215 if (stream == NULL) {
1216 sprintf(msg, "File '%s' not found", url);
1221 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1222 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1224 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1225 c->http_error = 301;
1227 q += sprintf(q, "HTTP/1.0 301 Moved\r\n");
1228 q += sprintf(q, "Location: %s\r\n", stream->feed_filename);
1229 q += sprintf(q, "Content-type: text/html\r\n");
1230 q += sprintf(q, "\r\n");
1231 q += sprintf(q, "<html><head><title>Moved</title></head><body>\r\n");
1232 q += sprintf(q, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1233 q += sprintf(q, "</body></html>\r\n");
1235 /* prepare output buffer */
1236 c->buffer_ptr = c->buffer;
1238 c->state = HTTPSTATE_SEND_HEADER;
1242 /* If this is WMP, get the rate information */
1243 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1244 if (modify_current_stream(c, ratebuf)) {
1245 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1246 if (c->switch_feed_streams[i] >= 0)
1247 do_switch_stream(c, i);
1252 if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1253 current_bandwidth += stream->bandwidth;
1256 if (post == 0 && max_bandwidth < current_bandwidth) {
1257 c->http_error = 200;
1259 q += sprintf(q, "HTTP/1.0 200 Server too busy\r\n");
1260 q += sprintf(q, "Content-type: text/html\r\n");
1261 q += sprintf(q, "\r\n");
1262 q += sprintf(q, "<html><head><title>Too busy</title></head><body>\r\n");
1263 q += sprintf(q, "The server is too busy to serve your request at this time.<p>\r\n");
1264 q += sprintf(q, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
1265 current_bandwidth, max_bandwidth);
1266 q += sprintf(q, "</body></html>\r\n");
1268 /* prepare output buffer */
1269 c->buffer_ptr = c->buffer;
1271 c->state = HTTPSTATE_SEND_HEADER;
1275 if (redir_type != REDIR_NONE) {
1278 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1279 if (strncasecmp(p, "Host:", 5) == 0) {
1283 p = strchr(p, '\n');
1294 while (isspace(*hostinfo))
1297 eoh = strchr(hostinfo, '\n');
1299 if (eoh[-1] == '\r')
1302 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1303 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1304 hostbuf[eoh - hostinfo] = 0;
1306 c->http_error = 200;
1308 switch(redir_type) {
1310 q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n");
1311 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1312 q += sprintf(q, "\r\n");
1313 q += sprintf(q, "<ASX Version=\"3\">\r\n");
1314 q += sprintf(q, "<!-- Autogenerated by ffserver -->\r\n");
1315 q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1316 hostbuf, filename, info);
1317 q += sprintf(q, "</ASX>\r\n");
1320 q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n");
1321 q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n");
1322 q += sprintf(q, "\r\n");
1323 q += sprintf(q, "# Autogenerated by ffserver\r\n");
1324 q += sprintf(q, "http://%s/%s%s\r\n",
1325 hostbuf, filename, info);
1328 q += sprintf(q, "HTTP/1.0 200 ASF Redirect follows\r\n");
1329 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1330 q += sprintf(q, "\r\n");
1331 q += sprintf(q, "[Reference]\r\n");
1332 q += sprintf(q, "Ref1=http://%s/%s%s\r\n",
1333 hostbuf, filename, info);
1337 char hostname[256], *p;
1338 /* extract only hostname */
1339 pstrcpy(hostname, sizeof(hostname), hostbuf);
1340 p = strrchr(hostname, ':');
1343 q += sprintf(q, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1344 /* XXX: incorrect mime type ? */
1345 q += sprintf(q, "Content-type: application/x-rtsp\r\n");
1346 q += sprintf(q, "\r\n");
1347 q += sprintf(q, "rtsp://%s:%d/%s\r\n",
1348 hostname, ntohs(my_rtsp_addr.sin_port),
1355 int sdp_data_size, len;
1356 struct sockaddr_in my_addr;
1358 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1359 q += sprintf(q, "Content-type: application/sdp\r\n");
1360 q += sprintf(q, "\r\n");
1362 len = sizeof(my_addr);
1363 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1365 /* XXX: should use a dynamic buffer */
1366 sdp_data_size = prepare_sdp_description(stream,
1369 if (sdp_data_size > 0) {
1370 memcpy(q, sdp_data, sdp_data_size);
1382 /* prepare output buffer */
1383 c->buffer_ptr = c->buffer;
1385 c->state = HTTPSTATE_SEND_HEADER;
1391 sprintf(msg, "ASX/RAM file not handled");
1395 stream->conns_served++;
1397 /* XXX: add there authenticate and IP match */
1400 /* if post, it means a feed is being sent */
1401 if (!stream->is_feed) {
1402 /* However it might be a status report from WMP! Lets log the data
1403 * as it might come in handy one day
1408 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1409 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1413 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1414 client_id = strtol(p + 18, 0, 10);
1416 p = strchr(p, '\n');
1424 char *eol = strchr(logline, '\n');
1429 if (eol[-1] == '\r')
1431 http_log("%.*s\n", eol - logline, logline);
1432 c->suppress_log = 1;
1437 http_log("\nGot request:\n%s\n", c->buffer);
1440 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1443 /* Now we have to find the client_id */
1444 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1445 if (wmpc->wmp_client_id == client_id)
1450 if (modify_current_stream(wmpc, ratebuf)) {
1451 wmpc->switch_pending = 1;
1456 sprintf(msg, "POST command not handled");
1459 if (http_start_receive_data(c) < 0) {
1460 sprintf(msg, "could not open feed");
1464 c->state = HTTPSTATE_RECEIVE_DATA;
1469 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1470 http_log("\nGot request:\n%s\n", c->buffer);
1474 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1477 /* open input stream */
1478 if (open_input_stream(c, info) < 0) {
1479 sprintf(msg, "Input stream corresponding to '%s' not found", url);
1483 /* prepare http header */
1485 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1486 mime_type = c->stream->fmt->mime_type;
1488 mime_type = "application/x-octet_stream";
1489 q += sprintf(q, "Pragma: no-cache\r\n");
1491 /* for asf, we need extra headers */
1492 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1493 /* Need to allocate a client id */
1495 c->wmp_client_id = random() & 0x7fffffff;
1497 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);
1499 q += sprintf(q, "Content-Type: %s\r\n", mime_type);
1500 q += sprintf(q, "\r\n");
1502 /* prepare output buffer */
1504 c->buffer_ptr = c->buffer;
1506 c->state = HTTPSTATE_SEND_HEADER;
1509 c->http_error = 404;
1511 q += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
1512 q += sprintf(q, "Content-type: %s\r\n", "text/html");
1513 q += sprintf(q, "\r\n");
1514 q += sprintf(q, "<HTML>\n");
1515 q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1516 q += sprintf(q, "<BODY>%s</BODY>\n", msg);
1517 q += sprintf(q, "</HTML>\n");
1519 /* prepare output buffer */
1520 c->buffer_ptr = c->buffer;
1522 c->state = HTTPSTATE_SEND_HEADER;
1526 c->http_error = 200; /* horrible : we use this value to avoid
1527 going to the send data state */
1528 c->state = HTTPSTATE_SEND_HEADER;
1532 static void fmt_bytecount(ByteIOContext *pb, INT64 count)
1534 static const char *suffix = " kMGTP";
1537 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1540 url_fprintf(pb, "%lld%c", count, *s);
1543 static void compute_stats(HTTPContext *c)
1550 ByteIOContext pb1, *pb = &pb1;
1552 if (url_open_dyn_buf(pb) < 0) {
1553 /* XXX: return an error ? */
1554 c->buffer_ptr = c->buffer;
1555 c->buffer_end = c->buffer;
1559 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1560 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1561 url_fprintf(pb, "Pragma: no-cache\r\n");
1562 url_fprintf(pb, "\r\n");
1564 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1565 if (c->stream->feed_filename) {
1566 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1568 url_fprintf(pb, "</HEAD>\n<BODY>");
1569 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1571 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1572 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1573 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");
1574 stream = first_stream;
1575 while (stream != NULL) {
1576 char sfilename[1024];
1579 if (stream->feed != stream) {
1580 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1581 eosf = sfilename + strlen(sfilename);
1582 if (eosf - sfilename >= 4) {
1583 if (strcmp(eosf - 4, ".asf") == 0) {
1584 strcpy(eosf - 4, ".asx");
1585 } else if (strcmp(eosf - 3, ".rm") == 0) {
1586 strcpy(eosf - 3, ".ram");
1587 } else if (stream->fmt == &rtp_mux) {
1588 /* generate a sample RTSP director if
1589 unicast. Generate an SDP redirector if
1591 eosf = strrchr(sfilename, '.');
1593 eosf = sfilename + strlen(sfilename);
1594 if (stream->is_multicast)
1595 strcpy(eosf, ".sdp");
1597 strcpy(eosf, ".rtsp");
1601 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1602 sfilename, stream->filename);
1603 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1604 stream->conns_served);
1605 fmt_bytecount(pb, stream->bytes_served);
1606 switch(stream->stream_type) {
1607 case STREAM_TYPE_LIVE:
1609 int audio_bit_rate = 0;
1610 int video_bit_rate = 0;
1611 const char *audio_codec_name = "";
1612 const char *video_codec_name = "";
1613 const char *audio_codec_name_extra = "";
1614 const char *video_codec_name_extra = "";
1616 for(i=0;i<stream->nb_streams;i++) {
1617 AVStream *st = stream->streams[i];
1618 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1619 switch(st->codec.codec_type) {
1620 case CODEC_TYPE_AUDIO:
1621 audio_bit_rate += st->codec.bit_rate;
1623 if (*audio_codec_name)
1624 audio_codec_name_extra = "...";
1625 audio_codec_name = codec->name;
1628 case CODEC_TYPE_VIDEO:
1629 video_bit_rate += st->codec.bit_rate;
1631 if (*video_codec_name)
1632 video_codec_name_extra = "...";
1633 video_codec_name = codec->name;
1640 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",
1643 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1644 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1646 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1648 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1650 url_fprintf(pb, "\n");
1654 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1658 stream = stream->next;
1660 url_fprintf(pb, "</TABLE>\n");
1662 stream = first_stream;
1663 while (stream != NULL) {
1664 if (stream->feed == stream) {
1665 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1667 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1669 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1674 /* This is somewhat linux specific I guess */
1675 snprintf(ps_cmd, sizeof(ps_cmd),
1676 "ps -o \"%%cpu,cputime\" --no-headers %d",
1679 pid_stat = popen(ps_cmd, "r");
1684 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1686 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1694 url_fprintf(pb, "<p>");
1696 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");
1698 for (i = 0; i < stream->nb_streams; i++) {
1699 AVStream *st = stream->streams[i];
1700 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1701 char *type = "unknown";
1702 char parameters[64];
1706 switch(st->codec.codec_type) {
1707 case CODEC_TYPE_AUDIO:
1710 case CODEC_TYPE_VIDEO:
1712 sprintf(parameters, "%dx%d, q=%d-%d, fps=%d", st->codec.width, st->codec.height,
1713 st->codec.qmin, st->codec.qmax, st->codec.frame_rate / FRAME_RATE_BASE);
1718 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1719 i, type, st->codec.bit_rate/1000, codec ? codec->name : "", parameters);
1721 url_fprintf(pb, "</table>\n");
1724 stream = stream->next;
1730 AVCodecContext *enc;
1734 stream = first_feed;
1735 while (stream != NULL) {
1736 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1737 url_fprintf(pb, "<TABLE>\n");
1738 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1739 for(i=0;i<stream->nb_streams;i++) {
1740 AVStream *st = stream->streams[i];
1741 FeedData *fdata = st->priv_data;
1744 avcodec_string(buf, sizeof(buf), enc);
1745 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1746 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1747 avg /= enc->frame_size;
1748 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1749 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1751 url_fprintf(pb, "</TABLE>\n");
1752 stream = stream->next_feed;
1757 /* connection status */
1758 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1760 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1761 nb_connections, nb_max_connections);
1763 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1764 current_bandwidth, max_bandwidth);
1766 url_fprintf(pb, "<TABLE>\n");
1767 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");
1768 c1 = first_http_ctx;
1770 while (c1 != NULL) {
1776 for (j = 0; j < c1->stream->nb_streams; j++) {
1777 if (!c1->stream->feed) {
1778 bitrate += c1->stream->streams[j]->codec.bit_rate;
1780 if (c1->feed_streams[j] >= 0) {
1781 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec.bit_rate;
1788 p = inet_ntoa(c1->from_addr.sin_addr);
1789 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1791 c1->stream ? c1->stream->filename : "",
1792 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1795 http_state[c1->state]);
1796 fmt_bytecount(pb, bitrate);
1797 url_fprintf(pb, "<td align=right>");
1798 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1799 url_fprintf(pb, "<td align=right>");
1800 fmt_bytecount(pb, c1->data_count);
1801 url_fprintf(pb, "\n");
1804 url_fprintf(pb, "</TABLE>\n");
1809 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1810 url_fprintf(pb, "</BODY>\n</HTML>\n");
1812 len = url_close_dyn_buf(pb, &c->pb_buffer);
1813 c->buffer_ptr = c->pb_buffer;
1814 c->buffer_end = c->pb_buffer + len;
1817 /* check if the parser needs to be opened for stream i */
1818 static void open_parser(AVFormatContext *s, int i)
1820 AVStream *st = s->streams[i];
1823 if (!st->codec.codec) {
1824 codec = avcodec_find_decoder(st->codec.codec_id);
1825 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1826 st->codec.parse_only = 1;
1827 if (avcodec_open(&st->codec, codec) < 0) {
1828 st->codec.parse_only = 0;
1834 static int open_input_stream(HTTPContext *c, const char *info)
1837 char input_filename[1024];
1842 /* find file name */
1843 if (c->stream->feed) {
1844 strcpy(input_filename, c->stream->feed->feed_filename);
1845 buf_size = FFM_PACKET_SIZE;
1846 /* compute position (absolute time) */
1847 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1848 stream_pos = parse_date(buf, 0);
1849 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1850 int prebuffer = strtol(buf, 0, 10);
1851 stream_pos = av_gettime() - prebuffer * (INT64)1000000;
1853 stream_pos = av_gettime() - c->stream->prebuffer * (INT64)1000;
1856 strcpy(input_filename, c->stream->feed_filename);
1858 /* compute position (relative time) */
1859 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1860 stream_pos = parse_date(buf, 1);
1865 if (input_filename[0] == '\0')
1869 { time_t when = stream_pos / 1000000;
1870 http_log("Stream pos = %lld, time=%s", stream_pos, ctime(&when));
1875 if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0) {
1876 http_log("%s not found", input_filename);
1881 /* open each parser */
1882 for(i=0;i<s->nb_streams;i++)
1885 /* choose stream as clock source (we favorize video stream if
1886 present) for packet sending */
1887 c->pts_stream_index = 0;
1888 for(i=0;i<c->stream->nb_streams;i++) {
1889 if (c->pts_stream_index == 0 &&
1890 c->stream->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO) {
1891 c->pts_stream_index = i;
1895 if (c->fmt_in->iformat->read_seek) {
1896 c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
1898 /* set the start time (needed for maxtime and RTP packet timing) */
1899 c->start_time = cur_time;
1900 c->first_pts = AV_NOPTS_VALUE;
1904 /* currently desactivated because the new PTS handling is not
1906 //#define AV_READ_FRAME
1907 #ifdef AV_READ_FRAME
1909 /* XXX: generalize that in ffmpeg for picture/audio/data. Currently
1910 the return packet MUST NOT be freed */
1911 int av_read_frame(AVFormatContext *s, AVPacket *pkt)
1914 int len, ret, old_nb_streams, i;
1916 /* see if remaining frames must be parsed */
1918 if (s->cur_len > 0) {
1919 st = s->streams[s->cur_pkt.stream_index];
1920 len = avcodec_parse_frame(&st->codec, &pkt->data, &pkt->size,
1921 s->cur_ptr, s->cur_len);
1923 /* error: get next packet */
1929 /* init pts counter if not done */
1930 if (st->pts.den == 0) {
1931 switch(st->codec.codec_type) {
1932 case CODEC_TYPE_AUDIO:
1933 st->pts_incr = (INT64)s->pts_den;
1934 av_frac_init(&st->pts, st->pts.val, 0,
1935 (INT64)s->pts_num * st->codec.sample_rate);
1937 case CODEC_TYPE_VIDEO:
1938 st->pts_incr = (INT64)s->pts_den * FRAME_RATE_BASE;
1939 av_frac_init(&st->pts, st->pts.val, 0,
1940 (INT64)s->pts_num * st->codec.frame_rate);
1947 /* a frame was read: return it */
1948 pkt->pts = st->pts.val;
1950 printf("add pts=%Lx num=%Lx den=%Lx incr=%Lx\n",
1951 st->pts.val, st->pts.num, st->pts.den, st->pts_incr);
1953 switch(st->codec.codec_type) {
1954 case CODEC_TYPE_AUDIO:
1955 av_frac_add(&st->pts, st->pts_incr * st->codec.frame_size);
1957 case CODEC_TYPE_VIDEO:
1958 av_frac_add(&st->pts, st->pts_incr);
1963 pkt->stream_index = s->cur_pkt.stream_index;
1964 /* we use the codec indication because it is
1965 more accurate than the demux flags */
1967 if (st->codec.coded_frame->key_frame)
1968 pkt->flags |= PKT_FLAG_KEY;
1973 /* free previous packet */
1974 av_free_packet(&s->cur_pkt);
1976 old_nb_streams = s->nb_streams;
1977 ret = av_read_packet(s, &s->cur_pkt);
1980 /* open parsers for each new streams */
1981 for(i = old_nb_streams; i < s->nb_streams; i++)
1983 st = s->streams[s->cur_pkt.stream_index];
1985 /* update current pts (XXX: dts handling) from packet, or
1986 use current pts if none given */
1987 if (s->cur_pkt.pts != AV_NOPTS_VALUE) {
1988 av_frac_set(&st->pts, s->cur_pkt.pts);
1990 s->cur_pkt.pts = st->pts.val;
1992 if (!st->codec.codec) {
1993 /* no codec opened: just return the raw packet */
1996 /* no codec opened: just update the pts by considering we
1997 have one frame and free the packet */
1998 if (st->pts.den == 0) {
1999 switch(st->codec.codec_type) {
2000 case CODEC_TYPE_AUDIO:
2001 st->pts_incr = (INT64)s->pts_den * st->codec.frame_size;
2002 av_frac_init(&st->pts, st->pts.val, 0,
2003 (INT64)s->pts_num * st->codec.sample_rate);
2005 case CODEC_TYPE_VIDEO:
2006 st->pts_incr = (INT64)s->pts_den * FRAME_RATE_BASE;
2007 av_frac_init(&st->pts, st->pts.val, 0,
2008 (INT64)s->pts_num * st->codec.frame_rate);
2014 av_frac_add(&st->pts, st->pts_incr);
2017 s->cur_ptr = s->cur_pkt.data;
2018 s->cur_len = s->cur_pkt.size;
2024 static int compute_send_delay(HTTPContext *c)
2026 INT64 cur_pts, delta_pts, next_pts;
2029 /* compute current pts value from system time */
2030 cur_pts = ((INT64)(cur_time - c->start_time) * c->fmt_in->pts_den) /
2031 (c->fmt_in->pts_num * 1000LL);
2032 /* compute the delta from the stream we choose as
2033 main clock (we do that to avoid using explicit
2034 buffers to do exact packet reordering for each
2036 /* XXX: really need to fix the number of streams */
2037 if (c->pts_stream_index >= c->fmt_in->nb_streams)
2040 next_pts = c->fmt_in->streams[c->pts_stream_index]->pts.val;
2041 delta_pts = next_pts - cur_pts;
2042 if (delta_pts <= 0) {
2045 delay1 = (delta_pts * 1000 * c->fmt_in->pts_num) / c->fmt_in->pts_den;
2051 /* just fall backs */
2052 int av_read_frame(AVFormatContext *s, AVPacket *pkt)
2054 return av_read_packet(s, pkt);
2057 static int compute_send_delay(HTTPContext *c)
2059 int datarate = 8 * get_longterm_datarate(&c->datarate, c->data_count);
2061 if (datarate > c->stream->bandwidth * 2000) {
2069 static int http_prepare_data(HTTPContext *c)
2072 AVFormatContext *ctx;
2075 case HTTPSTATE_SEND_DATA_HEADER:
2076 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2077 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
2079 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
2080 c->stream->comment);
2081 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
2082 c->stream->copyright);
2083 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
2086 /* open output stream by using specified codecs */
2087 c->fmt_ctx.oformat = c->stream->fmt;
2088 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2089 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2091 st = av_mallocz(sizeof(AVStream));
2092 c->fmt_ctx.streams[i] = st;
2093 /* if file or feed, then just take streams from FFStream struct */
2094 if (!c->stream->feed ||
2095 c->stream->feed == c->stream)
2096 memcpy(st, c->stream->streams[i], sizeof(AVStream));
2098 memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]],
2100 st->codec.frame_number = 0; /* XXX: should be done in
2101 AVStream, not in codec */
2102 /* I'm pretty sure that this is not correct...
2103 * However, without it, we crash
2105 st->codec.coded_frame = &dummy_frame;
2107 c->got_key_frame = 0;
2109 /* prepare header and save header data in a stream */
2110 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2111 /* XXX: potential leak */
2114 c->fmt_ctx.pb.is_streamed = 1;
2116 av_write_header(&c->fmt_ctx);
2118 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2119 c->buffer_ptr = c->pb_buffer;
2120 c->buffer_end = c->pb_buffer + len;
2122 c->state = HTTPSTATE_SEND_DATA;
2123 c->last_packet_sent = 0;
2125 case HTTPSTATE_SEND_DATA:
2126 /* find a new packet */
2130 /* read a packet from the input stream */
2131 if (c->stream->feed) {
2132 ffm_set_write_index(c->fmt_in,
2133 c->stream->feed->feed_write_index,
2134 c->stream->feed->feed_size);
2137 if (c->stream->max_time &&
2138 c->stream->max_time + c->start_time - cur_time < 0) {
2139 /* We have timed out */
2140 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2142 if (1 || c->is_packetized) {
2143 if (compute_send_delay(c) > 0) {
2144 c->state = HTTPSTATE_WAIT;
2145 return 1; /* state changed */
2149 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2150 if (c->stream->feed && c->stream->feed->feed_opened) {
2151 /* if coming from feed, it means we reached the end of the
2152 ffm file, so must wait for more data */
2153 c->state = HTTPSTATE_WAIT_FEED;
2154 return 1; /* state changed */
2156 if (c->stream->loop) {
2157 av_close_input_file(c->fmt_in);
2159 if (open_input_stream(c, "") < 0)
2164 /* must send trailer now because eof or error */
2165 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2169 /* update first pts if needed */
2170 if (c->first_pts == AV_NOPTS_VALUE)
2171 c->first_pts = pkt.pts;
2173 /* send it to the appropriate stream */
2174 if (c->stream->feed) {
2175 /* if coming from a feed, select the right stream */
2176 if (c->switch_pending) {
2177 c->switch_pending = 0;
2178 for(i=0;i<c->stream->nb_streams;i++) {
2179 if (c->switch_feed_streams[i] == pkt.stream_index) {
2180 if (pkt.flags & PKT_FLAG_KEY) {
2181 do_switch_stream(c, i);
2184 if (c->switch_feed_streams[i] >= 0) {
2185 c->switch_pending = 1;
2189 for(i=0;i<c->stream->nb_streams;i++) {
2190 if (c->feed_streams[i] == pkt.stream_index) {
2191 pkt.stream_index = i;
2192 if (pkt.flags & PKT_FLAG_KEY) {
2193 c->got_key_frame |= 1 << i;
2195 /* See if we have all the key frames, then
2196 * we start to send. This logic is not quite
2197 * right, but it works for the case of a
2198 * single video stream with one or more
2199 * audio streams (for which every frame is
2200 * typically a key frame).
2202 if (!c->stream->send_on_key ||
2203 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2209 AVCodecContext *codec;
2212 /* specific handling for RTP: we use several
2213 output stream (one for each RTP
2214 connection). XXX: need more abstract handling */
2215 if (c->is_packetized) {
2216 c->packet_stream_index = pkt.stream_index;
2217 ctx = c->rtp_ctx[c->packet_stream_index];
2218 codec = &ctx->streams[0]->codec;
2219 /* only one stream per RTP connection */
2220 pkt.stream_index = 0;
2224 codec = &ctx->streams[pkt.stream_index]->codec;
2227 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2230 if (codec->codec_type == CODEC_TYPE_AUDIO) {
2231 codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
2232 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
2236 if (c->is_packetized) {
2237 ret = url_open_dyn_packet_buf(&ctx->pb,
2238 url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]));
2239 c->packet_byte_count = 0;
2240 c->packet_start_time_us = av_gettime();
2242 ret = url_open_dyn_buf(&ctx->pb);
2245 /* XXX: potential leak */
2248 if (av_write_frame(ctx, pkt.stream_index, pkt.data, pkt.size)) {
2249 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2252 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2253 c->buffer_ptr = c->pb_buffer;
2254 c->buffer_end = c->pb_buffer + len;
2256 codec->frame_number++;
2258 #ifndef AV_READ_FRAME
2259 av_free_packet(&pkt);
2266 case HTTPSTATE_SEND_DATA_TRAILER:
2267 /* last packet test ? */
2268 if (c->last_packet_sent || c->is_packetized)
2271 /* prepare header */
2272 if (url_open_dyn_buf(&ctx->pb) < 0) {
2273 /* XXX: potential leak */
2276 av_write_trailer(ctx);
2277 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2278 c->buffer_ptr = c->pb_buffer;
2279 c->buffer_end = c->pb_buffer + len;
2281 c->last_packet_sent = 1;
2288 #define SHORT_TERM_BANDWIDTH 8000000
2290 /* should convert the format at the same time */
2291 static int http_send_data(HTTPContext *c)
2295 while (c->buffer_ptr >= c->buffer_end) {
2296 av_freep(&c->pb_buffer);
2297 ret = http_prepare_data(c);
2300 else if (ret == 0) {
2303 /* state change requested */
2308 if (c->buffer_ptr < c->buffer_end) {
2309 if (c->is_packetized) {
2310 /* RTP/UDP data output */
2311 len = c->buffer_end - c->buffer_ptr;
2313 /* fail safe - should never happen */
2315 c->buffer_ptr = c->buffer_end;
2318 len = (c->buffer_ptr[0] << 24) |
2319 (c->buffer_ptr[1] << 16) |
2320 (c->buffer_ptr[2] << 8) |
2322 if (len > (c->buffer_end - c->buffer_ptr))
2325 /* short term bandwidth limitation */
2326 dt = av_gettime() - c->packet_start_time_us;
2330 if ((c->packet_byte_count + len) * (INT64)1000000 >=
2331 (SHORT_TERM_BANDWIDTH / 8) * (INT64)dt) {
2332 /* bandwidth overflow : wait at most one tick and retry */
2333 c->state = HTTPSTATE_WAIT_SHORT;
2338 url_write(c->rtp_handles[c->packet_stream_index],
2339 c->buffer_ptr, len);
2340 c->buffer_ptr += len;
2341 c->packet_byte_count += len;
2343 /* TCP data output */
2344 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2346 if (errno != EAGAIN && errno != EINTR) {
2347 /* error : close connection */
2353 c->buffer_ptr += len;
2356 c->data_count += len;
2357 update_datarate(&c->datarate, c->data_count);
2359 c->stream->bytes_served += len;
2364 static int http_start_receive_data(HTTPContext *c)
2368 if (c->stream->feed_opened)
2372 fd = open(c->stream->feed_filename, O_RDWR);
2377 c->stream->feed_write_index = ffm_read_write_index(fd);
2378 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2379 lseek(fd, 0, SEEK_SET);
2381 /* init buffer input */
2382 c->buffer_ptr = c->buffer;
2383 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2384 c->stream->feed_opened = 1;
2388 static int http_receive_data(HTTPContext *c)
2392 if (c->buffer_end > c->buffer_ptr) {
2395 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2397 if (errno != EAGAIN && errno != EINTR) {
2398 /* error : close connection */
2401 } else if (len == 0) {
2402 /* end of connection : close it */
2405 c->buffer_ptr += len;
2406 c->data_count += len;
2407 update_datarate(&c->datarate, c->data_count);
2411 if (c->buffer_ptr >= c->buffer_end) {
2412 FFStream *feed = c->stream;
2413 /* a packet has been received : write it in the store, except
2415 if (c->data_count > FFM_PACKET_SIZE) {
2417 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2418 /* XXX: use llseek or url_seek */
2419 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2420 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2422 feed->feed_write_index += FFM_PACKET_SIZE;
2423 /* update file size */
2424 if (feed->feed_write_index > c->stream->feed_size)
2425 feed->feed_size = feed->feed_write_index;
2427 /* handle wrap around if max file size reached */
2428 if (feed->feed_write_index >= c->stream->feed_max_size)
2429 feed->feed_write_index = FFM_PACKET_SIZE;
2432 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2434 /* wake up any waiting connections */
2435 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2436 if (c1->state == HTTPSTATE_WAIT_FEED &&
2437 c1->stream->feed == c->stream->feed) {
2438 c1->state = HTTPSTATE_SEND_DATA;
2442 /* We have a header in our hands that contains useful data */
2444 AVInputFormat *fmt_in;
2445 ByteIOContext *pb = &s.pb;
2448 memset(&s, 0, sizeof(s));
2450 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2451 pb->buf_end = c->buffer_end; /* ?? */
2452 pb->is_streamed = 1;
2454 /* use feed output format name to find corresponding input format */
2455 fmt_in = av_find_input_format(feed->fmt->name);
2459 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2463 if (fmt_in->read_header(&s, 0) < 0) {
2464 av_freep(&s.priv_data);
2468 /* Now we have the actual streams */
2469 if (s.nb_streams != feed->nb_streams) {
2470 av_freep(&s.priv_data);
2473 for (i = 0; i < s.nb_streams; i++) {
2474 memcpy(&feed->streams[i]->codec,
2475 &s.streams[i]->codec, sizeof(AVCodecContext));
2477 av_freep(&s.priv_data);
2479 c->buffer_ptr = c->buffer;
2484 c->stream->feed_opened = 0;
2489 /********************************************************************/
2492 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2499 switch(error_number) {
2500 #define DEF(n, c, s) case c: str = s; break;
2501 #include "rtspcodes.h"
2504 str = "Unknown Error";
2508 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2509 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2511 /* output GMT time */
2515 p = buf2 + strlen(p) - 1;
2518 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2521 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2523 rtsp_reply_header(c, error_number);
2524 url_fprintf(c->pb, "\r\n");
2527 static int rtsp_parse_request(HTTPContext *c)
2529 const char *p, *p1, *p2;
2536 RTSPHeader header1, *header = &header1;
2538 c->buffer_ptr[0] = '\0';
2541 get_word(cmd, sizeof(cmd), &p);
2542 get_word(url, sizeof(url), &p);
2543 get_word(protocol, sizeof(protocol), &p);
2545 pstrcpy(c->method, sizeof(c->method), cmd);
2546 pstrcpy(c->url, sizeof(c->url), url);
2547 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2550 if (url_open_dyn_buf(c->pb) < 0) {
2551 /* XXX: cannot do more */
2552 c->pb = NULL; /* safety */
2556 /* check version name */
2557 if (strcmp(protocol, "RTSP/1.0") != 0) {
2558 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2562 /* parse each header line */
2563 memset(header, 0, sizeof(RTSPHeader));
2564 /* skip to next line */
2565 while (*p != '\n' && *p != '\0')
2569 while (*p != '\0') {
2570 p1 = strchr(p, '\n');
2574 if (p2 > p && p2[-1] == '\r')
2576 /* skip empty line */
2580 if (len > sizeof(line) - 1)
2581 len = sizeof(line) - 1;
2582 memcpy(line, p, len);
2584 rtsp_parse_line(header, line);
2588 /* handle sequence number */
2589 c->seq = header->seq;
2591 if (!strcmp(cmd, "DESCRIBE")) {
2592 rtsp_cmd_describe(c, url);
2593 } else if (!strcmp(cmd, "SETUP")) {
2594 rtsp_cmd_setup(c, url, header);
2595 } else if (!strcmp(cmd, "PLAY")) {
2596 rtsp_cmd_play(c, url, header);
2597 } else if (!strcmp(cmd, "PAUSE")) {
2598 rtsp_cmd_pause(c, url, header);
2599 } else if (!strcmp(cmd, "TEARDOWN")) {
2600 rtsp_cmd_teardown(c, url, header);
2602 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2605 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2606 c->pb = NULL; /* safety */
2608 /* XXX: cannot do more */
2611 c->buffer_ptr = c->pb_buffer;
2612 c->buffer_end = c->pb_buffer + len;
2613 c->state = RTSPSTATE_SEND_REPLY;
2617 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2619 static int prepare_sdp_description(FFStream *stream, UINT8 **pbuffer,
2620 struct in_addr my_ip)
2622 ByteIOContext pb1, *pb = &pb1;
2623 int i, payload_type, port, private_payload_type, j;
2624 const char *ipstr, *title, *mediatype;
2627 if (url_open_dyn_buf(pb) < 0)
2630 /* general media info */
2632 url_fprintf(pb, "v=0\n");
2633 ipstr = inet_ntoa(my_ip);
2634 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2635 title = stream->title;
2636 if (title[0] == '\0')
2638 url_fprintf(pb, "s=%s\n", title);
2639 if (stream->comment[0] != '\0')
2640 url_fprintf(pb, "i=%s\n", stream->comment);
2641 if (stream->is_multicast) {
2642 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2644 /* for each stream, we output the necessary info */
2645 private_payload_type = 96;
2646 for(i = 0; i < stream->nb_streams; i++) {
2647 st = stream->streams[i];
2648 switch(st->codec.codec_type) {
2649 case CODEC_TYPE_AUDIO:
2650 mediatype = "audio";
2652 case CODEC_TYPE_VIDEO:
2653 mediatype = "video";
2656 mediatype = "application";
2659 /* NOTE: the port indication is not correct in case of
2660 unicast. It is not an issue because RTSP gives it */
2661 payload_type = rtp_get_payload_type(&st->codec);
2662 if (payload_type < 0)
2663 payload_type = private_payload_type++;
2664 if (stream->is_multicast) {
2665 port = stream->multicast_port + 2 * i;
2669 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2670 mediatype, port, payload_type);
2671 if (payload_type >= 96) {
2672 /* for private payload type, we need to give more info */
2673 switch(st->codec.codec_id) {
2674 case CODEC_ID_MPEG4:
2677 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2678 payload_type, 90000);
2679 /* we must also add the mpeg4 header */
2680 data = st->codec.extradata;
2682 url_fprintf(pb, "a=fmtp:%d config=");
2683 for(j=0;j<st->codec.extradata_size;j++) {
2684 url_fprintf(pb, "%02x", data[j]);
2686 url_fprintf(pb, "\n");
2691 /* XXX: add other codecs ? */
2695 url_fprintf(pb, "a=control:streamid=%d\n", i);
2697 return url_close_dyn_buf(pb, pbuffer);
2699 url_close_dyn_buf(pb, pbuffer);
2704 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2710 int content_length, len;
2711 struct sockaddr_in my_addr;
2713 /* find which url is asked */
2714 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2719 for(stream = first_stream; stream != NULL; stream = stream->next) {
2720 if (!stream->is_feed && stream->fmt == &rtp_mux &&
2721 !strcmp(path, stream->filename)) {
2725 /* no stream found */
2726 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2730 /* prepare the media description in sdp format */
2732 /* get the host IP */
2733 len = sizeof(my_addr);
2734 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2736 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2737 if (content_length < 0) {
2738 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2741 rtsp_reply_header(c, RTSP_STATUS_OK);
2742 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2743 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2744 url_fprintf(c->pb, "\r\n");
2745 put_buffer(c->pb, content, content_length);
2748 static HTTPContext *find_rtp_session(const char *session_id)
2752 if (session_id[0] == '\0')
2755 for(c = first_http_ctx; c != NULL; c = c->next) {
2756 if (!strcmp(c->session_id, session_id))
2762 RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2764 RTSPTransportField *th;
2767 for(i=0;i<h->nb_transports;i++) {
2768 th = &h->transports[i];
2769 if (th->protocol == protocol)
2775 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2779 int stream_index, port;
2784 RTSPTransportField *th;
2785 struct sockaddr_in dest_addr;
2786 RTSPActionServerSetup setup;
2788 /* find which url is asked */
2789 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2794 /* now check each stream */
2795 for(stream = first_stream; stream != NULL; stream = stream->next) {
2796 if (!stream->is_feed && stream->fmt == &rtp_mux) {
2797 /* accept aggregate filenames only if single stream */
2798 if (!strcmp(path, stream->filename)) {
2799 if (stream->nb_streams != 1) {
2800 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2807 for(stream_index = 0; stream_index < stream->nb_streams;
2809 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2810 stream->filename, stream_index);
2811 if (!strcmp(path, buf))
2816 /* no stream found */
2817 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2821 /* generate session id if needed */
2822 if (h->session_id[0] == '\0') {
2823 snprintf(h->session_id, sizeof(h->session_id),
2824 "%08x%08x", (int)random(), (int)random());
2827 /* find rtp session, and create it if none found */
2828 rtp_c = find_rtp_session(h->session_id);
2830 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id);
2832 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2836 /* open input stream */
2837 if (open_input_stream(rtp_c, "") < 0) {
2838 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2842 /* always prefer UDP */
2843 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2845 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2847 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2851 rtp_c->rtp_protocol = th->protocol;
2854 /* test if stream is OK (test needed because several SETUP needs
2855 to be done for a given file) */
2856 if (rtp_c->stream != stream) {
2857 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2861 /* test if stream is already set up */
2862 if (rtp_c->rtp_ctx[stream_index]) {
2863 rtsp_reply_error(c, RTSP_STATUS_STATE);
2867 /* check transport */
2868 th = find_transport(h, rtp_c->rtp_protocol);
2869 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2870 th->client_port_min <= 0)) {
2871 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2875 /* setup default options */
2876 setup.transport_option[0] = '\0';
2877 dest_addr = rtp_c->from_addr;
2878 dest_addr.sin_port = htons(th->client_port_min);
2880 /* add transport option if needed */
2881 if (ff_rtsp_callback) {
2882 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2883 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2884 (char *)&setup, sizeof(setup),
2885 stream->rtsp_option) < 0) {
2886 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2889 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2893 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
2894 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2898 /* now everything is OK, so we can send the connection parameters */
2899 rtsp_reply_header(c, RTSP_STATUS_OK);
2901 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2903 switch(rtp_c->rtp_protocol) {
2904 case RTSP_PROTOCOL_RTP_UDP:
2905 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2906 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2907 "client_port=%d-%d;server_port=%d-%d",
2908 th->client_port_min, th->client_port_min + 1,
2911 case RTSP_PROTOCOL_RTP_TCP:
2912 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2913 stream_index * 2, stream_index * 2 + 1);
2918 if (setup.transport_option[0] != '\0') {
2919 url_fprintf(c->pb, ";%s", setup.transport_option);
2921 url_fprintf(c->pb, "\r\n");
2924 url_fprintf(c->pb, "\r\n");
2928 /* find an rtp connection by using the session ID. Check consistency
2930 static HTTPContext *find_rtp_session_with_url(const char *url,
2931 const char *session_id)
2937 rtp_c = find_rtp_session(session_id);
2941 /* find which url is asked */
2942 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2946 if (strcmp(path, rtp_c->stream->filename) != 0)
2951 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2955 rtp_c = find_rtp_session_with_url(url, h->session_id);
2957 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2961 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2962 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2963 rtp_c->state != HTTPSTATE_READY) {
2964 rtsp_reply_error(c, RTSP_STATUS_STATE);
2968 rtp_c->state = HTTPSTATE_SEND_DATA;
2970 /* now everything is OK, so we can send the connection parameters */
2971 rtsp_reply_header(c, RTSP_STATUS_OK);
2973 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2974 url_fprintf(c->pb, "\r\n");
2977 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
2981 rtp_c = find_rtp_session_with_url(url, h->session_id);
2983 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2987 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2988 rtp_c->state != HTTPSTATE_WAIT_FEED) {
2989 rtsp_reply_error(c, RTSP_STATUS_STATE);
2993 rtp_c->state = HTTPSTATE_READY;
2995 /* now everything is OK, so we can send the connection parameters */
2996 rtsp_reply_header(c, RTSP_STATUS_OK);
2998 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2999 url_fprintf(c->pb, "\r\n");
3002 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3006 rtp_c = find_rtp_session_with_url(url, h->session_id);
3008 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3012 /* abort the session */
3013 close_connection(rtp_c);
3015 if (ff_rtsp_callback) {
3016 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
3018 rtp_c->stream->rtsp_option);
3021 /* now everything is OK, so we can send the connection parameters */
3022 rtsp_reply_header(c, RTSP_STATUS_OK);
3024 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3025 url_fprintf(c->pb, "\r\n");
3029 /********************************************************************/
3032 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3033 FFStream *stream, const char *session_id)
3035 HTTPContext *c = NULL;
3037 /* XXX: should output a warning page when coming
3038 close to the connection limit */
3039 if (nb_connections >= nb_max_connections)
3042 /* add a new connection */
3043 c = av_mallocz(sizeof(HTTPContext));
3048 c->poll_entry = NULL;
3049 c->from_addr = *from_addr;
3050 c->buffer_size = IOBUFFER_INIT_SIZE;
3051 c->buffer = av_malloc(c->buffer_size);
3056 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
3057 c->state = HTTPSTATE_READY;
3058 c->is_packetized = 1;
3059 /* protocol is shown in statistics */
3060 pstrcpy(c->protocol, sizeof(c->protocol), "RTP");
3062 current_bandwidth += stream->bandwidth;
3064 c->next = first_http_ctx;
3076 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3077 command). if dest_addr is NULL, then TCP tunneling in RTSP is
3079 static int rtp_new_av_stream(HTTPContext *c,
3080 int stream_index, struct sockaddr_in *dest_addr)
3082 AVFormatContext *ctx;
3089 /* now we can open the relevant output stream */
3090 ctx = av_mallocz(sizeof(AVFormatContext));
3093 ctx->oformat = &rtp_mux;
3095 st = av_mallocz(sizeof(AVStream));
3098 ctx->nb_streams = 1;
3099 ctx->streams[0] = st;
3101 if (!c->stream->feed ||
3102 c->stream->feed == c->stream) {
3103 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3106 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3111 /* build destination RTP address */
3112 ipaddr = inet_ntoa(dest_addr->sin_addr);
3114 /* XXX: also pass as parameter to function ? */
3115 if (c->stream->is_multicast) {
3117 ttl = c->stream->multicast_ttl;
3120 snprintf(ctx->filename, sizeof(ctx->filename),
3121 "rtp://%s:%d?multicast=1&ttl=%d",
3122 ipaddr, ntohs(dest_addr->sin_port), ttl);
3124 snprintf(ctx->filename, sizeof(ctx->filename),
3125 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3128 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3130 c->rtp_handles[stream_index] = h;
3135 http_log("%s:%d - - [%s] \"RTPSTART %s/streamid=%d\"\n",
3136 ipaddr, ntohs(dest_addr->sin_port),
3138 c->stream->filename, stream_index);
3140 /* normally, no packets should be output here, but the packet size may be checked */
3141 if (url_open_dyn_packet_buf(&ctx->pb,
3142 url_get_max_packet_size(h)) < 0) {
3143 /* XXX: close stream */
3146 if (av_write_header(ctx) < 0) {
3153 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3156 c->rtp_ctx[stream_index] = ctx;
3160 /********************************************************************/
3161 /* ffserver initialization */
3163 AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3167 fst = av_mallocz(sizeof(AVStream));
3170 fst->priv_data = av_mallocz(sizeof(FeedData));
3171 memcpy(&fst->codec, codec, sizeof(AVCodecContext));
3172 fst->codec.coded_frame = &dummy_frame;
3173 stream->streams[stream->nb_streams++] = fst;
3177 /* return the stream number in the feed */
3178 int add_av_stream(FFStream *feed,
3182 AVCodecContext *av, *av1;
3186 for(i=0;i<feed->nb_streams;i++) {
3187 st = feed->streams[i];
3189 if (av1->codec_id == av->codec_id &&
3190 av1->codec_type == av->codec_type &&
3191 av1->bit_rate == av->bit_rate) {
3193 switch(av->codec_type) {
3194 case CODEC_TYPE_AUDIO:
3195 if (av1->channels == av->channels &&
3196 av1->sample_rate == av->sample_rate)
3199 case CODEC_TYPE_VIDEO:
3200 if (av1->width == av->width &&
3201 av1->height == av->height &&
3202 av1->frame_rate == av->frame_rate &&
3203 av1->gop_size == av->gop_size)
3212 fst = add_av_stream1(feed, av);
3215 return feed->nb_streams - 1;
3220 void remove_stream(FFStream *stream)
3224 while (*ps != NULL) {
3225 if (*ps == stream) {
3233 /* specific mpeg4 handling : we extract the raw parameters */
3234 void extract_mpeg4_header(AVFormatContext *infile)
3236 int mpeg4_count, i, size;
3242 for(i=0;i<infile->nb_streams;i++) {
3243 st = infile->streams[i];
3244 if (st->codec.codec_id == CODEC_ID_MPEG4 &&
3245 st->codec.extradata == NULL) {
3252 printf("MPEG4 without extra data: trying to find header\n");
3253 while (mpeg4_count > 0) {
3254 if (av_read_packet(infile, &pkt) < 0)
3256 st = infile->streams[pkt.stream_index];
3257 if (st->codec.codec_id == CODEC_ID_MPEG4 &&
3258 st->codec.extradata == NULL) {
3259 /* fill extradata with the header */
3260 /* XXX: we make hard suppositions here ! */
3262 while (p < pkt.data + pkt.size - 4) {
3263 /* stop when vop header is found */
3264 if (p[0] == 0x00 && p[1] == 0x00 &&
3265 p[2] == 0x01 && p[3] == 0xb6) {
3266 size = p - pkt.data;
3267 // av_hex_dump(pkt.data, size);
3268 st->codec.extradata = av_malloc(size);
3269 st->codec.extradata_size = size;
3270 memcpy(st->codec.extradata, pkt.data, size);
3277 av_free_packet(&pkt);
3281 /* compute the needed AVStream for each file */
3282 void build_file_streams(void)
3284 FFStream *stream, *stream_next;
3285 AVFormatContext *infile;
3288 /* gather all streams */
3289 for(stream = first_stream; stream != NULL; stream = stream_next) {
3290 stream_next = stream->next;
3291 if (stream->stream_type == STREAM_TYPE_LIVE &&
3293 /* the stream comes from a file */
3294 /* try to open the file */
3296 if (av_open_input_file(&infile, stream->feed_filename,
3297 NULL, 0, NULL) < 0) {
3298 http_log("%s not found", stream->feed_filename);
3299 /* remove stream (no need to spend more time on it) */
3301 remove_stream(stream);
3303 /* find all the AVStreams inside and reference them in
3305 if (av_find_stream_info(infile) < 0) {
3306 http_log("Could not find codec parameters from '%s'",
3307 stream->feed_filename);
3308 av_close_input_file(infile);
3311 extract_mpeg4_header(infile);
3313 for(i=0;i<infile->nb_streams;i++) {
3314 add_av_stream1(stream, &infile->streams[i]->codec);
3316 av_close_input_file(infile);
3322 /* compute the needed AVStream for each feed */
3323 void build_feed_streams(void)
3325 FFStream *stream, *feed;
3328 /* gather all streams */
3329 for(stream = first_stream; stream != NULL; stream = stream->next) {
3330 feed = stream->feed;
3332 if (!stream->is_feed) {
3333 /* we handle a stream coming from a feed */
3334 for(i=0;i<stream->nb_streams;i++) {
3335 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3341 /* gather all streams */
3342 for(stream = first_stream; stream != NULL; stream = stream->next) {
3343 feed = stream->feed;
3345 if (stream->is_feed) {
3346 for(i=0;i<stream->nb_streams;i++) {
3347 stream->feed_streams[i] = i;
3353 /* create feed files if needed */
3354 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3357 if (url_exist(feed->feed_filename)) {
3358 /* See if it matches */
3362 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3363 /* Now see if it matches */
3364 if (s->nb_streams == feed->nb_streams) {
3366 for(i=0;i<s->nb_streams;i++) {
3368 sf = feed->streams[i];
3371 if (sf->index != ss->index ||
3373 printf("Index & Id do not match for stream %d\n", i);
3376 AVCodecContext *ccf, *ccs;
3380 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3382 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3383 printf("Codecs do not match for stream %d\n", i);
3385 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3386 printf("Codec bitrates do not match for stream %d\n", i);
3388 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3389 if (CHECK_CODEC(frame_rate) ||
3390 CHECK_CODEC(width) ||
3391 CHECK_CODEC(height)) {
3392 printf("Codec width, height and framerate do not match for stream %d\n", i);
3395 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3396 if (CHECK_CODEC(sample_rate) ||
3397 CHECK_CODEC(channels) ||
3398 CHECK_CODEC(frame_size)) {
3399 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3403 printf("Unknown codec type\n");
3412 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3413 feed->feed_filename, s->nb_streams, feed->nb_streams);
3416 av_close_input_file(s);
3418 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3419 feed->feed_filename);
3422 unlink(feed->feed_filename);
3424 if (!url_exist(feed->feed_filename)) {
3425 AVFormatContext s1, *s = &s1;
3427 /* only write the header of the ffm file */
3428 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3429 fprintf(stderr, "Could not open output feed file '%s'\n",
3430 feed->feed_filename);
3433 s->oformat = feed->fmt;
3434 s->nb_streams = feed->nb_streams;
3435 for(i=0;i<s->nb_streams;i++) {
3437 st = feed->streams[i];
3441 /* XXX: need better api */
3442 av_freep(&s->priv_data);
3445 /* get feed size and write index */
3446 fd = open(feed->feed_filename, O_RDONLY);
3448 fprintf(stderr, "Could not open output feed file '%s'\n",
3449 feed->feed_filename);
3453 feed->feed_write_index = ffm_read_write_index(fd);
3454 feed->feed_size = lseek(fd, 0, SEEK_END);
3455 /* ensure that we do not wrap before the end of file */
3456 if (feed->feed_max_size < feed->feed_size)
3457 feed->feed_max_size = feed->feed_size;
3463 /* compute the bandwidth used by each stream */
3464 static void compute_bandwidth(void)
3469 for(stream = first_stream; stream != NULL; stream = stream->next) {
3471 for(i=0;i<stream->nb_streams;i++) {
3472 AVStream *st = stream->streams[i];
3473 switch(st->codec.codec_type) {
3474 case CODEC_TYPE_AUDIO:
3475 case CODEC_TYPE_VIDEO:
3476 bandwidth += st->codec.bit_rate;
3482 stream->bandwidth = (bandwidth + 999) / 1000;
3486 static void get_arg(char *buf, int buf_size, const char **pp)
3493 while (isspace(*p)) p++;
3496 if (*p == '\"' || *p == '\'')
3508 if ((q - buf) < buf_size - 1)
3513 if (quote && *p == quote)
3518 /* add a codec and set the default parameters */
3519 void add_codec(FFStream *stream, AVCodecContext *av)
3523 /* compute default parameters */
3524 switch(av->codec_type) {
3525 case CODEC_TYPE_AUDIO:
3526 if (av->bit_rate == 0)
3527 av->bit_rate = 64000;
3528 if (av->sample_rate == 0)
3529 av->sample_rate = 22050;
3530 if (av->channels == 0)
3533 case CODEC_TYPE_VIDEO:
3534 if (av->bit_rate == 0)
3535 av->bit_rate = 64000;
3536 if (av->frame_rate == 0)
3537 av->frame_rate = 5 * FRAME_RATE_BASE;
3538 if (av->width == 0 || av->height == 0) {
3542 /* Bitrate tolerance is less for streaming */
3543 if (av->bit_rate_tolerance == 0)
3544 av->bit_rate_tolerance = av->bit_rate / 4;
3549 if (av->max_qdiff == 0)
3551 av->qcompress = 0.5;
3555 av->rc_eq = "tex^qComp";
3556 if (!av->i_quant_factor)
3557 av->i_quant_factor = -0.8;
3558 if (!av->b_quant_factor)
3559 av->b_quant_factor = 1.25;
3560 if (!av->b_quant_offset)
3561 av->b_quant_offset = 1.25;
3562 if (!av->rc_min_rate)
3563 av->rc_min_rate = av->bit_rate / 2;
3564 if (!av->rc_max_rate)
3565 av->rc_max_rate = av->bit_rate * 2;
3572 st = av_mallocz(sizeof(AVStream));
3575 stream->streams[stream->nb_streams++] = st;
3576 memcpy(&st->codec, av, sizeof(AVCodecContext));
3579 int opt_audio_codec(const char *arg)
3585 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3590 return CODEC_ID_NONE;
3596 int opt_video_codec(const char *arg)
3602 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3607 return CODEC_ID_NONE;
3613 /* simplistic plugin support */
3615 #ifdef CONFIG_HAVE_DLOPEN
3616 void load_module(const char *filename)
3619 void (*init_func)(void);
3620 dll = dlopen(filename, RTLD_NOW);
3622 fprintf(stderr, "Could not load module '%s' - %s\n",
3623 filename, dlerror());
3627 init_func = dlsym(dll, "ffserver_module_init");
3630 "%s: init function 'ffserver_module_init()' not found\n",
3639 int parse_ffconfig(const char *filename)
3646 int val, errors, line_num;
3647 FFStream **last_stream, *stream, *redirect;
3648 FFStream **last_feed, *feed;
3649 AVCodecContext audio_enc, video_enc;
3650 int audio_id, video_id;
3652 f = fopen(filename, "r");
3660 first_stream = NULL;
3661 last_stream = &first_stream;
3663 last_feed = &first_feed;
3667 audio_id = CODEC_ID_NONE;
3668 video_id = CODEC_ID_NONE;
3670 if (fgets(line, sizeof(line), f) == NULL)
3676 if (*p == '\0' || *p == '#')
3679 get_arg(cmd, sizeof(cmd), &p);
3681 if (!strcasecmp(cmd, "Port")) {
3682 get_arg(arg, sizeof(arg), &p);
3683 my_http_addr.sin_port = htons (atoi(arg));
3684 } else if (!strcasecmp(cmd, "BindAddress")) {
3685 get_arg(arg, sizeof(arg), &p);
3686 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3687 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3688 filename, line_num, arg);
3691 } else if (!strcasecmp(cmd, "NoDaemon")) {
3692 ffserver_daemon = 0;
3693 } else if (!strcasecmp(cmd, "RTSPPort")) {
3694 get_arg(arg, sizeof(arg), &p);
3695 my_rtsp_addr.sin_port = htons (atoi(arg));
3696 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3697 get_arg(arg, sizeof(arg), &p);
3698 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3699 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3700 filename, line_num, arg);
3703 } else if (!strcasecmp(cmd, "MaxClients")) {
3704 get_arg(arg, sizeof(arg), &p);
3706 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3707 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3708 filename, line_num, arg);
3711 nb_max_connections = val;
3713 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3714 get_arg(arg, sizeof(arg), &p);
3716 if (val < 10 || val > 100000) {
3717 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3718 filename, line_num, arg);
3721 max_bandwidth = val;
3723 } else if (!strcasecmp(cmd, "CustomLog")) {
3724 get_arg(logfilename, sizeof(logfilename), &p);
3725 } else if (!strcasecmp(cmd, "<Feed")) {
3726 /*********************************************/
3727 /* Feed related options */
3729 if (stream || feed) {
3730 fprintf(stderr, "%s:%d: Already in a tag\n",
3731 filename, line_num);
3733 feed = av_mallocz(sizeof(FFStream));
3734 /* add in stream list */
3735 *last_stream = feed;
3736 last_stream = &feed->next;
3737 /* add in feed list */
3739 last_feed = &feed->next_feed;
3741 get_arg(feed->filename, sizeof(feed->filename), &p);
3742 q = strrchr(feed->filename, '>');
3745 feed->fmt = guess_format("ffm", NULL, NULL);
3746 /* defaut feed file */
3747 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3748 "/tmp/%s.ffm", feed->filename);
3749 feed->feed_max_size = 5 * 1024 * 1024;
3751 feed->feed = feed; /* self feeding :-) */
3753 } else if (!strcasecmp(cmd, "Launch")) {
3757 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3759 feed->child_argv[0] = av_malloc(7);
3760 strcpy(feed->child_argv[0], "ffmpeg");
3762 for (i = 1; i < 62; i++) {
3765 get_arg(argbuf, sizeof(argbuf), &p);
3769 feed->child_argv[i] = av_malloc(strlen(argbuf + 1));
3770 strcpy(feed->child_argv[i], argbuf);
3773 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3775 snprintf(feed->child_argv[i], 256, "http://127.0.0.1:%d/%s",
3776 ntohs(my_http_addr.sin_port), feed->filename);
3778 } else if (!strcasecmp(cmd, "File")) {
3780 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3781 } else if (stream) {
3782 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3784 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3789 get_arg(arg, sizeof(arg), &p);
3791 fsize = strtod(p1, (char **)&p1);
3792 switch(toupper(*p1)) {
3797 fsize *= 1024 * 1024;
3800 fsize *= 1024 * 1024 * 1024;
3803 feed->feed_max_size = (INT64)fsize;
3805 } else if (!strcasecmp(cmd, "</Feed>")) {
3807 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3808 filename, line_num);
3812 /* Make sure that we start out clean */
3813 if (unlink(feed->feed_filename) < 0
3814 && errno != ENOENT) {
3815 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3816 filename, line_num, feed->feed_filename, strerror(errno));
3822 } else if (!strcasecmp(cmd, "<Stream")) {
3823 /*********************************************/
3824 /* Stream related options */
3826 if (stream || feed) {
3827 fprintf(stderr, "%s:%d: Already in a tag\n",
3828 filename, line_num);
3830 stream = av_mallocz(sizeof(FFStream));
3831 *last_stream = stream;
3832 last_stream = &stream->next;
3834 get_arg(stream->filename, sizeof(stream->filename), &p);
3835 q = strrchr(stream->filename, '>');
3838 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3839 memset(&audio_enc, 0, sizeof(AVCodecContext));
3840 memset(&video_enc, 0, sizeof(AVCodecContext));
3841 audio_id = CODEC_ID_NONE;
3842 video_id = CODEC_ID_NONE;
3844 audio_id = stream->fmt->audio_codec;
3845 video_id = stream->fmt->video_codec;
3848 } else if (!strcasecmp(cmd, "Feed")) {
3849 get_arg(arg, sizeof(arg), &p);
3854 while (sfeed != NULL) {
3855 if (!strcmp(sfeed->filename, arg))
3857 sfeed = sfeed->next_feed;
3860 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3861 filename, line_num, arg);
3863 stream->feed = sfeed;
3866 } else if (!strcasecmp(cmd, "Format")) {
3867 get_arg(arg, sizeof(arg), &p);
3868 if (!strcmp(arg, "status")) {
3869 stream->stream_type = STREAM_TYPE_STATUS;
3872 stream->stream_type = STREAM_TYPE_LIVE;
3873 /* jpeg cannot be used here, so use single frame jpeg */
3874 if (!strcmp(arg, "jpeg"))
3875 strcpy(arg, "singlejpeg");
3876 stream->fmt = guess_stream_format(arg, NULL, NULL);
3878 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3879 filename, line_num, arg);
3884 audio_id = stream->fmt->audio_codec;
3885 video_id = stream->fmt->video_codec;
3887 } else if (!strcasecmp(cmd, "FaviconURL")) {
3888 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3889 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3891 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3892 filename, line_num);
3895 } else if (!strcasecmp(cmd, "Author")) {
3897 get_arg(stream->author, sizeof(stream->author), &p);
3899 } else if (!strcasecmp(cmd, "Comment")) {
3901 get_arg(stream->comment, sizeof(stream->comment), &p);
3903 } else if (!strcasecmp(cmd, "Copyright")) {
3905 get_arg(stream->copyright, sizeof(stream->copyright), &p);
3907 } else if (!strcasecmp(cmd, "Title")) {
3909 get_arg(stream->title, sizeof(stream->title), &p);
3911 } else if (!strcasecmp(cmd, "Preroll")) {
3912 get_arg(arg, sizeof(arg), &p);
3914 stream->prebuffer = atof(arg) * 1000;
3916 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
3918 stream->send_on_key = 1;
3920 } else if (!strcasecmp(cmd, "AudioCodec")) {
3921 get_arg(arg, sizeof(arg), &p);
3922 audio_id = opt_audio_codec(arg);
3923 if (audio_id == CODEC_ID_NONE) {
3924 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
3925 filename, line_num, arg);
3928 } else if (!strcasecmp(cmd, "VideoCodec")) {
3929 get_arg(arg, sizeof(arg), &p);
3930 video_id = opt_video_codec(arg);
3931 if (video_id == CODEC_ID_NONE) {
3932 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
3933 filename, line_num, arg);
3936 } else if (!strcasecmp(cmd, "MaxTime")) {
3937 get_arg(arg, sizeof(arg), &p);
3939 stream->max_time = atof(arg) * 1000;
3941 } else if (!strcasecmp(cmd, "AudioBitRate")) {
3942 get_arg(arg, sizeof(arg), &p);
3944 audio_enc.bit_rate = atoi(arg) * 1000;
3946 } else if (!strcasecmp(cmd, "AudioChannels")) {
3947 get_arg(arg, sizeof(arg), &p);
3949 audio_enc.channels = atoi(arg);
3951 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
3952 get_arg(arg, sizeof(arg), &p);
3954 audio_enc.sample_rate = atoi(arg);
3956 } else if (!strcasecmp(cmd, "AudioQuality")) {
3957 get_arg(arg, sizeof(arg), &p);
3959 // audio_enc.quality = atof(arg) * 1000;
3961 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
3963 int minrate, maxrate;
3965 get_arg(arg, sizeof(arg), &p);
3967 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
3968 video_enc.rc_min_rate = minrate * 1000;
3969 video_enc.rc_max_rate = maxrate * 1000;
3971 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
3972 filename, line_num, arg);
3976 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
3978 get_arg(arg, sizeof(arg), &p);
3979 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
3981 } else if (!strcasecmp(cmd, "VideoBitRate")) {
3982 get_arg(arg, sizeof(arg), &p);
3984 video_enc.bit_rate = atoi(arg) * 1000;
3986 } else if (!strcasecmp(cmd, "VideoSize")) {
3987 get_arg(arg, sizeof(arg), &p);
3989 parse_image_size(&video_enc.width, &video_enc.height, arg);
3990 if ((video_enc.width % 16) != 0 ||
3991 (video_enc.height % 16) != 0) {
3992 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
3993 filename, line_num);
3997 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
3998 get_arg(arg, sizeof(arg), &p);
4000 video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
4002 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4003 get_arg(arg, sizeof(arg), &p);
4005 video_enc.gop_size = atoi(arg);
4007 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4009 video_enc.gop_size = 1;
4011 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4013 video_enc.flags |= CODEC_FLAG_HQ;
4015 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4016 get_arg(arg, sizeof(arg), &p);
4018 video_enc.max_qdiff = atoi(arg);
4019 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4020 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4021 filename, line_num);
4025 } else if (!strcasecmp(cmd, "VideoQMax")) {
4026 get_arg(arg, sizeof(arg), &p);
4028 video_enc.qmax = atoi(arg);
4029 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4030 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4031 filename, line_num);
4035 } else if (!strcasecmp(cmd, "VideoQMin")) {
4036 get_arg(arg, sizeof(arg), &p);
4038 video_enc.qmin = atoi(arg);
4039 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4040 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4041 filename, line_num);
4045 } else if (!strcasecmp(cmd, "LumaElim")) {
4046 get_arg(arg, sizeof(arg), &p);
4048 video_enc.luma_elim_threshold = atoi(arg);
4050 } else if (!strcasecmp(cmd, "ChromaElim")) {
4051 get_arg(arg, sizeof(arg), &p);
4053 video_enc.chroma_elim_threshold = atoi(arg);
4055 } else if (!strcasecmp(cmd, "LumiMask")) {
4056 get_arg(arg, sizeof(arg), &p);
4058 video_enc.lumi_masking = atof(arg);
4060 } else if (!strcasecmp(cmd, "DarkMask")) {
4061 get_arg(arg, sizeof(arg), &p);
4063 video_enc.dark_masking = atof(arg);
4065 } else if (!strcasecmp(cmd, "NoVideo")) {
4066 video_id = CODEC_ID_NONE;
4067 } else if (!strcasecmp(cmd, "NoAudio")) {
4068 audio_id = CODEC_ID_NONE;
4069 } else if (!strcasecmp(cmd, "ACL")) {
4073 get_arg(arg, sizeof(arg), &p);
4074 if (strcasecmp(arg, "allow") == 0) {
4075 acl.action = IP_ALLOW;
4076 } else if (strcasecmp(arg, "deny") == 0) {
4077 acl.action = IP_DENY;
4079 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4080 filename, line_num, arg);
4084 get_arg(arg, sizeof(arg), &p);
4086 he = gethostbyname(arg);
4088 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4089 filename, line_num, arg);
4092 /* Only take the first */
4093 acl.first = *(struct in_addr *) he->h_addr_list[0];
4094 acl.last = acl.first;
4097 get_arg(arg, sizeof(arg), &p);
4100 he = gethostbyname(arg);
4102 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4103 filename, line_num, arg);
4106 /* Only take the first */
4107 acl.last = *(struct in_addr *) he->h_addr_list[0];
4112 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4113 IPAddressACL **naclp = 0;
4119 naclp = &stream->acl;
4123 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4124 filename, line_num);
4130 naclp = &(*naclp)->next;
4135 } else if (!strcasecmp(cmd, "RTSPOption")) {
4136 get_arg(arg, sizeof(arg), &p);
4138 av_freep(&stream->rtsp_option);
4139 /* XXX: av_strdup ? */
4140 stream->rtsp_option = av_malloc(strlen(arg) + 1);
4141 if (stream->rtsp_option) {
4142 strcpy(stream->rtsp_option, arg);
4145 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4146 get_arg(arg, sizeof(arg), &p);
4148 if (!inet_aton(arg, &stream->multicast_ip)) {
4149 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
4150 filename, line_num, arg);
4153 stream->is_multicast = 1;
4154 stream->loop = 1; /* default is looping */
4156 } else if (!strcasecmp(cmd, "MulticastPort")) {
4157 get_arg(arg, sizeof(arg), &p);
4159 stream->multicast_port = atoi(arg);
4161 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4162 get_arg(arg, sizeof(arg), &p);
4164 stream->multicast_ttl = atoi(arg);
4166 } else if (!strcasecmp(cmd, "NoLoop")) {
4170 } else if (!strcasecmp(cmd, "</Stream>")) {
4172 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4173 filename, line_num);
4176 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4177 if (audio_id != CODEC_ID_NONE) {
4178 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4179 audio_enc.codec_id = audio_id;
4180 add_codec(stream, &audio_enc);
4182 if (video_id != CODEC_ID_NONE) {
4183 video_enc.codec_type = CODEC_TYPE_VIDEO;
4184 video_enc.codec_id = video_id;
4185 add_codec(stream, &video_enc);
4189 } else if (!strcasecmp(cmd, "<Redirect")) {
4190 /*********************************************/
4192 if (stream || feed || redirect) {
4193 fprintf(stderr, "%s:%d: Already in a tag\n",
4194 filename, line_num);
4197 redirect = av_mallocz(sizeof(FFStream));
4198 *last_stream = redirect;
4199 last_stream = &redirect->next;
4201 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4202 q = strrchr(redirect->filename, '>');
4205 redirect->stream_type = STREAM_TYPE_REDIRECT;
4207 } else if (!strcasecmp(cmd, "URL")) {
4209 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4211 } else if (!strcasecmp(cmd, "</Redirect>")) {
4213 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4214 filename, line_num);
4217 if (!redirect->feed_filename[0]) {
4218 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4219 filename, line_num);
4223 } else if (!strcasecmp(cmd, "LoadModule")) {
4224 get_arg(arg, sizeof(arg), &p);
4225 #ifdef CONFIG_HAVE_DLOPEN
4228 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4229 filename, line_num, arg);
4233 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4234 filename, line_num, cmd);
4248 static void write_packet(FFCodec *ffenc,
4249 UINT8 *buf, int size)
4252 AVCodecContext *enc = &ffenc->enc;
4254 mk_header(&hdr, enc, size);
4255 wptr = http_fifo.wptr;
4256 fifo_write(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &wptr);
4257 fifo_write(&http_fifo, buf, size, &wptr);
4258 /* atomic modification of wptr */
4259 http_fifo.wptr = wptr;
4260 ffenc->data_count += size;
4261 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4267 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4268 "usage: ffserver [-L] [-h] [-f configfile]\n"
4269 "Hyper fast multi format Audio/Video streaming server\n"
4271 "-L : print the LICENCE\n"
4273 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4280 "ffserver version " FFMPEG_VERSION "\n"
4281 "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4282 "This library is free software; you can redistribute it and/or\n"
4283 "modify it under the terms of the GNU Lesser General Public\n"
4284 "License as published by the Free Software Foundation; either\n"
4285 "version 2 of the License, or (at your option) any later version.\n"
4287 "This library is distributed in the hope that it will be useful,\n"
4288 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4289 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4290 "Lesser General Public License for more details.\n"
4292 "You should have received a copy of the GNU Lesser General Public\n"
4293 "License along with this library; if not, write to the Free Software\n"
4294 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
4298 static void handle_child_exit(int sig)
4303 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4306 for (feed = first_feed; feed; feed = feed->next) {
4307 if (feed->pid == pid) {
4308 int uptime = time(0) - feed->pid_start;
4311 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4314 /* Turn off any more restarts */
4315 feed->child_argv = 0;
4321 need_to_start_children = 1;
4324 int main(int argc, char **argv)
4326 const char *config_filename;
4328 struct sigaction sigact;
4332 config_filename = "/etc/ffserver.conf";
4334 my_program_name = argv[0];
4335 my_program_dir = getcwd(0, 0);
4336 ffserver_daemon = 1;
4339 c = getopt(argc, argv, "ndLh?f:");
4355 ffserver_daemon = 0;
4358 config_filename = optarg;
4365 putenv("http_proxy"); /* Kill the http_proxy */
4367 srandom(gettime_ms() + (getpid() << 16));
4369 /* address on which the server will handle HTTP connections */
4370 my_http_addr.sin_family = AF_INET;
4371 my_http_addr.sin_port = htons (8080);
4372 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4374 /* address on which the server will handle RTSP connections */
4375 my_rtsp_addr.sin_family = AF_INET;
4376 my_rtsp_addr.sin_port = htons (5454);
4377 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4379 nb_max_connections = 5;
4380 max_bandwidth = 1000;
4381 first_stream = NULL;
4382 logfilename[0] = '\0';
4384 memset(&sigact, 0, sizeof(sigact));
4385 sigact.sa_handler = handle_child_exit;
4386 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4387 sigaction(SIGCHLD, &sigact, 0);
4389 if (parse_ffconfig(config_filename) < 0) {
4390 fprintf(stderr, "Incorrect config file - exiting.\n");
4394 build_file_streams();
4396 build_feed_streams();
4398 compute_bandwidth();
4400 /* put the process in background and detach it from its TTY */
4401 if (ffserver_daemon) {
4408 } else if (pid > 0) {
4416 open("/dev/null", O_RDWR);
4417 if (strcmp(logfilename, "-") != 0) {
4427 signal(SIGPIPE, SIG_IGN);
4429 /* open log file if needed */
4430 if (logfilename[0] != '\0') {
4431 if (!strcmp(logfilename, "-"))
4434 logfile = fopen(logfilename, "w");
4437 if (http_server() < 0) {
4438 fprintf(stderr, "Could not start server\n");