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)
93 int64_t count1, count2;
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_t *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_t 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_t *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 /* These are in host order */
169 struct in_addr first;
173 /* description of each stream of the ffserver.conf file */
174 typedef struct FFStream {
175 enum StreamType stream_type;
176 char filename[1024]; /* stream filename */
177 struct FFStream *feed; /* feed we are using (can be null if
182 int prebuffer; /* Number of millseconds early to start */
183 long max_time; /* Number of milliseconds to run */
185 AVStream *streams[MAX_STREAMS];
186 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
187 char feed_filename[1024]; /* file name of the feed storage, or
188 input file name for a stream */
193 pid_t pid; /* Of ffmpeg process */
194 time_t pid_start; /* Of ffmpeg process */
196 struct FFStream *next;
197 int bandwidth; /* bandwidth, in kbits/s */
200 /* multicast specific */
202 struct in_addr multicast_ip;
203 int multicast_port; /* first port used for multicast */
205 int loop; /* if true, send the stream in loops (only meaningful if file) */
208 int feed_opened; /* true if someone is writing to the feed */
209 int is_feed; /* true if it is a feed */
211 int64_t bytes_served;
212 int64_t feed_max_size; /* maximum storage size */
213 int64_t feed_write_index; /* current write position in feed (it wraps round) */
214 int64_t feed_size; /* current size of feed */
215 struct FFStream *next_feed;
218 typedef struct FeedData {
219 long long data_count;
220 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
223 struct sockaddr_in my_http_addr;
224 struct sockaddr_in my_rtsp_addr;
226 char logfilename[1024];
227 HTTPContext *first_http_ctx;
228 FFStream *first_feed; /* contains only feeds */
229 FFStream *first_stream; /* contains all streams, including feeds */
231 static void new_connection(int server_fd, int is_rtsp);
232 static void close_connection(HTTPContext *c);
235 static int handle_connection(HTTPContext *c);
236 static int http_parse_request(HTTPContext *c);
237 static int http_send_data(HTTPContext *c);
238 static void compute_stats(HTTPContext *c);
239 static int open_input_stream(HTTPContext *c, const char *info);
240 static int http_start_receive_data(HTTPContext *c);
241 static int http_receive_data(HTTPContext *c);
242 static int compute_send_delay(HTTPContext *c);
245 static int rtsp_parse_request(HTTPContext *c);
246 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
247 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
248 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
249 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
250 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
253 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
254 struct in_addr my_ip);
257 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
258 FFStream *stream, const char *session_id);
259 static int rtp_new_av_stream(HTTPContext *c,
260 int stream_index, struct sockaddr_in *dest_addr);
262 static const char *my_program_name;
263 static const char *my_program_dir;
265 static int ffserver_debug;
266 static int ffserver_daemon;
267 static int no_launch;
268 static int need_to_start_children;
270 int nb_max_connections;
274 int current_bandwidth;
276 static long cur_time; // Making this global saves on passing it around everywhere
278 static long gettime_ms(void)
282 gettimeofday(&tv,NULL);
283 return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
286 static FILE *logfile = NULL;
288 static void http_log(const char *fmt, ...)
294 vfprintf(logfile, fmt, ap);
300 static char *ctime1(char *buf2)
308 p = buf2 + strlen(p) - 1;
314 static void log_connection(HTTPContext *c)
321 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
322 inet_ntoa(c->from_addr.sin_addr),
323 ctime1(buf2), c->method, c->url,
324 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
327 static void update_datarate(DataRateData *drd, int64_t count)
329 if (!drd->time1 && !drd->count1) {
330 drd->time1 = drd->time2 = cur_time;
331 drd->count1 = drd->count2 = count;
333 if (cur_time - drd->time2 > 5000) {
334 drd->time1 = drd->time2;
335 drd->count1 = drd->count2;
336 drd->time2 = cur_time;
342 /* In bytes per second */
343 static int compute_datarate(DataRateData *drd, int64_t count)
345 if (cur_time == drd->time1)
348 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
351 static int get_longterm_datarate(DataRateData *drd, int64_t count)
353 /* You get the first 3 seconds flat out */
354 if (cur_time - drd->time1 < 3000)
356 return compute_datarate(drd, count);
360 static void start_children(FFStream *feed)
365 for (; feed; feed = feed->next) {
366 if (feed->child_argv && !feed->pid) {
367 feed->pid_start = time(0);
372 fprintf(stderr, "Unable to create children\n");
381 for (i = 3; i < 256; i++) {
385 if (!ffserver_debug) {
386 i = open("/dev/null", O_RDWR);
395 pstrcpy(pathname, sizeof(pathname), my_program_name);
397 slash = strrchr(pathname, '/');
403 strcpy(slash, "ffmpeg");
405 /* This is needed to make relative pathnames work */
406 chdir(my_program_dir);
408 signal(SIGPIPE, SIG_DFL);
410 execvp(pathname, feed->child_argv);
418 /* open a listening socket */
419 static int socket_open_listen(struct sockaddr_in *my_addr)
423 server_fd = socket(AF_INET,SOCK_STREAM,0);
430 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
432 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
434 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
440 if (listen (server_fd, 5) < 0) {
445 fcntl(server_fd, F_SETFL, O_NONBLOCK);
450 /* start all multicast streams */
451 static void start_multicast(void)
456 struct sockaddr_in dest_addr;
457 int default_port, stream_index;
460 for(stream = first_stream; stream != NULL; stream = stream->next) {
461 if (stream->is_multicast) {
462 /* open the RTP connection */
463 snprintf(session_id, sizeof(session_id),
464 "%08x%08x", (int)random(), (int)random());
466 /* choose a port if none given */
467 if (stream->multicast_port == 0) {
468 stream->multicast_port = default_port;
472 dest_addr.sin_family = AF_INET;
473 dest_addr.sin_addr = stream->multicast_ip;
474 dest_addr.sin_port = htons(stream->multicast_port);
476 rtp_c = rtp_new_connection(&dest_addr, stream, session_id);
480 if (open_input_stream(rtp_c, "") < 0) {
481 fprintf(stderr, "Could not open input stream for stream '%s'\n",
486 rtp_c->rtp_protocol = RTSP_PROTOCOL_RTP_UDP_MULTICAST;
488 /* open each RTP stream */
489 for(stream_index = 0; stream_index < stream->nb_streams;
491 dest_addr.sin_port = htons(stream->multicast_port +
493 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
494 fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
495 stream->filename, stream_index);
500 /* change state to send data */
501 rtp_c->state = HTTPSTATE_SEND_DATA;
506 /* main loop of the http server */
507 static int http_server(void)
509 int server_fd, ret, rtsp_server_fd, delay, delay1;
510 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
511 HTTPContext *c, *c_next;
513 server_fd = socket_open_listen(&my_http_addr);
517 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
518 if (rtsp_server_fd < 0)
521 http_log("ffserver started.\n");
523 start_children(first_feed);
525 first_http_ctx = NULL;
527 first_http_ctx = NULL;
532 poll_entry = poll_table;
533 poll_entry->fd = server_fd;
534 poll_entry->events = POLLIN;
537 poll_entry->fd = rtsp_server_fd;
538 poll_entry->events = POLLIN;
541 /* wait for events on each HTTP handle */
548 case HTTPSTATE_SEND_HEADER:
549 case RTSPSTATE_SEND_REPLY:
550 c->poll_entry = poll_entry;
552 poll_entry->events = POLLOUT;
555 case HTTPSTATE_SEND_DATA_HEADER:
556 case HTTPSTATE_SEND_DATA:
557 case HTTPSTATE_SEND_DATA_TRAILER:
558 if (!c->is_packetized) {
559 /* for TCP, we output as much as we can (may need to put a limit) */
560 c->poll_entry = poll_entry;
562 poll_entry->events = POLLOUT;
565 /* not strictly correct, but currently cannot add
566 more than one fd in poll entry */
570 case HTTPSTATE_WAIT_REQUEST:
571 case HTTPSTATE_RECEIVE_DATA:
572 case HTTPSTATE_WAIT_FEED:
573 case RTSPSTATE_WAIT_REQUEST:
574 /* need to catch errors */
575 c->poll_entry = poll_entry;
577 poll_entry->events = POLLIN;/* Maybe this will work */
581 c->poll_entry = NULL;
582 delay1 = compute_send_delay(c);
586 case HTTPSTATE_WAIT_SHORT:
587 c->poll_entry = NULL;
588 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
593 c->poll_entry = NULL;
599 /* wait for an event on one connection. We poll at least every
600 second to handle timeouts */
602 ret = poll(poll_table, poll_entry - poll_table, delay);
605 cur_time = gettime_ms();
607 if (need_to_start_children) {
608 need_to_start_children = 0;
609 start_children(first_feed);
612 /* now handle the events */
613 for(c = first_http_ctx; c != NULL; c = c_next) {
615 if (handle_connection(c) < 0) {
616 /* close and free the connection */
622 poll_entry = poll_table;
623 /* new HTTP connection request ? */
624 if (poll_entry->revents & POLLIN) {
625 new_connection(server_fd, 0);
628 /* new RTSP connection request ? */
629 if (poll_entry->revents & POLLIN) {
630 new_connection(rtsp_server_fd, 1);
635 /* start waiting for a new HTTP/RTSP request */
636 static void start_wait_request(HTTPContext *c, int is_rtsp)
638 c->buffer_ptr = c->buffer;
639 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
642 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
643 c->state = RTSPSTATE_WAIT_REQUEST;
645 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
646 c->state = HTTPSTATE_WAIT_REQUEST;
650 static void new_connection(int server_fd, int is_rtsp)
652 struct sockaddr_in from_addr;
654 HTTPContext *c = NULL;
656 len = sizeof(from_addr);
657 fd = accept(server_fd, (struct sockaddr *)&from_addr,
661 fcntl(fd, F_SETFL, O_NONBLOCK);
663 /* XXX: should output a warning page when coming
664 close to the connection limit */
665 if (nb_connections >= nb_max_connections)
668 /* add a new connection */
669 c = av_mallocz(sizeof(HTTPContext));
673 c->next = first_http_ctx;
676 c->poll_entry = NULL;
677 c->from_addr = from_addr;
678 c->buffer_size = IOBUFFER_INIT_SIZE;
679 c->buffer = av_malloc(c->buffer_size);
684 start_wait_request(c, is_rtsp);
696 static void close_connection(HTTPContext *c)
698 HTTPContext **cp, *c1;
700 AVFormatContext *ctx;
704 /* remove connection from list */
705 cp = &first_http_ctx;
706 while ((*cp) != NULL) {
715 /* remove connection associated resources */
719 /* close each frame parser */
720 for(i=0;i<c->fmt_in->nb_streams;i++) {
721 st = c->fmt_in->streams[i];
722 if (st->codec.codec) {
723 avcodec_close(&st->codec);
726 av_close_input_file(c->fmt_in);
729 /* free RTP output streams if any */
732 nb_streams = c->stream->nb_streams;
734 for(i=0;i<nb_streams;i++) {
737 av_write_trailer(ctx);
740 h = c->rtp_handles[i];
746 if (!c->last_packet_sent) {
750 if (url_open_dyn_buf(&ctx->pb) >= 0) {
751 av_write_trailer(ctx);
752 (void) url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
758 current_bandwidth -= c->stream->bandwidth;
759 av_freep(&c->pb_buffer);
765 static int handle_connection(HTTPContext *c)
770 case HTTPSTATE_WAIT_REQUEST:
771 case RTSPSTATE_WAIT_REQUEST:
773 if ((c->timeout - cur_time) < 0)
775 if (c->poll_entry->revents & (POLLERR | POLLHUP))
778 /* no need to read if no events */
779 if (!(c->poll_entry->revents & POLLIN))
782 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
784 if (errno != EAGAIN && errno != EINTR)
786 } else if (len == 0) {
789 /* search for end of request. XXX: not fully correct since garbage could come after the end */
791 c->buffer_ptr += len;
793 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
794 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
795 /* request found : parse it and reply */
796 if (c->state == HTTPSTATE_WAIT_REQUEST) {
797 ret = http_parse_request(c);
799 ret = rtsp_parse_request(c);
803 } else if (ptr >= c->buffer_end) {
804 /* request too long: cannot do anything */
810 case HTTPSTATE_SEND_HEADER:
811 if (c->poll_entry->revents & (POLLERR | POLLHUP))
814 /* no need to write if no events */
815 if (!(c->poll_entry->revents & POLLOUT))
817 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
819 if (errno != EAGAIN && errno != EINTR) {
820 /* error : close connection */
821 av_freep(&c->pb_buffer);
825 c->buffer_ptr += len;
827 c->stream->bytes_served += len;
828 c->data_count += len;
829 if (c->buffer_ptr >= c->buffer_end) {
830 av_freep(&c->pb_buffer);
835 /* all the buffer was sent : synchronize to the incoming stream */
836 c->state = HTTPSTATE_SEND_DATA_HEADER;
837 c->buffer_ptr = c->buffer_end = c->buffer;
842 case HTTPSTATE_SEND_DATA:
843 case HTTPSTATE_SEND_DATA_HEADER:
844 case HTTPSTATE_SEND_DATA_TRAILER:
845 /* for packetized output, we consider we can always write (the
846 input streams sets the speed). It may be better to verify
847 that we do not rely too much on the kernel queues */
848 if (!c->is_packetized) {
849 if (c->poll_entry->revents & (POLLERR | POLLHUP))
852 /* no need to read if no events */
853 if (!(c->poll_entry->revents & POLLOUT))
856 if (http_send_data(c) < 0)
859 case HTTPSTATE_RECEIVE_DATA:
860 /* no need to read if no events */
861 if (c->poll_entry->revents & (POLLERR | POLLHUP))
863 if (!(c->poll_entry->revents & POLLIN))
865 if (http_receive_data(c) < 0)
868 case HTTPSTATE_WAIT_FEED:
869 /* no need to read if no events */
870 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
873 /* nothing to do, we'll be waken up by incoming feed packets */
877 /* if the delay expired, we can send new packets */
878 if (compute_send_delay(c) <= 0)
879 c->state = HTTPSTATE_SEND_DATA;
881 case HTTPSTATE_WAIT_SHORT:
882 /* just return back to send data */
883 c->state = HTTPSTATE_SEND_DATA;
886 case RTSPSTATE_SEND_REPLY:
887 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
888 av_freep(&c->pb_buffer);
891 /* no need to write if no events */
892 if (!(c->poll_entry->revents & POLLOUT))
894 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
896 if (errno != EAGAIN && errno != EINTR) {
897 /* error : close connection */
898 av_freep(&c->pb_buffer);
902 c->buffer_ptr += len;
903 c->data_count += len;
904 if (c->buffer_ptr >= c->buffer_end) {
905 /* all the buffer was sent : wait for a new request */
906 av_freep(&c->pb_buffer);
907 start_wait_request(c, 1);
911 case HTTPSTATE_READY:
920 static int extract_rates(char *rates, int ratelen, const char *request)
924 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
925 if (strncasecmp(p, "Pragma:", 7) == 0) {
926 const char *q = p + 7;
928 while (*q && *q != '\n' && isspace(*q))
931 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
937 memset(rates, 0xff, ratelen);
940 while (*q && *q != '\n' && *q != ':')
943 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
947 if (stream_no < ratelen && stream_no >= 0) {
948 rates[stream_no] = rate_no;
951 while (*q && *q != '\n' && !isspace(*q))
968 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
971 int best_bitrate = 100000000;
974 for (i = 0; i < feed->nb_streams; i++) {
975 AVCodecContext *feed_codec = &feed->streams[i]->codec;
977 if (feed_codec->codec_id != codec->codec_id ||
978 feed_codec->sample_rate != codec->sample_rate ||
979 feed_codec->width != codec->width ||
980 feed_codec->height != codec->height) {
984 /* Potential stream */
986 /* We want the fastest stream less than bit_rate, or the slowest
987 * faster than bit_rate
990 if (feed_codec->bit_rate <= bit_rate) {
991 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
992 best_bitrate = feed_codec->bit_rate;
996 if (feed_codec->bit_rate < best_bitrate) {
997 best_bitrate = feed_codec->bit_rate;
1006 static int modify_current_stream(HTTPContext *c, char *rates)
1009 FFStream *req = c->stream;
1010 int action_required = 0;
1012 /* Not much we can do for a feed */
1016 for (i = 0; i < req->nb_streams; i++) {
1017 AVCodecContext *codec = &req->streams[i]->codec;
1021 c->switch_feed_streams[i] = req->feed_streams[i];
1024 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1027 /* Wants off or slow */
1028 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1030 /* This doesn't work well when it turns off the only stream! */
1031 c->switch_feed_streams[i] = -2;
1032 c->feed_streams[i] = -2;
1037 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1038 action_required = 1;
1041 return action_required;
1045 static void do_switch_stream(HTTPContext *c, int i)
1047 if (c->switch_feed_streams[i] >= 0) {
1049 c->feed_streams[i] = c->switch_feed_streams[i];
1052 /* Now update the stream */
1054 c->switch_feed_streams[i] = -1;
1057 /* XXX: factorize in utils.c ? */
1058 /* XXX: take care with different space meaning */
1059 static void skip_spaces(const char **pp)
1063 while (*p == ' ' || *p == '\t')
1068 static void get_word(char *buf, int buf_size, const char **pp)
1076 while (!isspace(*p) && *p != '\0') {
1077 if ((q - buf) < buf_size - 1)
1086 static int validate_acl(FFStream *stream, HTTPContext *c)
1088 enum IPAddressAction last_action = IP_DENY;
1090 struct in_addr *src = &c->from_addr.sin_addr;
1091 unsigned long src_addr = ntohl(src->s_addr);
1093 for (acl = stream->acl; acl; acl = acl->next) {
1094 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr) {
1095 return (acl->action == IP_ALLOW) ? 1 : 0;
1097 last_action = acl->action;
1100 /* Nothing matched, so return not the last action */
1101 return (last_action == IP_DENY) ? 1 : 0;
1104 /* compute the real filename of a file by matching it without its
1105 extensions to all the stream filenames */
1106 static void compute_real_filename(char *filename, int max_size)
1113 /* compute filename by matching without the file extensions */
1114 pstrcpy(file1, sizeof(file1), filename);
1115 p = strrchr(file1, '.');
1118 for(stream = first_stream; stream != NULL; stream = stream->next) {
1119 pstrcpy(file2, sizeof(file2), stream->filename);
1120 p = strrchr(file2, '.');
1123 if (!strcmp(file1, file2)) {
1124 pstrcpy(filename, max_size, stream->filename);
1139 /* parse http request and prepare header */
1140 static int http_parse_request(HTTPContext *c)
1144 enum RedirType redir_type;
1146 char info[1024], *filename;
1150 const char *mime_type;
1154 char *useragent = 0;
1157 get_word(cmd, sizeof(cmd), (const char **)&p);
1158 pstrcpy(c->method, sizeof(c->method), cmd);
1160 if (!strcmp(cmd, "GET"))
1162 else if (!strcmp(cmd, "POST"))
1167 get_word(url, sizeof(url), (const char **)&p);
1168 pstrcpy(c->url, sizeof(c->url), url);
1170 get_word(protocol, sizeof(protocol), (const char **)&p);
1171 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1174 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
1176 /* find the filename and the optional info string in the request */
1183 pstrcpy(info, sizeof(info), p);
1189 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1190 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1192 if (*useragent && *useragent != '\n' && isspace(*useragent))
1196 p = strchr(p, '\n');
1203 redir_type = REDIR_NONE;
1204 if (match_ext(filename, "asx")) {
1205 redir_type = REDIR_ASX;
1206 filename[strlen(filename)-1] = 'f';
1207 } else if (match_ext(filename, "asf") &&
1208 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1209 /* if this isn't WMP or lookalike, return the redirector file */
1210 redir_type = REDIR_ASF;
1211 } else if (match_ext(filename, "rpm,ram")) {
1212 redir_type = REDIR_RAM;
1213 strcpy(filename + strlen(filename)-2, "m");
1214 } else if (match_ext(filename, "rtsp")) {
1215 redir_type = REDIR_RTSP;
1216 compute_real_filename(filename, sizeof(url) - 1);
1217 } else if (match_ext(filename, "sdp")) {
1218 redir_type = REDIR_SDP;
1219 compute_real_filename(filename, sizeof(url) - 1);
1222 stream = first_stream;
1223 while (stream != NULL) {
1224 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1226 stream = stream->next;
1228 if (stream == NULL) {
1229 sprintf(msg, "File '%s' not found", url);
1234 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1235 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1237 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1238 c->http_error = 301;
1240 q += sprintf(q, "HTTP/1.0 301 Moved\r\n");
1241 q += sprintf(q, "Location: %s\r\n", stream->feed_filename);
1242 q += sprintf(q, "Content-type: text/html\r\n");
1243 q += sprintf(q, "\r\n");
1244 q += sprintf(q, "<html><head><title>Moved</title></head><body>\r\n");
1245 q += sprintf(q, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1246 q += sprintf(q, "</body></html>\r\n");
1248 /* prepare output buffer */
1249 c->buffer_ptr = c->buffer;
1251 c->state = HTTPSTATE_SEND_HEADER;
1255 /* If this is WMP, get the rate information */
1256 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1257 if (modify_current_stream(c, ratebuf)) {
1258 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1259 if (c->switch_feed_streams[i] >= 0)
1260 do_switch_stream(c, i);
1265 if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1266 current_bandwidth += stream->bandwidth;
1269 if (post == 0 && max_bandwidth < current_bandwidth) {
1270 c->http_error = 200;
1272 q += sprintf(q, "HTTP/1.0 200 Server too busy\r\n");
1273 q += sprintf(q, "Content-type: text/html\r\n");
1274 q += sprintf(q, "\r\n");
1275 q += sprintf(q, "<html><head><title>Too busy</title></head><body>\r\n");
1276 q += sprintf(q, "The server is too busy to serve your request at this time.<p>\r\n");
1277 q += sprintf(q, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
1278 current_bandwidth, max_bandwidth);
1279 q += sprintf(q, "</body></html>\r\n");
1281 /* prepare output buffer */
1282 c->buffer_ptr = c->buffer;
1284 c->state = HTTPSTATE_SEND_HEADER;
1288 if (redir_type != REDIR_NONE) {
1291 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1292 if (strncasecmp(p, "Host:", 5) == 0) {
1296 p = strchr(p, '\n');
1307 while (isspace(*hostinfo))
1310 eoh = strchr(hostinfo, '\n');
1312 if (eoh[-1] == '\r')
1315 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1316 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1317 hostbuf[eoh - hostinfo] = 0;
1319 c->http_error = 200;
1321 switch(redir_type) {
1323 q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n");
1324 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1325 q += sprintf(q, "\r\n");
1326 q += sprintf(q, "<ASX Version=\"3\">\r\n");
1327 q += sprintf(q, "<!-- Autogenerated by ffserver -->\r\n");
1328 q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1329 hostbuf, filename, info);
1330 q += sprintf(q, "</ASX>\r\n");
1333 q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n");
1334 q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n");
1335 q += sprintf(q, "\r\n");
1336 q += sprintf(q, "# Autogenerated by ffserver\r\n");
1337 q += sprintf(q, "http://%s/%s%s\r\n",
1338 hostbuf, filename, info);
1341 q += sprintf(q, "HTTP/1.0 200 ASF Redirect follows\r\n");
1342 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1343 q += sprintf(q, "\r\n");
1344 q += sprintf(q, "[Reference]\r\n");
1345 q += sprintf(q, "Ref1=http://%s/%s%s\r\n",
1346 hostbuf, filename, info);
1350 char hostname[256], *p;
1351 /* extract only hostname */
1352 pstrcpy(hostname, sizeof(hostname), hostbuf);
1353 p = strrchr(hostname, ':');
1356 q += sprintf(q, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1357 /* XXX: incorrect mime type ? */
1358 q += sprintf(q, "Content-type: application/x-rtsp\r\n");
1359 q += sprintf(q, "\r\n");
1360 q += sprintf(q, "rtsp://%s:%d/%s\r\n",
1361 hostname, ntohs(my_rtsp_addr.sin_port),
1368 int sdp_data_size, len;
1369 struct sockaddr_in my_addr;
1371 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1372 q += sprintf(q, "Content-type: application/sdp\r\n");
1373 q += sprintf(q, "\r\n");
1375 len = sizeof(my_addr);
1376 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1378 /* XXX: should use a dynamic buffer */
1379 sdp_data_size = prepare_sdp_description(stream,
1382 if (sdp_data_size > 0) {
1383 memcpy(q, sdp_data, sdp_data_size);
1395 /* prepare output buffer */
1396 c->buffer_ptr = c->buffer;
1398 c->state = HTTPSTATE_SEND_HEADER;
1404 sprintf(msg, "ASX/RAM file not handled");
1408 stream->conns_served++;
1410 /* XXX: add there authenticate and IP match */
1413 /* if post, it means a feed is being sent */
1414 if (!stream->is_feed) {
1415 /* However it might be a status report from WMP! Lets log the data
1416 * as it might come in handy one day
1421 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1422 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1426 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1427 client_id = strtol(p + 18, 0, 10);
1429 p = strchr(p, '\n');
1437 char *eol = strchr(logline, '\n');
1442 if (eol[-1] == '\r')
1444 http_log("%.*s\n", eol - logline, logline);
1445 c->suppress_log = 1;
1450 http_log("\nGot request:\n%s\n", c->buffer);
1453 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1456 /* Now we have to find the client_id */
1457 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1458 if (wmpc->wmp_client_id == client_id)
1463 if (modify_current_stream(wmpc, ratebuf)) {
1464 wmpc->switch_pending = 1;
1469 sprintf(msg, "POST command not handled");
1473 if (http_start_receive_data(c) < 0) {
1474 sprintf(msg, "could not open feed");
1478 c->state = HTTPSTATE_RECEIVE_DATA;
1483 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1484 http_log("\nGot request:\n%s\n", c->buffer);
1488 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1491 /* open input stream */
1492 if (open_input_stream(c, info) < 0) {
1493 sprintf(msg, "Input stream corresponding to '%s' not found", url);
1497 /* prepare http header */
1499 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1500 mime_type = c->stream->fmt->mime_type;
1502 mime_type = "application/x-octet_stream";
1503 q += sprintf(q, "Pragma: no-cache\r\n");
1505 /* for asf, we need extra headers */
1506 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1507 /* Need to allocate a client id */
1509 c->wmp_client_id = random() & 0x7fffffff;
1511 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);
1513 q += sprintf(q, "Content-Type: %s\r\n", mime_type);
1514 q += sprintf(q, "\r\n");
1516 /* prepare output buffer */
1518 c->buffer_ptr = c->buffer;
1520 c->state = HTTPSTATE_SEND_HEADER;
1523 c->http_error = 404;
1525 q += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
1526 q += sprintf(q, "Content-type: %s\r\n", "text/html");
1527 q += sprintf(q, "\r\n");
1528 q += sprintf(q, "<HTML>\n");
1529 q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1530 q += sprintf(q, "<BODY>%s</BODY>\n", msg);
1531 q += sprintf(q, "</HTML>\n");
1533 /* prepare output buffer */
1534 c->buffer_ptr = c->buffer;
1536 c->state = HTTPSTATE_SEND_HEADER;
1540 c->http_error = 200; /* horrible : we use this value to avoid
1541 going to the send data state */
1542 c->state = HTTPSTATE_SEND_HEADER;
1546 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1548 static const char *suffix = " kMGTP";
1551 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1554 url_fprintf(pb, "%lld%c", count, *s);
1557 static void compute_stats(HTTPContext *c)
1564 ByteIOContext pb1, *pb = &pb1;
1566 if (url_open_dyn_buf(pb) < 0) {
1567 /* XXX: return an error ? */
1568 c->buffer_ptr = c->buffer;
1569 c->buffer_end = c->buffer;
1573 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1574 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1575 url_fprintf(pb, "Pragma: no-cache\r\n");
1576 url_fprintf(pb, "\r\n");
1578 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1579 if (c->stream->feed_filename) {
1580 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1582 url_fprintf(pb, "</HEAD>\n<BODY>");
1583 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1585 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1586 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1587 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");
1588 stream = first_stream;
1589 while (stream != NULL) {
1590 char sfilename[1024];
1593 if (stream->feed != stream) {
1594 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1595 eosf = sfilename + strlen(sfilename);
1596 if (eosf - sfilename >= 4) {
1597 if (strcmp(eosf - 4, ".asf") == 0) {
1598 strcpy(eosf - 4, ".asx");
1599 } else if (strcmp(eosf - 3, ".rm") == 0) {
1600 strcpy(eosf - 3, ".ram");
1601 } else if (stream->fmt == &rtp_mux) {
1602 /* generate a sample RTSP director if
1603 unicast. Generate an SDP redirector if
1605 eosf = strrchr(sfilename, '.');
1607 eosf = sfilename + strlen(sfilename);
1608 if (stream->is_multicast)
1609 strcpy(eosf, ".sdp");
1611 strcpy(eosf, ".rtsp");
1615 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1616 sfilename, stream->filename);
1617 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1618 stream->conns_served);
1619 fmt_bytecount(pb, stream->bytes_served);
1620 switch(stream->stream_type) {
1621 case STREAM_TYPE_LIVE:
1623 int audio_bit_rate = 0;
1624 int video_bit_rate = 0;
1625 const char *audio_codec_name = "";
1626 const char *video_codec_name = "";
1627 const char *audio_codec_name_extra = "";
1628 const char *video_codec_name_extra = "";
1630 for(i=0;i<stream->nb_streams;i++) {
1631 AVStream *st = stream->streams[i];
1632 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1633 switch(st->codec.codec_type) {
1634 case CODEC_TYPE_AUDIO:
1635 audio_bit_rate += st->codec.bit_rate;
1637 if (*audio_codec_name)
1638 audio_codec_name_extra = "...";
1639 audio_codec_name = codec->name;
1642 case CODEC_TYPE_VIDEO:
1643 video_bit_rate += st->codec.bit_rate;
1645 if (*video_codec_name)
1646 video_codec_name_extra = "...";
1647 video_codec_name = codec->name;
1654 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",
1657 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1658 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1660 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1662 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1664 url_fprintf(pb, "\n");
1668 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1672 stream = stream->next;
1674 url_fprintf(pb, "</TABLE>\n");
1676 stream = first_stream;
1677 while (stream != NULL) {
1678 if (stream->feed == stream) {
1679 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1681 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1683 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1688 /* This is somewhat linux specific I guess */
1689 snprintf(ps_cmd, sizeof(ps_cmd),
1690 "ps -o \"%%cpu,cputime\" --no-headers %d",
1693 pid_stat = popen(ps_cmd, "r");
1698 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1700 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1708 url_fprintf(pb, "<p>");
1710 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");
1712 for (i = 0; i < stream->nb_streams; i++) {
1713 AVStream *st = stream->streams[i];
1714 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1715 const char *type = "unknown";
1716 char parameters[64];
1720 switch(st->codec.codec_type) {
1721 case CODEC_TYPE_AUDIO:
1724 case CODEC_TYPE_VIDEO:
1726 sprintf(parameters, "%dx%d, q=%d-%d, fps=%d", st->codec.width, st->codec.height,
1727 st->codec.qmin, st->codec.qmax, st->codec.frame_rate / FRAME_RATE_BASE);
1732 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1733 i, type, st->codec.bit_rate/1000, codec ? codec->name : "", parameters);
1735 url_fprintf(pb, "</table>\n");
1738 stream = stream->next;
1744 AVCodecContext *enc;
1748 stream = first_feed;
1749 while (stream != NULL) {
1750 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1751 url_fprintf(pb, "<TABLE>\n");
1752 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1753 for(i=0;i<stream->nb_streams;i++) {
1754 AVStream *st = stream->streams[i];
1755 FeedData *fdata = st->priv_data;
1758 avcodec_string(buf, sizeof(buf), enc);
1759 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1760 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1761 avg /= enc->frame_size;
1762 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1763 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1765 url_fprintf(pb, "</TABLE>\n");
1766 stream = stream->next_feed;
1771 /* connection status */
1772 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1774 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1775 nb_connections, nb_max_connections);
1777 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1778 current_bandwidth, max_bandwidth);
1780 url_fprintf(pb, "<TABLE>\n");
1781 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");
1782 c1 = first_http_ctx;
1784 while (c1 != NULL) {
1790 for (j = 0; j < c1->stream->nb_streams; j++) {
1791 if (!c1->stream->feed) {
1792 bitrate += c1->stream->streams[j]->codec.bit_rate;
1794 if (c1->feed_streams[j] >= 0) {
1795 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec.bit_rate;
1802 p = inet_ntoa(c1->from_addr.sin_addr);
1803 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1805 c1->stream ? c1->stream->filename : "",
1806 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1809 http_state[c1->state]);
1810 fmt_bytecount(pb, bitrate);
1811 url_fprintf(pb, "<td align=right>");
1812 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1813 url_fprintf(pb, "<td align=right>");
1814 fmt_bytecount(pb, c1->data_count);
1815 url_fprintf(pb, "\n");
1818 url_fprintf(pb, "</TABLE>\n");
1823 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1824 url_fprintf(pb, "</BODY>\n</HTML>\n");
1826 len = url_close_dyn_buf(pb, &c->pb_buffer);
1827 c->buffer_ptr = c->pb_buffer;
1828 c->buffer_end = c->pb_buffer + len;
1831 /* check if the parser needs to be opened for stream i */
1832 static void open_parser(AVFormatContext *s, int i)
1834 AVStream *st = s->streams[i];
1837 if (!st->codec.codec) {
1838 codec = avcodec_find_decoder(st->codec.codec_id);
1839 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1840 st->codec.parse_only = 1;
1841 if (avcodec_open(&st->codec, codec) < 0) {
1842 st->codec.parse_only = 0;
1848 static int open_input_stream(HTTPContext *c, const char *info)
1851 char input_filename[1024];
1856 /* find file name */
1857 if (c->stream->feed) {
1858 strcpy(input_filename, c->stream->feed->feed_filename);
1859 buf_size = FFM_PACKET_SIZE;
1860 /* compute position (absolute time) */
1861 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1862 stream_pos = parse_date(buf, 0);
1863 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1864 int prebuffer = strtol(buf, 0, 10);
1865 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1867 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1870 strcpy(input_filename, c->stream->feed_filename);
1872 /* compute position (relative time) */
1873 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1874 stream_pos = parse_date(buf, 1);
1879 if (input_filename[0] == '\0')
1883 { time_t when = stream_pos / 1000000;
1884 http_log("Stream pos = %lld, time=%s", stream_pos, ctime(&when));
1889 if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0) {
1890 http_log("%s not found", input_filename);
1895 /* open each parser */
1896 for(i=0;i<s->nb_streams;i++)
1899 /* choose stream as clock source (we favorize video stream if
1900 present) for packet sending */
1901 c->pts_stream_index = 0;
1902 for(i=0;i<c->stream->nb_streams;i++) {
1903 if (c->pts_stream_index == 0 &&
1904 c->stream->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO) {
1905 c->pts_stream_index = i;
1909 if (c->fmt_in->iformat->read_seek) {
1910 c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
1912 /* set the start time (needed for maxtime and RTP packet timing) */
1913 c->start_time = cur_time;
1914 c->first_pts = AV_NOPTS_VALUE;
1918 /* currently desactivated because the new PTS handling is not
1920 //#define AV_READ_FRAME
1921 #ifdef AV_READ_FRAME
1923 /* XXX: generalize that in ffmpeg for picture/audio/data. Currently
1924 the return packet MUST NOT be freed */
1925 int av_read_frame(AVFormatContext *s, AVPacket *pkt)
1928 int len, ret, old_nb_streams, i;
1930 /* see if remaining frames must be parsed */
1932 if (s->cur_len > 0) {
1933 st = s->streams[s->cur_pkt.stream_index];
1934 len = avcodec_parse_frame(&st->codec, &pkt->data, &pkt->size,
1935 s->cur_ptr, s->cur_len);
1937 /* error: get next packet */
1943 /* init pts counter if not done */
1944 if (st->pts.den == 0) {
1945 switch(st->codec.codec_type) {
1946 case CODEC_TYPE_AUDIO:
1947 st->pts_incr = (int64_t)s->pts_den;
1948 av_frac_init(&st->pts, st->pts.val, 0,
1949 (int64_t)s->pts_num * st->codec.sample_rate);
1951 case CODEC_TYPE_VIDEO:
1952 st->pts_incr = (int64_t)s->pts_den * FRAME_RATE_BASE;
1953 av_frac_init(&st->pts, st->pts.val, 0,
1954 (int64_t)s->pts_num * st->codec.frame_rate);
1961 /* a frame was read: return it */
1962 pkt->pts = st->pts.val;
1964 printf("add pts=%Lx num=%Lx den=%Lx incr=%Lx\n",
1965 st->pts.val, st->pts.num, st->pts.den, st->pts_incr);
1967 switch(st->codec.codec_type) {
1968 case CODEC_TYPE_AUDIO:
1969 av_frac_add(&st->pts, st->pts_incr * st->codec.frame_size);
1971 case CODEC_TYPE_VIDEO:
1972 av_frac_add(&st->pts, st->pts_incr);
1977 pkt->stream_index = s->cur_pkt.stream_index;
1978 /* we use the codec indication because it is
1979 more accurate than the demux flags */
1981 if (st->codec.coded_frame->key_frame)
1982 pkt->flags |= PKT_FLAG_KEY;
1987 /* free previous packet */
1988 av_free_packet(&s->cur_pkt);
1990 old_nb_streams = s->nb_streams;
1991 ret = av_read_packet(s, &s->cur_pkt);
1994 /* open parsers for each new streams */
1995 for(i = old_nb_streams; i < s->nb_streams; i++)
1997 st = s->streams[s->cur_pkt.stream_index];
1999 /* update current pts (XXX: dts handling) from packet, or
2000 use current pts if none given */
2001 if (s->cur_pkt.pts != AV_NOPTS_VALUE) {
2002 av_frac_set(&st->pts, s->cur_pkt.pts);
2004 s->cur_pkt.pts = st->pts.val;
2006 if (!st->codec.codec) {
2007 /* no codec opened: just return the raw packet */
2010 /* no codec opened: just update the pts by considering we
2011 have one frame and free the packet */
2012 if (st->pts.den == 0) {
2013 switch(st->codec.codec_type) {
2014 case CODEC_TYPE_AUDIO:
2015 st->pts_incr = (int64_t)s->pts_den * st->codec.frame_size;
2016 av_frac_init(&st->pts, st->pts.val, 0,
2017 (int64_t)s->pts_num * st->codec.sample_rate);
2019 case CODEC_TYPE_VIDEO:
2020 st->pts_incr = (int64_t)s->pts_den * FRAME_RATE_BASE;
2021 av_frac_init(&st->pts, st->pts.val, 0,
2022 (int64_t)s->pts_num * st->codec.frame_rate);
2028 av_frac_add(&st->pts, st->pts_incr);
2031 s->cur_ptr = s->cur_pkt.data;
2032 s->cur_len = s->cur_pkt.size;
2038 static int compute_send_delay(HTTPContext *c)
2040 int64_t cur_pts, delta_pts, next_pts;
2043 /* compute current pts value from system time */
2044 cur_pts = ((int64_t)(cur_time - c->start_time) * c->fmt_in->pts_den) /
2045 (c->fmt_in->pts_num * 1000LL);
2046 /* compute the delta from the stream we choose as
2047 main clock (we do that to avoid using explicit
2048 buffers to do exact packet reordering for each
2050 /* XXX: really need to fix the number of streams */
2051 if (c->pts_stream_index >= c->fmt_in->nb_streams)
2054 next_pts = c->fmt_in->streams[c->pts_stream_index]->pts.val;
2055 delta_pts = next_pts - cur_pts;
2056 if (delta_pts <= 0) {
2059 delay1 = (delta_pts * 1000 * c->fmt_in->pts_num) / c->fmt_in->pts_den;
2065 /* just fall backs */
2066 static int av_read_frame(AVFormatContext *s, AVPacket *pkt)
2068 return av_read_packet(s, pkt);
2071 static int compute_send_delay(HTTPContext *c)
2073 int datarate = 8 * get_longterm_datarate(&c->datarate, c->data_count);
2075 if (datarate > c->stream->bandwidth * 2000) {
2083 static int http_prepare_data(HTTPContext *c)
2086 AVFormatContext *ctx;
2089 case HTTPSTATE_SEND_DATA_HEADER:
2090 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2091 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
2093 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
2094 c->stream->comment);
2095 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
2096 c->stream->copyright);
2097 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
2100 /* open output stream by using specified codecs */
2101 c->fmt_ctx.oformat = c->stream->fmt;
2102 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2103 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2105 st = av_mallocz(sizeof(AVStream));
2106 c->fmt_ctx.streams[i] = st;
2107 /* if file or feed, then just take streams from FFStream struct */
2108 if (!c->stream->feed ||
2109 c->stream->feed == c->stream)
2110 memcpy(st, c->stream->streams[i], sizeof(AVStream));
2112 memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]],
2114 st->codec.frame_number = 0; /* XXX: should be done in
2115 AVStream, not in codec */
2116 /* I'm pretty sure that this is not correct...
2117 * However, without it, we crash
2119 st->codec.coded_frame = &dummy_frame;
2121 c->got_key_frame = 0;
2123 /* prepare header and save header data in a stream */
2124 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2125 /* XXX: potential leak */
2128 c->fmt_ctx.pb.is_streamed = 1;
2130 av_set_parameters(&c->fmt_ctx, NULL);
2131 av_write_header(&c->fmt_ctx);
2133 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2134 c->buffer_ptr = c->pb_buffer;
2135 c->buffer_end = c->pb_buffer + len;
2137 c->state = HTTPSTATE_SEND_DATA;
2138 c->last_packet_sent = 0;
2140 case HTTPSTATE_SEND_DATA:
2141 /* find a new packet */
2145 /* read a packet from the input stream */
2146 if (c->stream->feed) {
2147 ffm_set_write_index(c->fmt_in,
2148 c->stream->feed->feed_write_index,
2149 c->stream->feed->feed_size);
2152 if (c->stream->max_time &&
2153 c->stream->max_time + c->start_time - cur_time < 0) {
2154 /* We have timed out */
2155 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2157 if (1 || c->is_packetized) {
2158 if (compute_send_delay(c) > 0) {
2159 c->state = HTTPSTATE_WAIT;
2160 return 1; /* state changed */
2164 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2165 if (c->stream->feed && c->stream->feed->feed_opened) {
2166 /* if coming from feed, it means we reached the end of the
2167 ffm file, so must wait for more data */
2168 c->state = HTTPSTATE_WAIT_FEED;
2169 return 1; /* state changed */
2171 if (c->stream->loop) {
2172 av_close_input_file(c->fmt_in);
2174 if (open_input_stream(c, "") < 0)
2179 /* must send trailer now because eof or error */
2180 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2184 /* update first pts if needed */
2185 if (c->first_pts == AV_NOPTS_VALUE)
2186 c->first_pts = pkt.pts;
2188 /* send it to the appropriate stream */
2189 if (c->stream->feed) {
2190 /* if coming from a feed, select the right stream */
2191 if (c->switch_pending) {
2192 c->switch_pending = 0;
2193 for(i=0;i<c->stream->nb_streams;i++) {
2194 if (c->switch_feed_streams[i] == pkt.stream_index) {
2195 if (pkt.flags & PKT_FLAG_KEY) {
2196 do_switch_stream(c, i);
2199 if (c->switch_feed_streams[i] >= 0) {
2200 c->switch_pending = 1;
2204 for(i=0;i<c->stream->nb_streams;i++) {
2205 if (c->feed_streams[i] == pkt.stream_index) {
2206 pkt.stream_index = i;
2207 if (pkt.flags & PKT_FLAG_KEY) {
2208 c->got_key_frame |= 1 << i;
2210 /* See if we have all the key frames, then
2211 * we start to send. This logic is not quite
2212 * right, but it works for the case of a
2213 * single video stream with one or more
2214 * audio streams (for which every frame is
2215 * typically a key frame).
2217 if (!c->stream->send_on_key ||
2218 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2224 AVCodecContext *codec;
2227 /* specific handling for RTP: we use several
2228 output stream (one for each RTP
2229 connection). XXX: need more abstract handling */
2230 if (c->is_packetized) {
2231 c->packet_stream_index = pkt.stream_index;
2232 ctx = c->rtp_ctx[c->packet_stream_index];
2233 codec = &ctx->streams[0]->codec;
2234 /* only one stream per RTP connection */
2235 pkt.stream_index = 0;
2239 codec = &ctx->streams[pkt.stream_index]->codec;
2242 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2245 if (codec->codec_type == CODEC_TYPE_AUDIO) {
2246 codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
2247 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
2251 if (c->is_packetized) {
2252 ret = url_open_dyn_packet_buf(&ctx->pb,
2253 url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]));
2254 c->packet_byte_count = 0;
2255 c->packet_start_time_us = av_gettime();
2257 ret = url_open_dyn_buf(&ctx->pb);
2260 /* XXX: potential leak */
2263 if (av_write_frame(ctx, pkt.stream_index, pkt.data, pkt.size)) {
2264 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2267 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2268 c->buffer_ptr = c->pb_buffer;
2269 c->buffer_end = c->pb_buffer + len;
2271 codec->frame_number++;
2273 #ifndef AV_READ_FRAME
2274 av_free_packet(&pkt);
2281 case HTTPSTATE_SEND_DATA_TRAILER:
2282 /* last packet test ? */
2283 if (c->last_packet_sent || c->is_packetized)
2286 /* prepare header */
2287 if (url_open_dyn_buf(&ctx->pb) < 0) {
2288 /* XXX: potential leak */
2291 av_write_trailer(ctx);
2292 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2293 c->buffer_ptr = c->pb_buffer;
2294 c->buffer_end = c->pb_buffer + len;
2296 c->last_packet_sent = 1;
2303 #define SHORT_TERM_BANDWIDTH 8000000
2305 /* should convert the format at the same time */
2306 static int http_send_data(HTTPContext *c)
2310 while (c->buffer_ptr >= c->buffer_end) {
2311 av_freep(&c->pb_buffer);
2312 ret = http_prepare_data(c);
2315 else if (ret == 0) {
2318 /* state change requested */
2323 if (c->buffer_ptr < c->buffer_end) {
2324 if (c->is_packetized) {
2325 /* RTP/UDP data output */
2326 len = c->buffer_end - c->buffer_ptr;
2328 /* fail safe - should never happen */
2330 c->buffer_ptr = c->buffer_end;
2333 len = (c->buffer_ptr[0] << 24) |
2334 (c->buffer_ptr[1] << 16) |
2335 (c->buffer_ptr[2] << 8) |
2337 if (len > (c->buffer_end - c->buffer_ptr))
2340 /* short term bandwidth limitation */
2341 dt = av_gettime() - c->packet_start_time_us;
2345 if ((c->packet_byte_count + len) * (int64_t)1000000 >=
2346 (SHORT_TERM_BANDWIDTH / 8) * (int64_t)dt) {
2347 /* bandwidth overflow : wait at most one tick and retry */
2348 c->state = HTTPSTATE_WAIT_SHORT;
2353 url_write(c->rtp_handles[c->packet_stream_index],
2354 c->buffer_ptr, len);
2355 c->buffer_ptr += len;
2356 c->packet_byte_count += len;
2358 /* TCP data output */
2359 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2361 if (errno != EAGAIN && errno != EINTR) {
2362 /* error : close connection */
2368 c->buffer_ptr += len;
2371 c->data_count += len;
2372 update_datarate(&c->datarate, c->data_count);
2374 c->stream->bytes_served += len;
2379 static int http_start_receive_data(HTTPContext *c)
2383 if (c->stream->feed_opened)
2387 fd = open(c->stream->feed_filename, O_RDWR);
2392 c->stream->feed_write_index = ffm_read_write_index(fd);
2393 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2394 lseek(fd, 0, SEEK_SET);
2396 /* init buffer input */
2397 c->buffer_ptr = c->buffer;
2398 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2399 c->stream->feed_opened = 1;
2403 static int http_receive_data(HTTPContext *c)
2407 if (c->buffer_end > c->buffer_ptr) {
2410 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2412 if (errno != EAGAIN && errno != EINTR) {
2413 /* error : close connection */
2416 } else if (len == 0) {
2417 /* end of connection : close it */
2420 c->buffer_ptr += len;
2421 c->data_count += len;
2422 update_datarate(&c->datarate, c->data_count);
2426 if (c->buffer_ptr >= c->buffer_end) {
2427 FFStream *feed = c->stream;
2428 /* a packet has been received : write it in the store, except
2430 if (c->data_count > FFM_PACKET_SIZE) {
2432 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2433 /* XXX: use llseek or url_seek */
2434 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2435 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2437 feed->feed_write_index += FFM_PACKET_SIZE;
2438 /* update file size */
2439 if (feed->feed_write_index > c->stream->feed_size)
2440 feed->feed_size = feed->feed_write_index;
2442 /* handle wrap around if max file size reached */
2443 if (feed->feed_write_index >= c->stream->feed_max_size)
2444 feed->feed_write_index = FFM_PACKET_SIZE;
2447 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2449 /* wake up any waiting connections */
2450 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2451 if (c1->state == HTTPSTATE_WAIT_FEED &&
2452 c1->stream->feed == c->stream->feed) {
2453 c1->state = HTTPSTATE_SEND_DATA;
2457 /* We have a header in our hands that contains useful data */
2459 AVInputFormat *fmt_in;
2460 ByteIOContext *pb = &s.pb;
2463 memset(&s, 0, sizeof(s));
2465 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2466 pb->buf_end = c->buffer_end; /* ?? */
2467 pb->is_streamed = 1;
2469 /* use feed output format name to find corresponding input format */
2470 fmt_in = av_find_input_format(feed->fmt->name);
2474 if (fmt_in->priv_data_size > 0) {
2475 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2481 if (fmt_in->read_header(&s, 0) < 0) {
2482 av_freep(&s.priv_data);
2486 /* Now we have the actual streams */
2487 if (s.nb_streams != feed->nb_streams) {
2488 av_freep(&s.priv_data);
2491 for (i = 0; i < s.nb_streams; i++) {
2492 memcpy(&feed->streams[i]->codec,
2493 &s.streams[i]->codec, sizeof(AVCodecContext));
2495 av_freep(&s.priv_data);
2497 c->buffer_ptr = c->buffer;
2502 c->stream->feed_opened = 0;
2507 /********************************************************************/
2510 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2517 switch(error_number) {
2518 #define DEF(n, c, s) case c: str = s; break;
2519 #include "rtspcodes.h"
2522 str = "Unknown Error";
2526 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2527 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2529 /* output GMT time */
2533 p = buf2 + strlen(p) - 1;
2536 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2539 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2541 rtsp_reply_header(c, error_number);
2542 url_fprintf(c->pb, "\r\n");
2545 static int rtsp_parse_request(HTTPContext *c)
2547 const char *p, *p1, *p2;
2554 RTSPHeader header1, *header = &header1;
2556 c->buffer_ptr[0] = '\0';
2559 get_word(cmd, sizeof(cmd), &p);
2560 get_word(url, sizeof(url), &p);
2561 get_word(protocol, sizeof(protocol), &p);
2563 pstrcpy(c->method, sizeof(c->method), cmd);
2564 pstrcpy(c->url, sizeof(c->url), url);
2565 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2568 if (url_open_dyn_buf(c->pb) < 0) {
2569 /* XXX: cannot do more */
2570 c->pb = NULL; /* safety */
2574 /* check version name */
2575 if (strcmp(protocol, "RTSP/1.0") != 0) {
2576 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2580 /* parse each header line */
2581 memset(header, 0, sizeof(RTSPHeader));
2582 /* skip to next line */
2583 while (*p != '\n' && *p != '\0')
2587 while (*p != '\0') {
2588 p1 = strchr(p, '\n');
2592 if (p2 > p && p2[-1] == '\r')
2594 /* skip empty line */
2598 if (len > sizeof(line) - 1)
2599 len = sizeof(line) - 1;
2600 memcpy(line, p, len);
2602 rtsp_parse_line(header, line);
2606 /* handle sequence number */
2607 c->seq = header->seq;
2609 if (!strcmp(cmd, "DESCRIBE")) {
2610 rtsp_cmd_describe(c, url);
2611 } else if (!strcmp(cmd, "SETUP")) {
2612 rtsp_cmd_setup(c, url, header);
2613 } else if (!strcmp(cmd, "PLAY")) {
2614 rtsp_cmd_play(c, url, header);
2615 } else if (!strcmp(cmd, "PAUSE")) {
2616 rtsp_cmd_pause(c, url, header);
2617 } else if (!strcmp(cmd, "TEARDOWN")) {
2618 rtsp_cmd_teardown(c, url, header);
2620 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2623 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2624 c->pb = NULL; /* safety */
2626 /* XXX: cannot do more */
2629 c->buffer_ptr = c->pb_buffer;
2630 c->buffer_end = c->pb_buffer + len;
2631 c->state = RTSPSTATE_SEND_REPLY;
2635 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2637 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2638 struct in_addr my_ip)
2640 ByteIOContext pb1, *pb = &pb1;
2641 int i, payload_type, port, private_payload_type, j;
2642 const char *ipstr, *title, *mediatype;
2645 if (url_open_dyn_buf(pb) < 0)
2648 /* general media info */
2650 url_fprintf(pb, "v=0\n");
2651 ipstr = inet_ntoa(my_ip);
2652 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2653 title = stream->title;
2654 if (title[0] == '\0')
2656 url_fprintf(pb, "s=%s\n", title);
2657 if (stream->comment[0] != '\0')
2658 url_fprintf(pb, "i=%s\n", stream->comment);
2659 if (stream->is_multicast) {
2660 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2662 /* for each stream, we output the necessary info */
2663 private_payload_type = 96;
2664 for(i = 0; i < stream->nb_streams; i++) {
2665 st = stream->streams[i];
2666 switch(st->codec.codec_type) {
2667 case CODEC_TYPE_AUDIO:
2668 mediatype = "audio";
2670 case CODEC_TYPE_VIDEO:
2671 mediatype = "video";
2674 mediatype = "application";
2677 /* NOTE: the port indication is not correct in case of
2678 unicast. It is not an issue because RTSP gives it */
2679 payload_type = rtp_get_payload_type(&st->codec);
2680 if (payload_type < 0)
2681 payload_type = private_payload_type++;
2682 if (stream->is_multicast) {
2683 port = stream->multicast_port + 2 * i;
2687 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2688 mediatype, port, payload_type);
2689 if (payload_type >= 96) {
2690 /* for private payload type, we need to give more info */
2691 switch(st->codec.codec_id) {
2692 case CODEC_ID_MPEG4:
2695 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2696 payload_type, 90000);
2697 /* we must also add the mpeg4 header */
2698 data = st->codec.extradata;
2700 url_fprintf(pb, "a=fmtp:%d config=");
2701 for(j=0;j<st->codec.extradata_size;j++) {
2702 url_fprintf(pb, "%02x", data[j]);
2704 url_fprintf(pb, "\n");
2709 /* XXX: add other codecs ? */
2713 url_fprintf(pb, "a=control:streamid=%d\n", i);
2715 return url_close_dyn_buf(pb, pbuffer);
2717 url_close_dyn_buf(pb, pbuffer);
2722 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2728 int content_length, len;
2729 struct sockaddr_in my_addr;
2731 /* find which url is asked */
2732 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2737 for(stream = first_stream; stream != NULL; stream = stream->next) {
2738 if (!stream->is_feed && stream->fmt == &rtp_mux &&
2739 !strcmp(path, stream->filename)) {
2743 /* no stream found */
2744 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2748 /* prepare the media description in sdp format */
2750 /* get the host IP */
2751 len = sizeof(my_addr);
2752 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2754 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2755 if (content_length < 0) {
2756 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2759 rtsp_reply_header(c, RTSP_STATUS_OK);
2760 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2761 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2762 url_fprintf(c->pb, "\r\n");
2763 put_buffer(c->pb, content, content_length);
2766 static HTTPContext *find_rtp_session(const char *session_id)
2770 if (session_id[0] == '\0')
2773 for(c = first_http_ctx; c != NULL; c = c->next) {
2774 if (!strcmp(c->session_id, session_id))
2780 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2782 RTSPTransportField *th;
2785 for(i=0;i<h->nb_transports;i++) {
2786 th = &h->transports[i];
2787 if (th->protocol == protocol)
2793 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2797 int stream_index, port;
2802 RTSPTransportField *th;
2803 struct sockaddr_in dest_addr;
2804 RTSPActionServerSetup setup;
2806 /* find which url is asked */
2807 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2812 /* now check each stream */
2813 for(stream = first_stream; stream != NULL; stream = stream->next) {
2814 if (!stream->is_feed && stream->fmt == &rtp_mux) {
2815 /* accept aggregate filenames only if single stream */
2816 if (!strcmp(path, stream->filename)) {
2817 if (stream->nb_streams != 1) {
2818 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2825 for(stream_index = 0; stream_index < stream->nb_streams;
2827 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2828 stream->filename, stream_index);
2829 if (!strcmp(path, buf))
2834 /* no stream found */
2835 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2839 /* generate session id if needed */
2840 if (h->session_id[0] == '\0') {
2841 snprintf(h->session_id, sizeof(h->session_id),
2842 "%08x%08x", (int)random(), (int)random());
2845 /* find rtp session, and create it if none found */
2846 rtp_c = find_rtp_session(h->session_id);
2848 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id);
2850 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2854 /* open input stream */
2855 if (open_input_stream(rtp_c, "") < 0) {
2856 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2860 /* always prefer UDP */
2861 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2863 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2865 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2869 rtp_c->rtp_protocol = th->protocol;
2872 /* test if stream is OK (test needed because several SETUP needs
2873 to be done for a given file) */
2874 if (rtp_c->stream != stream) {
2875 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2879 /* test if stream is already set up */
2880 if (rtp_c->rtp_ctx[stream_index]) {
2881 rtsp_reply_error(c, RTSP_STATUS_STATE);
2885 /* check transport */
2886 th = find_transport(h, rtp_c->rtp_protocol);
2887 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2888 th->client_port_min <= 0)) {
2889 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2893 /* setup default options */
2894 setup.transport_option[0] = '\0';
2895 dest_addr = rtp_c->from_addr;
2896 dest_addr.sin_port = htons(th->client_port_min);
2898 /* add transport option if needed */
2899 if (ff_rtsp_callback) {
2900 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2901 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2902 (char *)&setup, sizeof(setup),
2903 stream->rtsp_option) < 0) {
2904 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2907 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2911 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
2912 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2916 /* now everything is OK, so we can send the connection parameters */
2917 rtsp_reply_header(c, RTSP_STATUS_OK);
2919 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2921 switch(rtp_c->rtp_protocol) {
2922 case RTSP_PROTOCOL_RTP_UDP:
2923 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2924 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2925 "client_port=%d-%d;server_port=%d-%d",
2926 th->client_port_min, th->client_port_min + 1,
2929 case RTSP_PROTOCOL_RTP_TCP:
2930 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2931 stream_index * 2, stream_index * 2 + 1);
2936 if (setup.transport_option[0] != '\0') {
2937 url_fprintf(c->pb, ";%s", setup.transport_option);
2939 url_fprintf(c->pb, "\r\n");
2942 url_fprintf(c->pb, "\r\n");
2946 /* find an rtp connection by using the session ID. Check consistency
2948 static HTTPContext *find_rtp_session_with_url(const char *url,
2949 const char *session_id)
2955 rtp_c = find_rtp_session(session_id);
2959 /* find which url is asked */
2960 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2964 if (strcmp(path, rtp_c->stream->filename) != 0)
2969 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2973 rtp_c = find_rtp_session_with_url(url, h->session_id);
2975 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2979 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2980 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2981 rtp_c->state != HTTPSTATE_READY) {
2982 rtsp_reply_error(c, RTSP_STATUS_STATE);
2986 rtp_c->state = HTTPSTATE_SEND_DATA;
2988 /* now everything is OK, so we can send the connection parameters */
2989 rtsp_reply_header(c, RTSP_STATUS_OK);
2991 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2992 url_fprintf(c->pb, "\r\n");
2995 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
2999 rtp_c = find_rtp_session_with_url(url, h->session_id);
3001 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3005 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3006 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3007 rtsp_reply_error(c, RTSP_STATUS_STATE);
3011 rtp_c->state = HTTPSTATE_READY;
3013 /* now everything is OK, so we can send the connection parameters */
3014 rtsp_reply_header(c, RTSP_STATUS_OK);
3016 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3017 url_fprintf(c->pb, "\r\n");
3020 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3024 rtp_c = find_rtp_session_with_url(url, h->session_id);
3026 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3030 /* abort the session */
3031 close_connection(rtp_c);
3033 if (ff_rtsp_callback) {
3034 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
3036 rtp_c->stream->rtsp_option);
3039 /* now everything is OK, so we can send the connection parameters */
3040 rtsp_reply_header(c, RTSP_STATUS_OK);
3042 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3043 url_fprintf(c->pb, "\r\n");
3047 /********************************************************************/
3050 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3051 FFStream *stream, const char *session_id)
3053 HTTPContext *c = NULL;
3055 /* XXX: should output a warning page when coming
3056 close to the connection limit */
3057 if (nb_connections >= nb_max_connections)
3060 /* add a new connection */
3061 c = av_mallocz(sizeof(HTTPContext));
3066 c->poll_entry = NULL;
3067 c->from_addr = *from_addr;
3068 c->buffer_size = IOBUFFER_INIT_SIZE;
3069 c->buffer = av_malloc(c->buffer_size);
3074 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
3075 c->state = HTTPSTATE_READY;
3076 c->is_packetized = 1;
3077 /* protocol is shown in statistics */
3078 pstrcpy(c->protocol, sizeof(c->protocol), "RTP");
3080 current_bandwidth += stream->bandwidth;
3082 c->next = first_http_ctx;
3094 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3095 command). if dest_addr is NULL, then TCP tunneling in RTSP is
3097 static int rtp_new_av_stream(HTTPContext *c,
3098 int stream_index, struct sockaddr_in *dest_addr)
3100 AVFormatContext *ctx;
3107 /* now we can open the relevant output stream */
3108 ctx = av_mallocz(sizeof(AVFormatContext));
3111 ctx->oformat = &rtp_mux;
3113 st = av_mallocz(sizeof(AVStream));
3116 ctx->nb_streams = 1;
3117 ctx->streams[0] = st;
3119 if (!c->stream->feed ||
3120 c->stream->feed == c->stream) {
3121 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3124 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3129 /* build destination RTP address */
3130 ipaddr = inet_ntoa(dest_addr->sin_addr);
3132 /* XXX: also pass as parameter to function ? */
3133 if (c->stream->is_multicast) {
3135 ttl = c->stream->multicast_ttl;
3138 snprintf(ctx->filename, sizeof(ctx->filename),
3139 "rtp://%s:%d?multicast=1&ttl=%d",
3140 ipaddr, ntohs(dest_addr->sin_port), ttl);
3142 snprintf(ctx->filename, sizeof(ctx->filename),
3143 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3146 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3148 c->rtp_handles[stream_index] = h;
3153 http_log("%s:%d - - [%s] \"RTPSTART %s/streamid=%d\"\n",
3154 ipaddr, ntohs(dest_addr->sin_port),
3156 c->stream->filename, stream_index);
3158 /* normally, no packets should be output here, but the packet size may be checked */
3159 if (url_open_dyn_packet_buf(&ctx->pb,
3160 url_get_max_packet_size(h)) < 0) {
3161 /* XXX: close stream */
3164 av_set_parameters(ctx, NULL);
3165 if (av_write_header(ctx) < 0) {
3172 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3175 c->rtp_ctx[stream_index] = ctx;
3179 /********************************************************************/
3180 /* ffserver initialization */
3182 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3186 fst = av_mallocz(sizeof(AVStream));
3189 fst->priv_data = av_mallocz(sizeof(FeedData));
3190 memcpy(&fst->codec, codec, sizeof(AVCodecContext));
3191 fst->codec.coded_frame = &dummy_frame;
3192 stream->streams[stream->nb_streams++] = fst;
3196 /* return the stream number in the feed */
3197 static int add_av_stream(FFStream *feed, AVStream *st)
3200 AVCodecContext *av, *av1;
3204 for(i=0;i<feed->nb_streams;i++) {
3205 st = feed->streams[i];
3207 if (av1->codec_id == av->codec_id &&
3208 av1->codec_type == av->codec_type &&
3209 av1->bit_rate == av->bit_rate) {
3211 switch(av->codec_type) {
3212 case CODEC_TYPE_AUDIO:
3213 if (av1->channels == av->channels &&
3214 av1->sample_rate == av->sample_rate)
3217 case CODEC_TYPE_VIDEO:
3218 if (av1->width == av->width &&
3219 av1->height == av->height &&
3220 av1->frame_rate == av->frame_rate &&
3221 av1->gop_size == av->gop_size)
3230 fst = add_av_stream1(feed, av);
3233 return feed->nb_streams - 1;
3238 static void remove_stream(FFStream *stream)
3242 while (*ps != NULL) {
3243 if (*ps == stream) {
3251 /* specific mpeg4 handling : we extract the raw parameters */
3252 static void extract_mpeg4_header(AVFormatContext *infile)
3254 int mpeg4_count, i, size;
3260 for(i=0;i<infile->nb_streams;i++) {
3261 st = infile->streams[i];
3262 if (st->codec.codec_id == CODEC_ID_MPEG4 &&
3263 st->codec.extradata == NULL) {
3270 printf("MPEG4 without extra data: trying to find header\n");
3271 while (mpeg4_count > 0) {
3272 if (av_read_packet(infile, &pkt) < 0)
3274 st = infile->streams[pkt.stream_index];
3275 if (st->codec.codec_id == CODEC_ID_MPEG4 &&
3276 st->codec.extradata == NULL) {
3277 /* fill extradata with the header */
3278 /* XXX: we make hard suppositions here ! */
3280 while (p < pkt.data + pkt.size - 4) {
3281 /* stop when vop header is found */
3282 if (p[0] == 0x00 && p[1] == 0x00 &&
3283 p[2] == 0x01 && p[3] == 0xb6) {
3284 size = p - pkt.data;
3285 // av_hex_dump(pkt.data, size);
3286 st->codec.extradata = av_malloc(size);
3287 st->codec.extradata_size = size;
3288 memcpy(st->codec.extradata, pkt.data, size);
3295 av_free_packet(&pkt);
3299 /* compute the needed AVStream for each file */
3300 static void build_file_streams(void)
3302 FFStream *stream, *stream_next;
3303 AVFormatContext *infile;
3306 /* gather all streams */
3307 for(stream = first_stream; stream != NULL; stream = stream_next) {
3308 stream_next = stream->next;
3309 if (stream->stream_type == STREAM_TYPE_LIVE &&
3311 /* the stream comes from a file */
3312 /* try to open the file */
3314 if (av_open_input_file(&infile, stream->feed_filename,
3315 NULL, 0, NULL) < 0) {
3316 http_log("%s not found", stream->feed_filename);
3317 /* remove stream (no need to spend more time on it) */
3319 remove_stream(stream);
3321 /* find all the AVStreams inside and reference them in
3323 if (av_find_stream_info(infile) < 0) {
3324 http_log("Could not find codec parameters from '%s'",
3325 stream->feed_filename);
3326 av_close_input_file(infile);
3329 extract_mpeg4_header(infile);
3331 for(i=0;i<infile->nb_streams;i++) {
3332 add_av_stream1(stream, &infile->streams[i]->codec);
3334 av_close_input_file(infile);
3340 /* compute the needed AVStream for each feed */
3341 static void build_feed_streams(void)
3343 FFStream *stream, *feed;
3346 /* gather all streams */
3347 for(stream = first_stream; stream != NULL; stream = stream->next) {
3348 feed = stream->feed;
3350 if (!stream->is_feed) {
3351 /* we handle a stream coming from a feed */
3352 for(i=0;i<stream->nb_streams;i++) {
3353 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3359 /* gather all streams */
3360 for(stream = first_stream; stream != NULL; stream = stream->next) {
3361 feed = stream->feed;
3363 if (stream->is_feed) {
3364 for(i=0;i<stream->nb_streams;i++) {
3365 stream->feed_streams[i] = i;
3371 /* create feed files if needed */
3372 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3375 if (url_exist(feed->feed_filename)) {
3376 /* See if it matches */
3380 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3381 /* Now see if it matches */
3382 if (s->nb_streams == feed->nb_streams) {
3384 for(i=0;i<s->nb_streams;i++) {
3386 sf = feed->streams[i];
3389 if (sf->index != ss->index ||
3391 printf("Index & Id do not match for stream %d\n", i);
3394 AVCodecContext *ccf, *ccs;
3398 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3400 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3401 printf("Codecs do not match for stream %d\n", i);
3403 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3404 printf("Codec bitrates do not match for stream %d\n", i);
3406 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3407 if (CHECK_CODEC(frame_rate) ||
3408 CHECK_CODEC(width) ||
3409 CHECK_CODEC(height)) {
3410 printf("Codec width, height and framerate do not match for stream %d\n", i);
3413 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3414 if (CHECK_CODEC(sample_rate) ||
3415 CHECK_CODEC(channels) ||
3416 CHECK_CODEC(frame_size)) {
3417 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3421 printf("Unknown codec type\n");
3430 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3431 feed->feed_filename, s->nb_streams, feed->nb_streams);
3434 av_close_input_file(s);
3436 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3437 feed->feed_filename);
3440 unlink(feed->feed_filename);
3442 if (!url_exist(feed->feed_filename)) {
3443 AVFormatContext s1, *s = &s1;
3445 /* only write the header of the ffm file */
3446 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3447 fprintf(stderr, "Could not open output feed file '%s'\n",
3448 feed->feed_filename);
3451 s->oformat = feed->fmt;
3452 s->nb_streams = feed->nb_streams;
3453 for(i=0;i<s->nb_streams;i++) {
3455 st = feed->streams[i];
3458 av_set_parameters(s, NULL);
3460 /* XXX: need better api */
3461 av_freep(&s->priv_data);
3464 /* get feed size and write index */
3465 fd = open(feed->feed_filename, O_RDONLY);
3467 fprintf(stderr, "Could not open output feed file '%s'\n",
3468 feed->feed_filename);
3472 feed->feed_write_index = ffm_read_write_index(fd);
3473 feed->feed_size = lseek(fd, 0, SEEK_END);
3474 /* ensure that we do not wrap before the end of file */
3475 if (feed->feed_max_size < feed->feed_size)
3476 feed->feed_max_size = feed->feed_size;
3482 /* compute the bandwidth used by each stream */
3483 static void compute_bandwidth(void)
3488 for(stream = first_stream; stream != NULL; stream = stream->next) {
3490 for(i=0;i<stream->nb_streams;i++) {
3491 AVStream *st = stream->streams[i];
3492 switch(st->codec.codec_type) {
3493 case CODEC_TYPE_AUDIO:
3494 case CODEC_TYPE_VIDEO:
3495 bandwidth += st->codec.bit_rate;
3501 stream->bandwidth = (bandwidth + 999) / 1000;
3505 static void get_arg(char *buf, int buf_size, const char **pp)
3512 while (isspace(*p)) p++;
3515 if (*p == '\"' || *p == '\'')
3527 if ((q - buf) < buf_size - 1)
3532 if (quote && *p == quote)
3537 /* add a codec and set the default parameters */
3538 static void add_codec(FFStream *stream, AVCodecContext *av)
3542 /* compute default parameters */
3543 switch(av->codec_type) {
3544 case CODEC_TYPE_AUDIO:
3545 if (av->bit_rate == 0)
3546 av->bit_rate = 64000;
3547 if (av->sample_rate == 0)
3548 av->sample_rate = 22050;
3549 if (av->channels == 0)
3552 case CODEC_TYPE_VIDEO:
3553 if (av->bit_rate == 0)
3554 av->bit_rate = 64000;
3555 if (av->frame_rate == 0)
3556 av->frame_rate = 5 * FRAME_RATE_BASE;
3557 if (av->width == 0 || av->height == 0) {
3561 /* Bitrate tolerance is less for streaming */
3562 if (av->bit_rate_tolerance == 0)
3563 av->bit_rate_tolerance = av->bit_rate / 4;
3568 if (av->max_qdiff == 0)
3570 av->qcompress = 0.5;
3574 av->rc_eq = "tex^qComp";
3575 if (!av->i_quant_factor)
3576 av->i_quant_factor = -0.8;
3577 if (!av->b_quant_factor)
3578 av->b_quant_factor = 1.25;
3579 if (!av->b_quant_offset)
3580 av->b_quant_offset = 1.25;
3581 if (!av->rc_min_rate)
3582 av->rc_min_rate = av->bit_rate / 2;
3583 if (!av->rc_max_rate)
3584 av->rc_max_rate = av->bit_rate * 2;
3591 st = av_mallocz(sizeof(AVStream));
3594 stream->streams[stream->nb_streams++] = st;
3595 memcpy(&st->codec, av, sizeof(AVCodecContext));
3598 static int opt_audio_codec(const char *arg)
3604 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3609 return CODEC_ID_NONE;
3615 static int opt_video_codec(const char *arg)
3621 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3626 return CODEC_ID_NONE;
3632 /* simplistic plugin support */
3634 #ifdef CONFIG_HAVE_DLOPEN
3635 void load_module(const char *filename)
3638 void (*init_func)(void);
3639 dll = dlopen(filename, RTLD_NOW);
3641 fprintf(stderr, "Could not load module '%s' - %s\n",
3642 filename, dlerror());
3646 init_func = dlsym(dll, "ffserver_module_init");
3649 "%s: init function 'ffserver_module_init()' not found\n",
3658 static int parse_ffconfig(const char *filename)
3665 int val, errors, line_num;
3666 FFStream **last_stream, *stream, *redirect;
3667 FFStream **last_feed, *feed;
3668 AVCodecContext audio_enc, video_enc;
3669 int audio_id, video_id;
3671 f = fopen(filename, "r");
3679 first_stream = NULL;
3680 last_stream = &first_stream;
3682 last_feed = &first_feed;
3686 audio_id = CODEC_ID_NONE;
3687 video_id = CODEC_ID_NONE;
3689 if (fgets(line, sizeof(line), f) == NULL)
3695 if (*p == '\0' || *p == '#')
3698 get_arg(cmd, sizeof(cmd), &p);
3700 if (!strcasecmp(cmd, "Port")) {
3701 get_arg(arg, sizeof(arg), &p);
3702 my_http_addr.sin_port = htons (atoi(arg));
3703 } else if (!strcasecmp(cmd, "BindAddress")) {
3704 get_arg(arg, sizeof(arg), &p);
3705 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3706 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3707 filename, line_num, arg);
3710 } else if (!strcasecmp(cmd, "NoDaemon")) {
3711 ffserver_daemon = 0;
3712 } else if (!strcasecmp(cmd, "RTSPPort")) {
3713 get_arg(arg, sizeof(arg), &p);
3714 my_rtsp_addr.sin_port = htons (atoi(arg));
3715 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3716 get_arg(arg, sizeof(arg), &p);
3717 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3718 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3719 filename, line_num, arg);
3722 } else if (!strcasecmp(cmd, "MaxClients")) {
3723 get_arg(arg, sizeof(arg), &p);
3725 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3726 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3727 filename, line_num, arg);
3730 nb_max_connections = val;
3732 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3733 get_arg(arg, sizeof(arg), &p);
3735 if (val < 10 || val > 100000) {
3736 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3737 filename, line_num, arg);
3740 max_bandwidth = val;
3742 } else if (!strcasecmp(cmd, "CustomLog")) {
3743 get_arg(logfilename, sizeof(logfilename), &p);
3744 } else if (!strcasecmp(cmd, "<Feed")) {
3745 /*********************************************/
3746 /* Feed related options */
3748 if (stream || feed) {
3749 fprintf(stderr, "%s:%d: Already in a tag\n",
3750 filename, line_num);
3752 feed = av_mallocz(sizeof(FFStream));
3753 /* add in stream list */
3754 *last_stream = feed;
3755 last_stream = &feed->next;
3756 /* add in feed list */
3758 last_feed = &feed->next_feed;
3760 get_arg(feed->filename, sizeof(feed->filename), &p);
3761 q = strrchr(feed->filename, '>');
3764 feed->fmt = guess_format("ffm", NULL, NULL);
3765 /* defaut feed file */
3766 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3767 "/tmp/%s.ffm", feed->filename);
3768 feed->feed_max_size = 5 * 1024 * 1024;
3770 feed->feed = feed; /* self feeding :-) */
3772 } else if (!strcasecmp(cmd, "Launch")) {
3776 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3778 feed->child_argv[0] = av_malloc(7);
3779 strcpy(feed->child_argv[0], "ffmpeg");
3781 for (i = 1; i < 62; i++) {
3784 get_arg(argbuf, sizeof(argbuf), &p);
3788 feed->child_argv[i] = av_malloc(strlen(argbuf + 1));
3789 strcpy(feed->child_argv[i], argbuf);
3792 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3794 snprintf(feed->child_argv[i], 256, "http://127.0.0.1:%d/%s",
3795 ntohs(my_http_addr.sin_port), feed->filename);
3797 } else if (!strcasecmp(cmd, "File")) {
3799 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3800 } else if (stream) {
3801 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3803 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3808 get_arg(arg, sizeof(arg), &p);
3810 fsize = strtod(p1, (char **)&p1);
3811 switch(toupper(*p1)) {
3816 fsize *= 1024 * 1024;
3819 fsize *= 1024 * 1024 * 1024;
3822 feed->feed_max_size = (int64_t)fsize;
3824 } else if (!strcasecmp(cmd, "</Feed>")) {
3826 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3827 filename, line_num);
3831 /* Make sure that we start out clean */
3832 if (unlink(feed->feed_filename) < 0
3833 && errno != ENOENT) {
3834 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3835 filename, line_num, feed->feed_filename, strerror(errno));
3841 } else if (!strcasecmp(cmd, "<Stream")) {
3842 /*********************************************/
3843 /* Stream related options */
3845 if (stream || feed) {
3846 fprintf(stderr, "%s:%d: Already in a tag\n",
3847 filename, line_num);
3849 stream = av_mallocz(sizeof(FFStream));
3850 *last_stream = stream;
3851 last_stream = &stream->next;
3853 get_arg(stream->filename, sizeof(stream->filename), &p);
3854 q = strrchr(stream->filename, '>');
3857 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3858 memset(&audio_enc, 0, sizeof(AVCodecContext));
3859 memset(&video_enc, 0, sizeof(AVCodecContext));
3860 audio_id = CODEC_ID_NONE;
3861 video_id = CODEC_ID_NONE;
3863 audio_id = stream->fmt->audio_codec;
3864 video_id = stream->fmt->video_codec;
3867 } else if (!strcasecmp(cmd, "Feed")) {
3868 get_arg(arg, sizeof(arg), &p);
3873 while (sfeed != NULL) {
3874 if (!strcmp(sfeed->filename, arg))
3876 sfeed = sfeed->next_feed;
3879 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3880 filename, line_num, arg);
3882 stream->feed = sfeed;
3885 } else if (!strcasecmp(cmd, "Format")) {
3886 get_arg(arg, sizeof(arg), &p);
3887 if (!strcmp(arg, "status")) {
3888 stream->stream_type = STREAM_TYPE_STATUS;
3891 stream->stream_type = STREAM_TYPE_LIVE;
3892 /* jpeg cannot be used here, so use single frame jpeg */
3893 if (!strcmp(arg, "jpeg"))
3894 strcpy(arg, "singlejpeg");
3895 stream->fmt = guess_stream_format(arg, NULL, NULL);
3897 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3898 filename, line_num, arg);
3903 audio_id = stream->fmt->audio_codec;
3904 video_id = stream->fmt->video_codec;
3906 } else if (!strcasecmp(cmd, "FaviconURL")) {
3907 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3908 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3910 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3911 filename, line_num);
3914 } else if (!strcasecmp(cmd, "Author")) {
3916 get_arg(stream->author, sizeof(stream->author), &p);
3918 } else if (!strcasecmp(cmd, "Comment")) {
3920 get_arg(stream->comment, sizeof(stream->comment), &p);
3922 } else if (!strcasecmp(cmd, "Copyright")) {
3924 get_arg(stream->copyright, sizeof(stream->copyright), &p);
3926 } else if (!strcasecmp(cmd, "Title")) {
3928 get_arg(stream->title, sizeof(stream->title), &p);
3930 } else if (!strcasecmp(cmd, "Preroll")) {
3931 get_arg(arg, sizeof(arg), &p);
3933 stream->prebuffer = atof(arg) * 1000;
3935 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
3937 stream->send_on_key = 1;
3939 } else if (!strcasecmp(cmd, "AudioCodec")) {
3940 get_arg(arg, sizeof(arg), &p);
3941 audio_id = opt_audio_codec(arg);
3942 if (audio_id == CODEC_ID_NONE) {
3943 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
3944 filename, line_num, arg);
3947 } else if (!strcasecmp(cmd, "VideoCodec")) {
3948 get_arg(arg, sizeof(arg), &p);
3949 video_id = opt_video_codec(arg);
3950 if (video_id == CODEC_ID_NONE) {
3951 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
3952 filename, line_num, arg);
3955 } else if (!strcasecmp(cmd, "MaxTime")) {
3956 get_arg(arg, sizeof(arg), &p);
3958 stream->max_time = atof(arg) * 1000;
3960 } else if (!strcasecmp(cmd, "AudioBitRate")) {
3961 get_arg(arg, sizeof(arg), &p);
3963 audio_enc.bit_rate = atoi(arg) * 1000;
3965 } else if (!strcasecmp(cmd, "AudioChannels")) {
3966 get_arg(arg, sizeof(arg), &p);
3968 audio_enc.channels = atoi(arg);
3970 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
3971 get_arg(arg, sizeof(arg), &p);
3973 audio_enc.sample_rate = atoi(arg);
3975 } else if (!strcasecmp(cmd, "AudioQuality")) {
3976 get_arg(arg, sizeof(arg), &p);
3978 // audio_enc.quality = atof(arg) * 1000;
3980 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
3982 int minrate, maxrate;
3984 get_arg(arg, sizeof(arg), &p);
3986 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
3987 video_enc.rc_min_rate = minrate * 1000;
3988 video_enc.rc_max_rate = maxrate * 1000;
3990 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
3991 filename, line_num, arg);
3995 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
3997 get_arg(arg, sizeof(arg), &p);
3998 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4000 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4001 get_arg(arg, sizeof(arg), &p);
4003 video_enc.bit_rate = atoi(arg) * 1000;
4005 } else if (!strcasecmp(cmd, "VideoSize")) {
4006 get_arg(arg, sizeof(arg), &p);
4008 parse_image_size(&video_enc.width, &video_enc.height, arg);
4009 if ((video_enc.width % 16) != 0 ||
4010 (video_enc.height % 16) != 0) {
4011 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4012 filename, line_num);
4016 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4017 get_arg(arg, sizeof(arg), &p);
4019 video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
4021 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4022 get_arg(arg, sizeof(arg), &p);
4024 video_enc.gop_size = atoi(arg);
4026 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4028 video_enc.gop_size = 1;
4030 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4032 video_enc.flags |= CODEC_FLAG_HQ;
4034 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4035 get_arg(arg, sizeof(arg), &p);
4037 video_enc.max_qdiff = atoi(arg);
4038 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4039 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4040 filename, line_num);
4044 } else if (!strcasecmp(cmd, "VideoQMax")) {
4045 get_arg(arg, sizeof(arg), &p);
4047 video_enc.qmax = atoi(arg);
4048 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4049 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4050 filename, line_num);
4054 } else if (!strcasecmp(cmd, "VideoQMin")) {
4055 get_arg(arg, sizeof(arg), &p);
4057 video_enc.qmin = atoi(arg);
4058 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4059 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4060 filename, line_num);
4064 } else if (!strcasecmp(cmd, "LumaElim")) {
4065 get_arg(arg, sizeof(arg), &p);
4067 video_enc.luma_elim_threshold = atoi(arg);
4069 } else if (!strcasecmp(cmd, "ChromaElim")) {
4070 get_arg(arg, sizeof(arg), &p);
4072 video_enc.chroma_elim_threshold = atoi(arg);
4074 } else if (!strcasecmp(cmd, "LumiMask")) {
4075 get_arg(arg, sizeof(arg), &p);
4077 video_enc.lumi_masking = atof(arg);
4079 } else if (!strcasecmp(cmd, "DarkMask")) {
4080 get_arg(arg, sizeof(arg), &p);
4082 video_enc.dark_masking = atof(arg);
4084 } else if (!strcasecmp(cmd, "NoVideo")) {
4085 video_id = CODEC_ID_NONE;
4086 } else if (!strcasecmp(cmd, "NoAudio")) {
4087 audio_id = CODEC_ID_NONE;
4088 } else if (!strcasecmp(cmd, "ACL")) {
4092 get_arg(arg, sizeof(arg), &p);
4093 if (strcasecmp(arg, "allow") == 0) {
4094 acl.action = IP_ALLOW;
4095 } else if (strcasecmp(arg, "deny") == 0) {
4096 acl.action = IP_DENY;
4098 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4099 filename, line_num, arg);
4103 get_arg(arg, sizeof(arg), &p);
4105 he = gethostbyname(arg);
4107 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4108 filename, line_num, arg);
4111 /* Only take the first */
4112 acl.first.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4113 acl.last = acl.first;
4116 get_arg(arg, sizeof(arg), &p);
4119 he = gethostbyname(arg);
4121 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4122 filename, line_num, arg);
4125 /* Only take the first */
4126 acl.last.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4131 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4132 IPAddressACL **naclp = 0;
4138 naclp = &stream->acl;
4142 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4143 filename, line_num);
4149 naclp = &(*naclp)->next;
4154 } else if (!strcasecmp(cmd, "RTSPOption")) {
4155 get_arg(arg, sizeof(arg), &p);
4157 av_freep(&stream->rtsp_option);
4158 /* XXX: av_strdup ? */
4159 stream->rtsp_option = av_malloc(strlen(arg) + 1);
4160 if (stream->rtsp_option) {
4161 strcpy(stream->rtsp_option, arg);
4164 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4165 get_arg(arg, sizeof(arg), &p);
4167 if (!inet_aton(arg, &stream->multicast_ip)) {
4168 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
4169 filename, line_num, arg);
4172 stream->is_multicast = 1;
4173 stream->loop = 1; /* default is looping */
4175 } else if (!strcasecmp(cmd, "MulticastPort")) {
4176 get_arg(arg, sizeof(arg), &p);
4178 stream->multicast_port = atoi(arg);
4180 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4181 get_arg(arg, sizeof(arg), &p);
4183 stream->multicast_ttl = atoi(arg);
4185 } else if (!strcasecmp(cmd, "NoLoop")) {
4189 } else if (!strcasecmp(cmd, "</Stream>")) {
4191 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4192 filename, line_num);
4195 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4196 if (audio_id != CODEC_ID_NONE) {
4197 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4198 audio_enc.codec_id = audio_id;
4199 add_codec(stream, &audio_enc);
4201 if (video_id != CODEC_ID_NONE) {
4202 video_enc.codec_type = CODEC_TYPE_VIDEO;
4203 video_enc.codec_id = video_id;
4204 add_codec(stream, &video_enc);
4208 } else if (!strcasecmp(cmd, "<Redirect")) {
4209 /*********************************************/
4211 if (stream || feed || redirect) {
4212 fprintf(stderr, "%s:%d: Already in a tag\n",
4213 filename, line_num);
4216 redirect = av_mallocz(sizeof(FFStream));
4217 *last_stream = redirect;
4218 last_stream = &redirect->next;
4220 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4221 q = strrchr(redirect->filename, '>');
4224 redirect->stream_type = STREAM_TYPE_REDIRECT;
4226 } else if (!strcasecmp(cmd, "URL")) {
4228 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4230 } else if (!strcasecmp(cmd, "</Redirect>")) {
4232 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4233 filename, line_num);
4236 if (!redirect->feed_filename[0]) {
4237 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4238 filename, line_num);
4242 } else if (!strcasecmp(cmd, "LoadModule")) {
4243 get_arg(arg, sizeof(arg), &p);
4244 #ifdef CONFIG_HAVE_DLOPEN
4247 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4248 filename, line_num, arg);
4252 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4253 filename, line_num, cmd);
4267 static void write_packet(FFCodec *ffenc,
4268 uint8_t *buf, int size)
4271 AVCodecContext *enc = &ffenc->enc;
4273 mk_header(&hdr, enc, size);
4274 wptr = http_fifo.wptr;
4275 fifo_write(&http_fifo, (uint8_t *)&hdr, sizeof(hdr), &wptr);
4276 fifo_write(&http_fifo, buf, size, &wptr);
4277 /* atomic modification of wptr */
4278 http_fifo.wptr = wptr;
4279 ffenc->data_count += size;
4280 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4284 static void help(void)
4286 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4287 "usage: ffserver [-L] [-h] [-f configfile]\n"
4288 "Hyper fast multi format Audio/Video streaming server\n"
4290 "-L : print the LICENCE\n"
4292 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4296 static void licence(void)
4299 "ffserver version " FFMPEG_VERSION "\n"
4300 "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4301 "This library is free software; you can redistribute it and/or\n"
4302 "modify it under the terms of the GNU Lesser General Public\n"
4303 "License as published by the Free Software Foundation; either\n"
4304 "version 2 of the License, or (at your option) any later version.\n"
4306 "This library is distributed in the hope that it will be useful,\n"
4307 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4308 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4309 "Lesser General Public License for more details.\n"
4311 "You should have received a copy of the GNU Lesser General Public\n"
4312 "License along with this library; if not, write to the Free Software\n"
4313 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
4317 static void handle_child_exit(int sig)
4322 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4325 for (feed = first_feed; feed; feed = feed->next) {
4326 if (feed->pid == pid) {
4327 int uptime = time(0) - feed->pid_start;
4330 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4333 /* Turn off any more restarts */
4334 feed->child_argv = 0;
4340 need_to_start_children = 1;
4343 int main(int argc, char **argv)
4345 const char *config_filename;
4347 struct sigaction sigact;
4351 config_filename = "/etc/ffserver.conf";
4353 my_program_name = argv[0];
4354 my_program_dir = getcwd(0, 0);
4355 ffserver_daemon = 1;
4358 c = getopt(argc, argv, "ndLh?f:");
4374 ffserver_daemon = 0;
4377 config_filename = optarg;
4384 putenv("http_proxy"); /* Kill the http_proxy */
4386 srandom(gettime_ms() + (getpid() << 16));
4388 /* address on which the server will handle HTTP connections */
4389 my_http_addr.sin_family = AF_INET;
4390 my_http_addr.sin_port = htons (8080);
4391 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4393 /* address on which the server will handle RTSP connections */
4394 my_rtsp_addr.sin_family = AF_INET;
4395 my_rtsp_addr.sin_port = htons (5454);
4396 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4398 nb_max_connections = 5;
4399 max_bandwidth = 1000;
4400 first_stream = NULL;
4401 logfilename[0] = '\0';
4403 memset(&sigact, 0, sizeof(sigact));
4404 sigact.sa_handler = handle_child_exit;
4405 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4406 sigaction(SIGCHLD, &sigact, 0);
4408 if (parse_ffconfig(config_filename) < 0) {
4409 fprintf(stderr, "Incorrect config file - exiting.\n");
4413 build_file_streams();
4415 build_feed_streams();
4417 compute_bandwidth();
4419 /* put the process in background and detach it from its TTY */
4420 if (ffserver_daemon) {
4427 } else if (pid > 0) {
4435 open("/dev/null", O_RDWR);
4436 if (strcmp(logfilename, "-") != 0) {
4446 signal(SIGPIPE, SIG_IGN);
4448 /* open log file if needed */
4449 if (logfilename[0] != '\0') {
4450 if (!strcmp(logfilename, "-"))
4453 logfile = fopen(logfilename, "w");
4456 if (http_server() < 0) {
4457 fprintf(stderr, "Could not start server\n");