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_set_parameters(&c->fmt_ctx, NULL);
2117 av_write_header(&c->fmt_ctx);
2119 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2120 c->buffer_ptr = c->pb_buffer;
2121 c->buffer_end = c->pb_buffer + len;
2123 c->state = HTTPSTATE_SEND_DATA;
2124 c->last_packet_sent = 0;
2126 case HTTPSTATE_SEND_DATA:
2127 /* find a new packet */
2131 /* read a packet from the input stream */
2132 if (c->stream->feed) {
2133 ffm_set_write_index(c->fmt_in,
2134 c->stream->feed->feed_write_index,
2135 c->stream->feed->feed_size);
2138 if (c->stream->max_time &&
2139 c->stream->max_time + c->start_time - cur_time < 0) {
2140 /* We have timed out */
2141 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2143 if (1 || c->is_packetized) {
2144 if (compute_send_delay(c) > 0) {
2145 c->state = HTTPSTATE_WAIT;
2146 return 1; /* state changed */
2150 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2151 if (c->stream->feed && c->stream->feed->feed_opened) {
2152 /* if coming from feed, it means we reached the end of the
2153 ffm file, so must wait for more data */
2154 c->state = HTTPSTATE_WAIT_FEED;
2155 return 1; /* state changed */
2157 if (c->stream->loop) {
2158 av_close_input_file(c->fmt_in);
2160 if (open_input_stream(c, "") < 0)
2165 /* must send trailer now because eof or error */
2166 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2170 /* update first pts if needed */
2171 if (c->first_pts == AV_NOPTS_VALUE)
2172 c->first_pts = pkt.pts;
2174 /* send it to the appropriate stream */
2175 if (c->stream->feed) {
2176 /* if coming from a feed, select the right stream */
2177 if (c->switch_pending) {
2178 c->switch_pending = 0;
2179 for(i=0;i<c->stream->nb_streams;i++) {
2180 if (c->switch_feed_streams[i] == pkt.stream_index) {
2181 if (pkt.flags & PKT_FLAG_KEY) {
2182 do_switch_stream(c, i);
2185 if (c->switch_feed_streams[i] >= 0) {
2186 c->switch_pending = 1;
2190 for(i=0;i<c->stream->nb_streams;i++) {
2191 if (c->feed_streams[i] == pkt.stream_index) {
2192 pkt.stream_index = i;
2193 if (pkt.flags & PKT_FLAG_KEY) {
2194 c->got_key_frame |= 1 << i;
2196 /* See if we have all the key frames, then
2197 * we start to send. This logic is not quite
2198 * right, but it works for the case of a
2199 * single video stream with one or more
2200 * audio streams (for which every frame is
2201 * typically a key frame).
2203 if (!c->stream->send_on_key ||
2204 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2210 AVCodecContext *codec;
2213 /* specific handling for RTP: we use several
2214 output stream (one for each RTP
2215 connection). XXX: need more abstract handling */
2216 if (c->is_packetized) {
2217 c->packet_stream_index = pkt.stream_index;
2218 ctx = c->rtp_ctx[c->packet_stream_index];
2219 codec = &ctx->streams[0]->codec;
2220 /* only one stream per RTP connection */
2221 pkt.stream_index = 0;
2225 codec = &ctx->streams[pkt.stream_index]->codec;
2228 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2231 if (codec->codec_type == CODEC_TYPE_AUDIO) {
2232 codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
2233 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
2237 if (c->is_packetized) {
2238 ret = url_open_dyn_packet_buf(&ctx->pb,
2239 url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]));
2240 c->packet_byte_count = 0;
2241 c->packet_start_time_us = av_gettime();
2243 ret = url_open_dyn_buf(&ctx->pb);
2246 /* XXX: potential leak */
2249 if (av_write_frame(ctx, pkt.stream_index, pkt.data, pkt.size)) {
2250 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2253 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2254 c->buffer_ptr = c->pb_buffer;
2255 c->buffer_end = c->pb_buffer + len;
2257 codec->frame_number++;
2259 #ifndef AV_READ_FRAME
2260 av_free_packet(&pkt);
2267 case HTTPSTATE_SEND_DATA_TRAILER:
2268 /* last packet test ? */
2269 if (c->last_packet_sent || c->is_packetized)
2272 /* prepare header */
2273 if (url_open_dyn_buf(&ctx->pb) < 0) {
2274 /* XXX: potential leak */
2277 av_write_trailer(ctx);
2278 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2279 c->buffer_ptr = c->pb_buffer;
2280 c->buffer_end = c->pb_buffer + len;
2282 c->last_packet_sent = 1;
2289 #define SHORT_TERM_BANDWIDTH 8000000
2291 /* should convert the format at the same time */
2292 static int http_send_data(HTTPContext *c)
2296 while (c->buffer_ptr >= c->buffer_end) {
2297 av_freep(&c->pb_buffer);
2298 ret = http_prepare_data(c);
2301 else if (ret == 0) {
2304 /* state change requested */
2309 if (c->buffer_ptr < c->buffer_end) {
2310 if (c->is_packetized) {
2311 /* RTP/UDP data output */
2312 len = c->buffer_end - c->buffer_ptr;
2314 /* fail safe - should never happen */
2316 c->buffer_ptr = c->buffer_end;
2319 len = (c->buffer_ptr[0] << 24) |
2320 (c->buffer_ptr[1] << 16) |
2321 (c->buffer_ptr[2] << 8) |
2323 if (len > (c->buffer_end - c->buffer_ptr))
2326 /* short term bandwidth limitation */
2327 dt = av_gettime() - c->packet_start_time_us;
2331 if ((c->packet_byte_count + len) * (INT64)1000000 >=
2332 (SHORT_TERM_BANDWIDTH / 8) * (INT64)dt) {
2333 /* bandwidth overflow : wait at most one tick and retry */
2334 c->state = HTTPSTATE_WAIT_SHORT;
2339 url_write(c->rtp_handles[c->packet_stream_index],
2340 c->buffer_ptr, len);
2341 c->buffer_ptr += len;
2342 c->packet_byte_count += len;
2344 /* TCP data output */
2345 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2347 if (errno != EAGAIN && errno != EINTR) {
2348 /* error : close connection */
2354 c->buffer_ptr += len;
2357 c->data_count += len;
2358 update_datarate(&c->datarate, c->data_count);
2360 c->stream->bytes_served += len;
2365 static int http_start_receive_data(HTTPContext *c)
2369 if (c->stream->feed_opened)
2373 fd = open(c->stream->feed_filename, O_RDWR);
2378 c->stream->feed_write_index = ffm_read_write_index(fd);
2379 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2380 lseek(fd, 0, SEEK_SET);
2382 /* init buffer input */
2383 c->buffer_ptr = c->buffer;
2384 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2385 c->stream->feed_opened = 1;
2389 static int http_receive_data(HTTPContext *c)
2393 if (c->buffer_end > c->buffer_ptr) {
2396 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2398 if (errno != EAGAIN && errno != EINTR) {
2399 /* error : close connection */
2402 } else if (len == 0) {
2403 /* end of connection : close it */
2406 c->buffer_ptr += len;
2407 c->data_count += len;
2408 update_datarate(&c->datarate, c->data_count);
2412 if (c->buffer_ptr >= c->buffer_end) {
2413 FFStream *feed = c->stream;
2414 /* a packet has been received : write it in the store, except
2416 if (c->data_count > FFM_PACKET_SIZE) {
2418 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2419 /* XXX: use llseek or url_seek */
2420 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2421 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2423 feed->feed_write_index += FFM_PACKET_SIZE;
2424 /* update file size */
2425 if (feed->feed_write_index > c->stream->feed_size)
2426 feed->feed_size = feed->feed_write_index;
2428 /* handle wrap around if max file size reached */
2429 if (feed->feed_write_index >= c->stream->feed_max_size)
2430 feed->feed_write_index = FFM_PACKET_SIZE;
2433 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2435 /* wake up any waiting connections */
2436 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2437 if (c1->state == HTTPSTATE_WAIT_FEED &&
2438 c1->stream->feed == c->stream->feed) {
2439 c1->state = HTTPSTATE_SEND_DATA;
2443 /* We have a header in our hands that contains useful data */
2445 AVInputFormat *fmt_in;
2446 ByteIOContext *pb = &s.pb;
2449 memset(&s, 0, sizeof(s));
2451 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2452 pb->buf_end = c->buffer_end; /* ?? */
2453 pb->is_streamed = 1;
2455 /* use feed output format name to find corresponding input format */
2456 fmt_in = av_find_input_format(feed->fmt->name);
2460 if (fmt_in->priv_data_size > 0) {
2461 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2467 if (fmt_in->read_header(&s, 0) < 0) {
2468 av_freep(&s.priv_data);
2472 /* Now we have the actual streams */
2473 if (s.nb_streams != feed->nb_streams) {
2474 av_freep(&s.priv_data);
2477 for (i = 0; i < s.nb_streams; i++) {
2478 memcpy(&feed->streams[i]->codec,
2479 &s.streams[i]->codec, sizeof(AVCodecContext));
2481 av_freep(&s.priv_data);
2483 c->buffer_ptr = c->buffer;
2488 c->stream->feed_opened = 0;
2493 /********************************************************************/
2496 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2503 switch(error_number) {
2504 #define DEF(n, c, s) case c: str = s; break;
2505 #include "rtspcodes.h"
2508 str = "Unknown Error";
2512 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2513 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2515 /* output GMT time */
2519 p = buf2 + strlen(p) - 1;
2522 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2525 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2527 rtsp_reply_header(c, error_number);
2528 url_fprintf(c->pb, "\r\n");
2531 static int rtsp_parse_request(HTTPContext *c)
2533 const char *p, *p1, *p2;
2540 RTSPHeader header1, *header = &header1;
2542 c->buffer_ptr[0] = '\0';
2545 get_word(cmd, sizeof(cmd), &p);
2546 get_word(url, sizeof(url), &p);
2547 get_word(protocol, sizeof(protocol), &p);
2549 pstrcpy(c->method, sizeof(c->method), cmd);
2550 pstrcpy(c->url, sizeof(c->url), url);
2551 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2554 if (url_open_dyn_buf(c->pb) < 0) {
2555 /* XXX: cannot do more */
2556 c->pb = NULL; /* safety */
2560 /* check version name */
2561 if (strcmp(protocol, "RTSP/1.0") != 0) {
2562 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2566 /* parse each header line */
2567 memset(header, 0, sizeof(RTSPHeader));
2568 /* skip to next line */
2569 while (*p != '\n' && *p != '\0')
2573 while (*p != '\0') {
2574 p1 = strchr(p, '\n');
2578 if (p2 > p && p2[-1] == '\r')
2580 /* skip empty line */
2584 if (len > sizeof(line) - 1)
2585 len = sizeof(line) - 1;
2586 memcpy(line, p, len);
2588 rtsp_parse_line(header, line);
2592 /* handle sequence number */
2593 c->seq = header->seq;
2595 if (!strcmp(cmd, "DESCRIBE")) {
2596 rtsp_cmd_describe(c, url);
2597 } else if (!strcmp(cmd, "SETUP")) {
2598 rtsp_cmd_setup(c, url, header);
2599 } else if (!strcmp(cmd, "PLAY")) {
2600 rtsp_cmd_play(c, url, header);
2601 } else if (!strcmp(cmd, "PAUSE")) {
2602 rtsp_cmd_pause(c, url, header);
2603 } else if (!strcmp(cmd, "TEARDOWN")) {
2604 rtsp_cmd_teardown(c, url, header);
2606 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2609 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2610 c->pb = NULL; /* safety */
2612 /* XXX: cannot do more */
2615 c->buffer_ptr = c->pb_buffer;
2616 c->buffer_end = c->pb_buffer + len;
2617 c->state = RTSPSTATE_SEND_REPLY;
2621 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2623 static int prepare_sdp_description(FFStream *stream, UINT8 **pbuffer,
2624 struct in_addr my_ip)
2626 ByteIOContext pb1, *pb = &pb1;
2627 int i, payload_type, port, private_payload_type, j;
2628 const char *ipstr, *title, *mediatype;
2631 if (url_open_dyn_buf(pb) < 0)
2634 /* general media info */
2636 url_fprintf(pb, "v=0\n");
2637 ipstr = inet_ntoa(my_ip);
2638 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2639 title = stream->title;
2640 if (title[0] == '\0')
2642 url_fprintf(pb, "s=%s\n", title);
2643 if (stream->comment[0] != '\0')
2644 url_fprintf(pb, "i=%s\n", stream->comment);
2645 if (stream->is_multicast) {
2646 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2648 /* for each stream, we output the necessary info */
2649 private_payload_type = 96;
2650 for(i = 0; i < stream->nb_streams; i++) {
2651 st = stream->streams[i];
2652 switch(st->codec.codec_type) {
2653 case CODEC_TYPE_AUDIO:
2654 mediatype = "audio";
2656 case CODEC_TYPE_VIDEO:
2657 mediatype = "video";
2660 mediatype = "application";
2663 /* NOTE: the port indication is not correct in case of
2664 unicast. It is not an issue because RTSP gives it */
2665 payload_type = rtp_get_payload_type(&st->codec);
2666 if (payload_type < 0)
2667 payload_type = private_payload_type++;
2668 if (stream->is_multicast) {
2669 port = stream->multicast_port + 2 * i;
2673 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2674 mediatype, port, payload_type);
2675 if (payload_type >= 96) {
2676 /* for private payload type, we need to give more info */
2677 switch(st->codec.codec_id) {
2678 case CODEC_ID_MPEG4:
2681 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2682 payload_type, 90000);
2683 /* we must also add the mpeg4 header */
2684 data = st->codec.extradata;
2686 url_fprintf(pb, "a=fmtp:%d config=");
2687 for(j=0;j<st->codec.extradata_size;j++) {
2688 url_fprintf(pb, "%02x", data[j]);
2690 url_fprintf(pb, "\n");
2695 /* XXX: add other codecs ? */
2699 url_fprintf(pb, "a=control:streamid=%d\n", i);
2701 return url_close_dyn_buf(pb, pbuffer);
2703 url_close_dyn_buf(pb, pbuffer);
2708 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2714 int content_length, len;
2715 struct sockaddr_in my_addr;
2717 /* find which url is asked */
2718 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2723 for(stream = first_stream; stream != NULL; stream = stream->next) {
2724 if (!stream->is_feed && stream->fmt == &rtp_mux &&
2725 !strcmp(path, stream->filename)) {
2729 /* no stream found */
2730 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2734 /* prepare the media description in sdp format */
2736 /* get the host IP */
2737 len = sizeof(my_addr);
2738 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2740 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2741 if (content_length < 0) {
2742 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2745 rtsp_reply_header(c, RTSP_STATUS_OK);
2746 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2747 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2748 url_fprintf(c->pb, "\r\n");
2749 put_buffer(c->pb, content, content_length);
2752 static HTTPContext *find_rtp_session(const char *session_id)
2756 if (session_id[0] == '\0')
2759 for(c = first_http_ctx; c != NULL; c = c->next) {
2760 if (!strcmp(c->session_id, session_id))
2766 RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2768 RTSPTransportField *th;
2771 for(i=0;i<h->nb_transports;i++) {
2772 th = &h->transports[i];
2773 if (th->protocol == protocol)
2779 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2783 int stream_index, port;
2788 RTSPTransportField *th;
2789 struct sockaddr_in dest_addr;
2790 RTSPActionServerSetup setup;
2792 /* find which url is asked */
2793 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2798 /* now check each stream */
2799 for(stream = first_stream; stream != NULL; stream = stream->next) {
2800 if (!stream->is_feed && stream->fmt == &rtp_mux) {
2801 /* accept aggregate filenames only if single stream */
2802 if (!strcmp(path, stream->filename)) {
2803 if (stream->nb_streams != 1) {
2804 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2811 for(stream_index = 0; stream_index < stream->nb_streams;
2813 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2814 stream->filename, stream_index);
2815 if (!strcmp(path, buf))
2820 /* no stream found */
2821 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2825 /* generate session id if needed */
2826 if (h->session_id[0] == '\0') {
2827 snprintf(h->session_id, sizeof(h->session_id),
2828 "%08x%08x", (int)random(), (int)random());
2831 /* find rtp session, and create it if none found */
2832 rtp_c = find_rtp_session(h->session_id);
2834 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id);
2836 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2840 /* open input stream */
2841 if (open_input_stream(rtp_c, "") < 0) {
2842 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2846 /* always prefer UDP */
2847 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2849 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2851 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2855 rtp_c->rtp_protocol = th->protocol;
2858 /* test if stream is OK (test needed because several SETUP needs
2859 to be done for a given file) */
2860 if (rtp_c->stream != stream) {
2861 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2865 /* test if stream is already set up */
2866 if (rtp_c->rtp_ctx[stream_index]) {
2867 rtsp_reply_error(c, RTSP_STATUS_STATE);
2871 /* check transport */
2872 th = find_transport(h, rtp_c->rtp_protocol);
2873 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2874 th->client_port_min <= 0)) {
2875 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2879 /* setup default options */
2880 setup.transport_option[0] = '\0';
2881 dest_addr = rtp_c->from_addr;
2882 dest_addr.sin_port = htons(th->client_port_min);
2884 /* add transport option if needed */
2885 if (ff_rtsp_callback) {
2886 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2887 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2888 (char *)&setup, sizeof(setup),
2889 stream->rtsp_option) < 0) {
2890 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2893 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2897 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
2898 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2902 /* now everything is OK, so we can send the connection parameters */
2903 rtsp_reply_header(c, RTSP_STATUS_OK);
2905 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2907 switch(rtp_c->rtp_protocol) {
2908 case RTSP_PROTOCOL_RTP_UDP:
2909 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2910 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2911 "client_port=%d-%d;server_port=%d-%d",
2912 th->client_port_min, th->client_port_min + 1,
2915 case RTSP_PROTOCOL_RTP_TCP:
2916 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2917 stream_index * 2, stream_index * 2 + 1);
2922 if (setup.transport_option[0] != '\0') {
2923 url_fprintf(c->pb, ";%s", setup.transport_option);
2925 url_fprintf(c->pb, "\r\n");
2928 url_fprintf(c->pb, "\r\n");
2932 /* find an rtp connection by using the session ID. Check consistency
2934 static HTTPContext *find_rtp_session_with_url(const char *url,
2935 const char *session_id)
2941 rtp_c = find_rtp_session(session_id);
2945 /* find which url is asked */
2946 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2950 if (strcmp(path, rtp_c->stream->filename) != 0)
2955 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2959 rtp_c = find_rtp_session_with_url(url, h->session_id);
2961 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2965 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2966 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2967 rtp_c->state != HTTPSTATE_READY) {
2968 rtsp_reply_error(c, RTSP_STATUS_STATE);
2972 rtp_c->state = HTTPSTATE_SEND_DATA;
2974 /* now everything is OK, so we can send the connection parameters */
2975 rtsp_reply_header(c, RTSP_STATUS_OK);
2977 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2978 url_fprintf(c->pb, "\r\n");
2981 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
2985 rtp_c = find_rtp_session_with_url(url, h->session_id);
2987 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2991 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2992 rtp_c->state != HTTPSTATE_WAIT_FEED) {
2993 rtsp_reply_error(c, RTSP_STATUS_STATE);
2997 rtp_c->state = HTTPSTATE_READY;
2999 /* now everything is OK, so we can send the connection parameters */
3000 rtsp_reply_header(c, RTSP_STATUS_OK);
3002 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3003 url_fprintf(c->pb, "\r\n");
3006 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3010 rtp_c = find_rtp_session_with_url(url, h->session_id);
3012 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3016 /* abort the session */
3017 close_connection(rtp_c);
3019 if (ff_rtsp_callback) {
3020 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
3022 rtp_c->stream->rtsp_option);
3025 /* now everything is OK, so we can send the connection parameters */
3026 rtsp_reply_header(c, RTSP_STATUS_OK);
3028 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3029 url_fprintf(c->pb, "\r\n");
3033 /********************************************************************/
3036 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3037 FFStream *stream, const char *session_id)
3039 HTTPContext *c = NULL;
3041 /* XXX: should output a warning page when coming
3042 close to the connection limit */
3043 if (nb_connections >= nb_max_connections)
3046 /* add a new connection */
3047 c = av_mallocz(sizeof(HTTPContext));
3052 c->poll_entry = NULL;
3053 c->from_addr = *from_addr;
3054 c->buffer_size = IOBUFFER_INIT_SIZE;
3055 c->buffer = av_malloc(c->buffer_size);
3060 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
3061 c->state = HTTPSTATE_READY;
3062 c->is_packetized = 1;
3063 /* protocol is shown in statistics */
3064 pstrcpy(c->protocol, sizeof(c->protocol), "RTP");
3066 current_bandwidth += stream->bandwidth;
3068 c->next = first_http_ctx;
3080 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3081 command). if dest_addr is NULL, then TCP tunneling in RTSP is
3083 static int rtp_new_av_stream(HTTPContext *c,
3084 int stream_index, struct sockaddr_in *dest_addr)
3086 AVFormatContext *ctx;
3093 /* now we can open the relevant output stream */
3094 ctx = av_mallocz(sizeof(AVFormatContext));
3097 ctx->oformat = &rtp_mux;
3099 st = av_mallocz(sizeof(AVStream));
3102 ctx->nb_streams = 1;
3103 ctx->streams[0] = st;
3105 if (!c->stream->feed ||
3106 c->stream->feed == c->stream) {
3107 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3110 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3115 /* build destination RTP address */
3116 ipaddr = inet_ntoa(dest_addr->sin_addr);
3118 /* XXX: also pass as parameter to function ? */
3119 if (c->stream->is_multicast) {
3121 ttl = c->stream->multicast_ttl;
3124 snprintf(ctx->filename, sizeof(ctx->filename),
3125 "rtp://%s:%d?multicast=1&ttl=%d",
3126 ipaddr, ntohs(dest_addr->sin_port), ttl);
3128 snprintf(ctx->filename, sizeof(ctx->filename),
3129 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3132 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3134 c->rtp_handles[stream_index] = h;
3139 http_log("%s:%d - - [%s] \"RTPSTART %s/streamid=%d\"\n",
3140 ipaddr, ntohs(dest_addr->sin_port),
3142 c->stream->filename, stream_index);
3144 /* normally, no packets should be output here, but the packet size may be checked */
3145 if (url_open_dyn_packet_buf(&ctx->pb,
3146 url_get_max_packet_size(h)) < 0) {
3147 /* XXX: close stream */
3150 av_set_parameters(ctx, NULL);
3151 if (av_write_header(ctx) < 0) {
3158 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3161 c->rtp_ctx[stream_index] = ctx;
3165 /********************************************************************/
3166 /* ffserver initialization */
3168 AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3172 fst = av_mallocz(sizeof(AVStream));
3175 fst->priv_data = av_mallocz(sizeof(FeedData));
3176 memcpy(&fst->codec, codec, sizeof(AVCodecContext));
3177 fst->codec.coded_frame = &dummy_frame;
3178 stream->streams[stream->nb_streams++] = fst;
3182 /* return the stream number in the feed */
3183 int add_av_stream(FFStream *feed,
3187 AVCodecContext *av, *av1;
3191 for(i=0;i<feed->nb_streams;i++) {
3192 st = feed->streams[i];
3194 if (av1->codec_id == av->codec_id &&
3195 av1->codec_type == av->codec_type &&
3196 av1->bit_rate == av->bit_rate) {
3198 switch(av->codec_type) {
3199 case CODEC_TYPE_AUDIO:
3200 if (av1->channels == av->channels &&
3201 av1->sample_rate == av->sample_rate)
3204 case CODEC_TYPE_VIDEO:
3205 if (av1->width == av->width &&
3206 av1->height == av->height &&
3207 av1->frame_rate == av->frame_rate &&
3208 av1->gop_size == av->gop_size)
3217 fst = add_av_stream1(feed, av);
3220 return feed->nb_streams - 1;
3225 void remove_stream(FFStream *stream)
3229 while (*ps != NULL) {
3230 if (*ps == stream) {
3238 /* specific mpeg4 handling : we extract the raw parameters */
3239 void extract_mpeg4_header(AVFormatContext *infile)
3241 int mpeg4_count, i, size;
3247 for(i=0;i<infile->nb_streams;i++) {
3248 st = infile->streams[i];
3249 if (st->codec.codec_id == CODEC_ID_MPEG4 &&
3250 st->codec.extradata == NULL) {
3257 printf("MPEG4 without extra data: trying to find header\n");
3258 while (mpeg4_count > 0) {
3259 if (av_read_packet(infile, &pkt) < 0)
3261 st = infile->streams[pkt.stream_index];
3262 if (st->codec.codec_id == CODEC_ID_MPEG4 &&
3263 st->codec.extradata == NULL) {
3264 /* fill extradata with the header */
3265 /* XXX: we make hard suppositions here ! */
3267 while (p < pkt.data + pkt.size - 4) {
3268 /* stop when vop header is found */
3269 if (p[0] == 0x00 && p[1] == 0x00 &&
3270 p[2] == 0x01 && p[3] == 0xb6) {
3271 size = p - pkt.data;
3272 // av_hex_dump(pkt.data, size);
3273 st->codec.extradata = av_malloc(size);
3274 st->codec.extradata_size = size;
3275 memcpy(st->codec.extradata, pkt.data, size);
3282 av_free_packet(&pkt);
3286 /* compute the needed AVStream for each file */
3287 void build_file_streams(void)
3289 FFStream *stream, *stream_next;
3290 AVFormatContext *infile;
3293 /* gather all streams */
3294 for(stream = first_stream; stream != NULL; stream = stream_next) {
3295 stream_next = stream->next;
3296 if (stream->stream_type == STREAM_TYPE_LIVE &&
3298 /* the stream comes from a file */
3299 /* try to open the file */
3301 if (av_open_input_file(&infile, stream->feed_filename,
3302 NULL, 0, NULL) < 0) {
3303 http_log("%s not found", stream->feed_filename);
3304 /* remove stream (no need to spend more time on it) */
3306 remove_stream(stream);
3308 /* find all the AVStreams inside and reference them in
3310 if (av_find_stream_info(infile) < 0) {
3311 http_log("Could not find codec parameters from '%s'",
3312 stream->feed_filename);
3313 av_close_input_file(infile);
3316 extract_mpeg4_header(infile);
3318 for(i=0;i<infile->nb_streams;i++) {
3319 add_av_stream1(stream, &infile->streams[i]->codec);
3321 av_close_input_file(infile);
3327 /* compute the needed AVStream for each feed */
3328 void build_feed_streams(void)
3330 FFStream *stream, *feed;
3333 /* gather all streams */
3334 for(stream = first_stream; stream != NULL; stream = stream->next) {
3335 feed = stream->feed;
3337 if (!stream->is_feed) {
3338 /* we handle a stream coming from a feed */
3339 for(i=0;i<stream->nb_streams;i++) {
3340 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3346 /* gather all streams */
3347 for(stream = first_stream; stream != NULL; stream = stream->next) {
3348 feed = stream->feed;
3350 if (stream->is_feed) {
3351 for(i=0;i<stream->nb_streams;i++) {
3352 stream->feed_streams[i] = i;
3358 /* create feed files if needed */
3359 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3362 if (url_exist(feed->feed_filename)) {
3363 /* See if it matches */
3367 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3368 /* Now see if it matches */
3369 if (s->nb_streams == feed->nb_streams) {
3371 for(i=0;i<s->nb_streams;i++) {
3373 sf = feed->streams[i];
3376 if (sf->index != ss->index ||
3378 printf("Index & Id do not match for stream %d\n", i);
3381 AVCodecContext *ccf, *ccs;
3385 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3387 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3388 printf("Codecs do not match for stream %d\n", i);
3390 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3391 printf("Codec bitrates do not match for stream %d\n", i);
3393 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3394 if (CHECK_CODEC(frame_rate) ||
3395 CHECK_CODEC(width) ||
3396 CHECK_CODEC(height)) {
3397 printf("Codec width, height and framerate do not match for stream %d\n", i);
3400 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3401 if (CHECK_CODEC(sample_rate) ||
3402 CHECK_CODEC(channels) ||
3403 CHECK_CODEC(frame_size)) {
3404 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3408 printf("Unknown codec type\n");
3417 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3418 feed->feed_filename, s->nb_streams, feed->nb_streams);
3421 av_close_input_file(s);
3423 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3424 feed->feed_filename);
3427 unlink(feed->feed_filename);
3429 if (!url_exist(feed->feed_filename)) {
3430 AVFormatContext s1, *s = &s1;
3432 /* only write the header of the ffm file */
3433 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3434 fprintf(stderr, "Could not open output feed file '%s'\n",
3435 feed->feed_filename);
3438 s->oformat = feed->fmt;
3439 s->nb_streams = feed->nb_streams;
3440 for(i=0;i<s->nb_streams;i++) {
3442 st = feed->streams[i];
3445 av_set_parameters(s, NULL);
3447 /* XXX: need better api */
3448 av_freep(&s->priv_data);
3451 /* get feed size and write index */
3452 fd = open(feed->feed_filename, O_RDONLY);
3454 fprintf(stderr, "Could not open output feed file '%s'\n",
3455 feed->feed_filename);
3459 feed->feed_write_index = ffm_read_write_index(fd);
3460 feed->feed_size = lseek(fd, 0, SEEK_END);
3461 /* ensure that we do not wrap before the end of file */
3462 if (feed->feed_max_size < feed->feed_size)
3463 feed->feed_max_size = feed->feed_size;
3469 /* compute the bandwidth used by each stream */
3470 static void compute_bandwidth(void)
3475 for(stream = first_stream; stream != NULL; stream = stream->next) {
3477 for(i=0;i<stream->nb_streams;i++) {
3478 AVStream *st = stream->streams[i];
3479 switch(st->codec.codec_type) {
3480 case CODEC_TYPE_AUDIO:
3481 case CODEC_TYPE_VIDEO:
3482 bandwidth += st->codec.bit_rate;
3488 stream->bandwidth = (bandwidth + 999) / 1000;
3492 static void get_arg(char *buf, int buf_size, const char **pp)
3499 while (isspace(*p)) p++;
3502 if (*p == '\"' || *p == '\'')
3514 if ((q - buf) < buf_size - 1)
3519 if (quote && *p == quote)
3524 /* add a codec and set the default parameters */
3525 void add_codec(FFStream *stream, AVCodecContext *av)
3529 /* compute default parameters */
3530 switch(av->codec_type) {
3531 case CODEC_TYPE_AUDIO:
3532 if (av->bit_rate == 0)
3533 av->bit_rate = 64000;
3534 if (av->sample_rate == 0)
3535 av->sample_rate = 22050;
3536 if (av->channels == 0)
3539 case CODEC_TYPE_VIDEO:
3540 if (av->bit_rate == 0)
3541 av->bit_rate = 64000;
3542 if (av->frame_rate == 0)
3543 av->frame_rate = 5 * FRAME_RATE_BASE;
3544 if (av->width == 0 || av->height == 0) {
3548 /* Bitrate tolerance is less for streaming */
3549 if (av->bit_rate_tolerance == 0)
3550 av->bit_rate_tolerance = av->bit_rate / 4;
3555 if (av->max_qdiff == 0)
3557 av->qcompress = 0.5;
3561 av->rc_eq = "tex^qComp";
3562 if (!av->i_quant_factor)
3563 av->i_quant_factor = -0.8;
3564 if (!av->b_quant_factor)
3565 av->b_quant_factor = 1.25;
3566 if (!av->b_quant_offset)
3567 av->b_quant_offset = 1.25;
3568 if (!av->rc_min_rate)
3569 av->rc_min_rate = av->bit_rate / 2;
3570 if (!av->rc_max_rate)
3571 av->rc_max_rate = av->bit_rate * 2;
3578 st = av_mallocz(sizeof(AVStream));
3581 stream->streams[stream->nb_streams++] = st;
3582 memcpy(&st->codec, av, sizeof(AVCodecContext));
3585 int opt_audio_codec(const char *arg)
3591 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3596 return CODEC_ID_NONE;
3602 int opt_video_codec(const char *arg)
3608 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3613 return CODEC_ID_NONE;
3619 /* simplistic plugin support */
3621 #ifdef CONFIG_HAVE_DLOPEN
3622 void load_module(const char *filename)
3625 void (*init_func)(void);
3626 dll = dlopen(filename, RTLD_NOW);
3628 fprintf(stderr, "Could not load module '%s' - %s\n",
3629 filename, dlerror());
3633 init_func = dlsym(dll, "ffserver_module_init");
3636 "%s: init function 'ffserver_module_init()' not found\n",
3645 int parse_ffconfig(const char *filename)
3652 int val, errors, line_num;
3653 FFStream **last_stream, *stream, *redirect;
3654 FFStream **last_feed, *feed;
3655 AVCodecContext audio_enc, video_enc;
3656 int audio_id, video_id;
3658 f = fopen(filename, "r");
3666 first_stream = NULL;
3667 last_stream = &first_stream;
3669 last_feed = &first_feed;
3673 audio_id = CODEC_ID_NONE;
3674 video_id = CODEC_ID_NONE;
3676 if (fgets(line, sizeof(line), f) == NULL)
3682 if (*p == '\0' || *p == '#')
3685 get_arg(cmd, sizeof(cmd), &p);
3687 if (!strcasecmp(cmd, "Port")) {
3688 get_arg(arg, sizeof(arg), &p);
3689 my_http_addr.sin_port = htons (atoi(arg));
3690 } else if (!strcasecmp(cmd, "BindAddress")) {
3691 get_arg(arg, sizeof(arg), &p);
3692 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3693 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3694 filename, line_num, arg);
3697 } else if (!strcasecmp(cmd, "NoDaemon")) {
3698 ffserver_daemon = 0;
3699 } else if (!strcasecmp(cmd, "RTSPPort")) {
3700 get_arg(arg, sizeof(arg), &p);
3701 my_rtsp_addr.sin_port = htons (atoi(arg));
3702 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3703 get_arg(arg, sizeof(arg), &p);
3704 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3705 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3706 filename, line_num, arg);
3709 } else if (!strcasecmp(cmd, "MaxClients")) {
3710 get_arg(arg, sizeof(arg), &p);
3712 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3713 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3714 filename, line_num, arg);
3717 nb_max_connections = val;
3719 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3720 get_arg(arg, sizeof(arg), &p);
3722 if (val < 10 || val > 100000) {
3723 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3724 filename, line_num, arg);
3727 max_bandwidth = val;
3729 } else if (!strcasecmp(cmd, "CustomLog")) {
3730 get_arg(logfilename, sizeof(logfilename), &p);
3731 } else if (!strcasecmp(cmd, "<Feed")) {
3732 /*********************************************/
3733 /* Feed related options */
3735 if (stream || feed) {
3736 fprintf(stderr, "%s:%d: Already in a tag\n",
3737 filename, line_num);
3739 feed = av_mallocz(sizeof(FFStream));
3740 /* add in stream list */
3741 *last_stream = feed;
3742 last_stream = &feed->next;
3743 /* add in feed list */
3745 last_feed = &feed->next_feed;
3747 get_arg(feed->filename, sizeof(feed->filename), &p);
3748 q = strrchr(feed->filename, '>');
3751 feed->fmt = guess_format("ffm", NULL, NULL);
3752 /* defaut feed file */
3753 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3754 "/tmp/%s.ffm", feed->filename);
3755 feed->feed_max_size = 5 * 1024 * 1024;
3757 feed->feed = feed; /* self feeding :-) */
3759 } else if (!strcasecmp(cmd, "Launch")) {
3763 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3765 feed->child_argv[0] = av_malloc(7);
3766 strcpy(feed->child_argv[0], "ffmpeg");
3768 for (i = 1; i < 62; i++) {
3771 get_arg(argbuf, sizeof(argbuf), &p);
3775 feed->child_argv[i] = av_malloc(strlen(argbuf + 1));
3776 strcpy(feed->child_argv[i], argbuf);
3779 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3781 snprintf(feed->child_argv[i], 256, "http://127.0.0.1:%d/%s",
3782 ntohs(my_http_addr.sin_port), feed->filename);
3784 } else if (!strcasecmp(cmd, "File")) {
3786 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3787 } else if (stream) {
3788 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3790 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3795 get_arg(arg, sizeof(arg), &p);
3797 fsize = strtod(p1, (char **)&p1);
3798 switch(toupper(*p1)) {
3803 fsize *= 1024 * 1024;
3806 fsize *= 1024 * 1024 * 1024;
3809 feed->feed_max_size = (INT64)fsize;
3811 } else if (!strcasecmp(cmd, "</Feed>")) {
3813 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3814 filename, line_num);
3818 /* Make sure that we start out clean */
3819 if (unlink(feed->feed_filename) < 0
3820 && errno != ENOENT) {
3821 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3822 filename, line_num, feed->feed_filename, strerror(errno));
3828 } else if (!strcasecmp(cmd, "<Stream")) {
3829 /*********************************************/
3830 /* Stream related options */
3832 if (stream || feed) {
3833 fprintf(stderr, "%s:%d: Already in a tag\n",
3834 filename, line_num);
3836 stream = av_mallocz(sizeof(FFStream));
3837 *last_stream = stream;
3838 last_stream = &stream->next;
3840 get_arg(stream->filename, sizeof(stream->filename), &p);
3841 q = strrchr(stream->filename, '>');
3844 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3845 memset(&audio_enc, 0, sizeof(AVCodecContext));
3846 memset(&video_enc, 0, sizeof(AVCodecContext));
3847 audio_id = CODEC_ID_NONE;
3848 video_id = CODEC_ID_NONE;
3850 audio_id = stream->fmt->audio_codec;
3851 video_id = stream->fmt->video_codec;
3854 } else if (!strcasecmp(cmd, "Feed")) {
3855 get_arg(arg, sizeof(arg), &p);
3860 while (sfeed != NULL) {
3861 if (!strcmp(sfeed->filename, arg))
3863 sfeed = sfeed->next_feed;
3866 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3867 filename, line_num, arg);
3869 stream->feed = sfeed;
3872 } else if (!strcasecmp(cmd, "Format")) {
3873 get_arg(arg, sizeof(arg), &p);
3874 if (!strcmp(arg, "status")) {
3875 stream->stream_type = STREAM_TYPE_STATUS;
3878 stream->stream_type = STREAM_TYPE_LIVE;
3879 /* jpeg cannot be used here, so use single frame jpeg */
3880 if (!strcmp(arg, "jpeg"))
3881 strcpy(arg, "singlejpeg");
3882 stream->fmt = guess_stream_format(arg, NULL, NULL);
3884 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3885 filename, line_num, arg);
3890 audio_id = stream->fmt->audio_codec;
3891 video_id = stream->fmt->video_codec;
3893 } else if (!strcasecmp(cmd, "FaviconURL")) {
3894 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3895 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3897 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3898 filename, line_num);
3901 } else if (!strcasecmp(cmd, "Author")) {
3903 get_arg(stream->author, sizeof(stream->author), &p);
3905 } else if (!strcasecmp(cmd, "Comment")) {
3907 get_arg(stream->comment, sizeof(stream->comment), &p);
3909 } else if (!strcasecmp(cmd, "Copyright")) {
3911 get_arg(stream->copyright, sizeof(stream->copyright), &p);
3913 } else if (!strcasecmp(cmd, "Title")) {
3915 get_arg(stream->title, sizeof(stream->title), &p);
3917 } else if (!strcasecmp(cmd, "Preroll")) {
3918 get_arg(arg, sizeof(arg), &p);
3920 stream->prebuffer = atof(arg) * 1000;
3922 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
3924 stream->send_on_key = 1;
3926 } else if (!strcasecmp(cmd, "AudioCodec")) {
3927 get_arg(arg, sizeof(arg), &p);
3928 audio_id = opt_audio_codec(arg);
3929 if (audio_id == CODEC_ID_NONE) {
3930 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
3931 filename, line_num, arg);
3934 } else if (!strcasecmp(cmd, "VideoCodec")) {
3935 get_arg(arg, sizeof(arg), &p);
3936 video_id = opt_video_codec(arg);
3937 if (video_id == CODEC_ID_NONE) {
3938 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
3939 filename, line_num, arg);
3942 } else if (!strcasecmp(cmd, "MaxTime")) {
3943 get_arg(arg, sizeof(arg), &p);
3945 stream->max_time = atof(arg) * 1000;
3947 } else if (!strcasecmp(cmd, "AudioBitRate")) {
3948 get_arg(arg, sizeof(arg), &p);
3950 audio_enc.bit_rate = atoi(arg) * 1000;
3952 } else if (!strcasecmp(cmd, "AudioChannels")) {
3953 get_arg(arg, sizeof(arg), &p);
3955 audio_enc.channels = atoi(arg);
3957 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
3958 get_arg(arg, sizeof(arg), &p);
3960 audio_enc.sample_rate = atoi(arg);
3962 } else if (!strcasecmp(cmd, "AudioQuality")) {
3963 get_arg(arg, sizeof(arg), &p);
3965 // audio_enc.quality = atof(arg) * 1000;
3967 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
3969 int minrate, maxrate;
3971 get_arg(arg, sizeof(arg), &p);
3973 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
3974 video_enc.rc_min_rate = minrate * 1000;
3975 video_enc.rc_max_rate = maxrate * 1000;
3977 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
3978 filename, line_num, arg);
3982 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
3984 get_arg(arg, sizeof(arg), &p);
3985 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
3987 } else if (!strcasecmp(cmd, "VideoBitRate")) {
3988 get_arg(arg, sizeof(arg), &p);
3990 video_enc.bit_rate = atoi(arg) * 1000;
3992 } else if (!strcasecmp(cmd, "VideoSize")) {
3993 get_arg(arg, sizeof(arg), &p);
3995 parse_image_size(&video_enc.width, &video_enc.height, arg);
3996 if ((video_enc.width % 16) != 0 ||
3997 (video_enc.height % 16) != 0) {
3998 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
3999 filename, line_num);
4003 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4004 get_arg(arg, sizeof(arg), &p);
4006 video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
4008 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4009 get_arg(arg, sizeof(arg), &p);
4011 video_enc.gop_size = atoi(arg);
4013 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4015 video_enc.gop_size = 1;
4017 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4019 video_enc.flags |= CODEC_FLAG_HQ;
4021 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4022 get_arg(arg, sizeof(arg), &p);
4024 video_enc.max_qdiff = atoi(arg);
4025 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4026 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4027 filename, line_num);
4031 } else if (!strcasecmp(cmd, "VideoQMax")) {
4032 get_arg(arg, sizeof(arg), &p);
4034 video_enc.qmax = atoi(arg);
4035 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4036 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4037 filename, line_num);
4041 } else if (!strcasecmp(cmd, "VideoQMin")) {
4042 get_arg(arg, sizeof(arg), &p);
4044 video_enc.qmin = atoi(arg);
4045 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4046 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4047 filename, line_num);
4051 } else if (!strcasecmp(cmd, "LumaElim")) {
4052 get_arg(arg, sizeof(arg), &p);
4054 video_enc.luma_elim_threshold = atoi(arg);
4056 } else if (!strcasecmp(cmd, "ChromaElim")) {
4057 get_arg(arg, sizeof(arg), &p);
4059 video_enc.chroma_elim_threshold = atoi(arg);
4061 } else if (!strcasecmp(cmd, "LumiMask")) {
4062 get_arg(arg, sizeof(arg), &p);
4064 video_enc.lumi_masking = atof(arg);
4066 } else if (!strcasecmp(cmd, "DarkMask")) {
4067 get_arg(arg, sizeof(arg), &p);
4069 video_enc.dark_masking = atof(arg);
4071 } else if (!strcasecmp(cmd, "NoVideo")) {
4072 video_id = CODEC_ID_NONE;
4073 } else if (!strcasecmp(cmd, "NoAudio")) {
4074 audio_id = CODEC_ID_NONE;
4075 } else if (!strcasecmp(cmd, "ACL")) {
4079 get_arg(arg, sizeof(arg), &p);
4080 if (strcasecmp(arg, "allow") == 0) {
4081 acl.action = IP_ALLOW;
4082 } else if (strcasecmp(arg, "deny") == 0) {
4083 acl.action = IP_DENY;
4085 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4086 filename, line_num, arg);
4090 get_arg(arg, sizeof(arg), &p);
4092 he = gethostbyname(arg);
4094 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4095 filename, line_num, arg);
4098 /* Only take the first */
4099 acl.first = *(struct in_addr *) he->h_addr_list[0];
4100 acl.last = acl.first;
4103 get_arg(arg, sizeof(arg), &p);
4106 he = gethostbyname(arg);
4108 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4109 filename, line_num, arg);
4112 /* Only take the first */
4113 acl.last = *(struct in_addr *) he->h_addr_list[0];
4118 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4119 IPAddressACL **naclp = 0;
4125 naclp = &stream->acl;
4129 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4130 filename, line_num);
4136 naclp = &(*naclp)->next;
4141 } else if (!strcasecmp(cmd, "RTSPOption")) {
4142 get_arg(arg, sizeof(arg), &p);
4144 av_freep(&stream->rtsp_option);
4145 /* XXX: av_strdup ? */
4146 stream->rtsp_option = av_malloc(strlen(arg) + 1);
4147 if (stream->rtsp_option) {
4148 strcpy(stream->rtsp_option, arg);
4151 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4152 get_arg(arg, sizeof(arg), &p);
4154 if (!inet_aton(arg, &stream->multicast_ip)) {
4155 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
4156 filename, line_num, arg);
4159 stream->is_multicast = 1;
4160 stream->loop = 1; /* default is looping */
4162 } else if (!strcasecmp(cmd, "MulticastPort")) {
4163 get_arg(arg, sizeof(arg), &p);
4165 stream->multicast_port = atoi(arg);
4167 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4168 get_arg(arg, sizeof(arg), &p);
4170 stream->multicast_ttl = atoi(arg);
4172 } else if (!strcasecmp(cmd, "NoLoop")) {
4176 } else if (!strcasecmp(cmd, "</Stream>")) {
4178 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4179 filename, line_num);
4182 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4183 if (audio_id != CODEC_ID_NONE) {
4184 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4185 audio_enc.codec_id = audio_id;
4186 add_codec(stream, &audio_enc);
4188 if (video_id != CODEC_ID_NONE) {
4189 video_enc.codec_type = CODEC_TYPE_VIDEO;
4190 video_enc.codec_id = video_id;
4191 add_codec(stream, &video_enc);
4195 } else if (!strcasecmp(cmd, "<Redirect")) {
4196 /*********************************************/
4198 if (stream || feed || redirect) {
4199 fprintf(stderr, "%s:%d: Already in a tag\n",
4200 filename, line_num);
4203 redirect = av_mallocz(sizeof(FFStream));
4204 *last_stream = redirect;
4205 last_stream = &redirect->next;
4207 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4208 q = strrchr(redirect->filename, '>');
4211 redirect->stream_type = STREAM_TYPE_REDIRECT;
4213 } else if (!strcasecmp(cmd, "URL")) {
4215 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4217 } else if (!strcasecmp(cmd, "</Redirect>")) {
4219 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4220 filename, line_num);
4223 if (!redirect->feed_filename[0]) {
4224 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4225 filename, line_num);
4229 } else if (!strcasecmp(cmd, "LoadModule")) {
4230 get_arg(arg, sizeof(arg), &p);
4231 #ifdef CONFIG_HAVE_DLOPEN
4234 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4235 filename, line_num, arg);
4239 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4240 filename, line_num, cmd);
4254 static void write_packet(FFCodec *ffenc,
4255 UINT8 *buf, int size)
4258 AVCodecContext *enc = &ffenc->enc;
4260 mk_header(&hdr, enc, size);
4261 wptr = http_fifo.wptr;
4262 fifo_write(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &wptr);
4263 fifo_write(&http_fifo, buf, size, &wptr);
4264 /* atomic modification of wptr */
4265 http_fifo.wptr = wptr;
4266 ffenc->data_count += size;
4267 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4273 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4274 "usage: ffserver [-L] [-h] [-f configfile]\n"
4275 "Hyper fast multi format Audio/Video streaming server\n"
4277 "-L : print the LICENCE\n"
4279 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4286 "ffserver version " FFMPEG_VERSION "\n"
4287 "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4288 "This library is free software; you can redistribute it and/or\n"
4289 "modify it under the terms of the GNU Lesser General Public\n"
4290 "License as published by the Free Software Foundation; either\n"
4291 "version 2 of the License, or (at your option) any later version.\n"
4293 "This library is distributed in the hope that it will be useful,\n"
4294 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4295 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4296 "Lesser General Public License for more details.\n"
4298 "You should have received a copy of the GNU Lesser General Public\n"
4299 "License along with this library; if not, write to the Free Software\n"
4300 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
4304 static void handle_child_exit(int sig)
4309 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4312 for (feed = first_feed; feed; feed = feed->next) {
4313 if (feed->pid == pid) {
4314 int uptime = time(0) - feed->pid_start;
4317 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4320 /* Turn off any more restarts */
4321 feed->child_argv = 0;
4327 need_to_start_children = 1;
4330 int main(int argc, char **argv)
4332 const char *config_filename;
4334 struct sigaction sigact;
4338 config_filename = "/etc/ffserver.conf";
4340 my_program_name = argv[0];
4341 my_program_dir = getcwd(0, 0);
4342 ffserver_daemon = 1;
4345 c = getopt(argc, argv, "ndLh?f:");
4361 ffserver_daemon = 0;
4364 config_filename = optarg;
4371 putenv("http_proxy"); /* Kill the http_proxy */
4373 srandom(gettime_ms() + (getpid() << 16));
4375 /* address on which the server will handle HTTP connections */
4376 my_http_addr.sin_family = AF_INET;
4377 my_http_addr.sin_port = htons (8080);
4378 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4380 /* address on which the server will handle RTSP connections */
4381 my_rtsp_addr.sin_family = AF_INET;
4382 my_rtsp_addr.sin_port = htons (5454);
4383 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4385 nb_max_connections = 5;
4386 max_bandwidth = 1000;
4387 first_stream = NULL;
4388 logfilename[0] = '\0';
4390 memset(&sigact, 0, sizeof(sigact));
4391 sigact.sa_handler = handle_child_exit;
4392 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4393 sigaction(SIGCHLD, &sigact, 0);
4395 if (parse_ffconfig(config_filename) < 0) {
4396 fprintf(stderr, "Incorrect config file - exiting.\n");
4400 build_file_streams();
4402 build_feed_streams();
4404 compute_bandwidth();
4406 /* put the process in background and detach it from its TTY */
4407 if (ffserver_daemon) {
4414 } else if (pid > 0) {
4422 open("/dev/null", O_RDWR);
4423 if (strcmp(logfilename, "-") != 0) {
4433 signal(SIGPIPE, SIG_IGN);
4435 /* open log file if needed */
4436 if (logfilename[0] != '\0') {
4437 if (!strcmp(logfilename, "-"))
4440 logfile = fopen(logfilename, "w");
4443 if (http_server() < 0) {
4444 fprintf(stderr, "Could not start server\n");