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
26 #include <sys/ioctl.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
39 #ifdef CONFIG_HAVE_DLFCN
45 /* maximum number of simultaneous HTTP connections */
46 #define HTTP_MAX_CONNECTIONS 2000
49 HTTPSTATE_WAIT_REQUEST,
50 HTTPSTATE_SEND_HEADER,
51 HTTPSTATE_SEND_DATA_HEADER,
52 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
53 HTTPSTATE_SEND_DATA_TRAILER,
54 HTTPSTATE_RECEIVE_DATA,
55 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
56 HTTPSTATE_WAIT, /* wait before sending next packets */
57 HTTPSTATE_WAIT_SHORT, /* short wait for short term
58 bandwidth limitation */
61 RTSPSTATE_WAIT_REQUEST,
65 const char *http_state[] = {
82 #define IOBUFFER_INIT_SIZE 8192
84 /* coef for exponential mean for bitrate estimation in statistics */
87 /* timeouts are in ms */
88 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
89 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
91 #define SYNC_TIMEOUT (10 * 1000)
94 int64_t count1, count2;
98 /* context associated with one connection */
99 typedef struct HTTPContext {
100 enum HTTPState state;
101 int fd; /* socket file descriptor */
102 struct sockaddr_in from_addr; /* origin */
103 struct pollfd *poll_entry; /* used when polling */
105 uint8_t *buffer_ptr, *buffer_end;
107 struct HTTPContext *next;
108 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
112 /* input format handling */
113 AVFormatContext *fmt_in;
114 long start_time; /* In milliseconds - this wraps fairly often */
115 int64_t first_pts; /* initial pts value */
116 int pts_stream_index; /* stream we choose as clock reference */
117 /* output format handling */
118 struct FFStream *stream;
119 /* -1 is invalid stream */
120 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
121 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
123 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
124 int last_packet_sent; /* true if last data packet was sent */
126 DataRateData datarate;
133 int is_packetized; /* if true, the stream is packetized */
134 int packet_stream_index; /* current stream for output in state machine */
136 /* RTSP state specific */
137 uint8_t *pb_buffer; /* XXX: use that in all the code */
139 int seq; /* RTSP sequence number */
141 /* RTP state specific */
142 enum RTSPProtocol rtp_protocol;
143 char session_id[32]; /* session id */
144 AVFormatContext *rtp_ctx[MAX_STREAMS];
145 URLContext *rtp_handles[MAX_STREAMS];
146 /* RTP short term bandwidth limitation */
147 int packet_byte_count;
148 int packet_start_time_us; /* used for short durations (a few
152 static AVFrame dummy_frame;
154 /* each generated stream is described here */
158 STREAM_TYPE_REDIRECT,
161 enum IPAddressAction {
166 typedef struct IPAddressACL {
167 struct IPAddressACL *next;
168 enum IPAddressAction action;
169 /* These are in host order */
170 struct in_addr first;
174 /* description of each stream of the ffserver.conf file */
175 typedef struct FFStream {
176 enum StreamType stream_type;
177 char filename[1024]; /* stream filename */
178 struct FFStream *feed; /* feed we are using (can be null if
183 int prebuffer; /* Number of millseconds early to start */
184 long max_time; /* Number of milliseconds to run */
186 AVStream *streams[MAX_STREAMS];
187 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
188 char feed_filename[1024]; /* file name of the feed storage, or
189 input file name for a stream */
194 pid_t pid; /* Of ffmpeg process */
195 time_t pid_start; /* Of ffmpeg process */
197 struct FFStream *next;
198 int bandwidth; /* bandwidth, in kbits/s */
201 /* multicast specific */
203 struct in_addr multicast_ip;
204 int multicast_port; /* first port used for multicast */
206 int loop; /* if true, send the stream in loops (only meaningful if file) */
209 int feed_opened; /* true if someone is writing to the feed */
210 int is_feed; /* true if it is a feed */
212 int64_t bytes_served;
213 int64_t feed_max_size; /* maximum storage size */
214 int64_t feed_write_index; /* current write position in feed (it wraps round) */
215 int64_t feed_size; /* current size of feed */
216 struct FFStream *next_feed;
219 typedef struct FeedData {
220 long long data_count;
221 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
224 struct sockaddr_in my_http_addr;
225 struct sockaddr_in my_rtsp_addr;
227 char logfilename[1024];
228 HTTPContext *first_http_ctx;
229 FFStream *first_feed; /* contains only feeds */
230 FFStream *first_stream; /* contains all streams, including feeds */
232 static void new_connection(int server_fd, int is_rtsp);
233 static void close_connection(HTTPContext *c);
236 static int handle_connection(HTTPContext *c);
237 static int http_parse_request(HTTPContext *c);
238 static int http_send_data(HTTPContext *c);
239 static void compute_stats(HTTPContext *c);
240 static int open_input_stream(HTTPContext *c, const char *info);
241 static int http_start_receive_data(HTTPContext *c);
242 static int http_receive_data(HTTPContext *c);
243 static int compute_send_delay(HTTPContext *c);
246 static int rtsp_parse_request(HTTPContext *c);
247 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
248 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
249 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
250 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
251 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
254 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
255 struct in_addr my_ip);
258 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
259 FFStream *stream, const char *session_id);
260 static int rtp_new_av_stream(HTTPContext *c,
261 int stream_index, struct sockaddr_in *dest_addr);
263 static const char *my_program_name;
264 static const char *my_program_dir;
266 static int ffserver_debug;
267 static int ffserver_daemon;
268 static int no_launch;
269 static int need_to_start_children;
271 int nb_max_connections;
275 int current_bandwidth;
277 static long cur_time; // Making this global saves on passing it around everywhere
279 static long gettime_ms(void)
283 gettimeofday(&tv,NULL);
284 return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
287 static FILE *logfile = NULL;
289 static void http_log(const char *fmt, ...)
295 vfprintf(logfile, fmt, ap);
301 static char *ctime1(char *buf2)
309 p = buf2 + strlen(p) - 1;
315 static void log_connection(HTTPContext *c)
322 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
323 inet_ntoa(c->from_addr.sin_addr),
324 ctime1(buf2), c->method, c->url,
325 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
328 static void update_datarate(DataRateData *drd, int64_t count)
330 if (!drd->time1 && !drd->count1) {
331 drd->time1 = drd->time2 = cur_time;
332 drd->count1 = drd->count2 = count;
334 if (cur_time - drd->time2 > 5000) {
335 drd->time1 = drd->time2;
336 drd->count1 = drd->count2;
337 drd->time2 = cur_time;
343 /* In bytes per second */
344 static int compute_datarate(DataRateData *drd, int64_t count)
346 if (cur_time == drd->time1)
349 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
352 static int get_longterm_datarate(DataRateData *drd, int64_t count)
354 /* You get the first 3 seconds flat out */
355 if (cur_time - drd->time1 < 3000)
357 return compute_datarate(drd, count);
361 static void start_children(FFStream *feed)
366 for (; feed; feed = feed->next) {
367 if (feed->child_argv && !feed->pid) {
368 feed->pid_start = time(0);
373 fprintf(stderr, "Unable to create children\n");
382 for (i = 3; i < 256; i++) {
386 if (!ffserver_debug) {
387 i = open("/dev/null", O_RDWR);
396 pstrcpy(pathname, sizeof(pathname), my_program_name);
398 slash = strrchr(pathname, '/');
404 strcpy(slash, "ffmpeg");
406 /* This is needed to make relative pathnames work */
407 chdir(my_program_dir);
409 signal(SIGPIPE, SIG_DFL);
411 execvp(pathname, feed->child_argv);
419 /* open a listening socket */
420 static int socket_open_listen(struct sockaddr_in *my_addr)
424 server_fd = socket(AF_INET,SOCK_STREAM,0);
431 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
433 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
435 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
441 if (listen (server_fd, 5) < 0) {
446 fcntl(server_fd, F_SETFL, O_NONBLOCK);
451 /* start all multicast streams */
452 static void start_multicast(void)
457 struct sockaddr_in dest_addr;
458 int default_port, stream_index;
461 for(stream = first_stream; stream != NULL; stream = stream->next) {
462 if (stream->is_multicast) {
463 /* open the RTP connection */
464 snprintf(session_id, sizeof(session_id),
465 "%08x%08x", (int)random(), (int)random());
467 /* choose a port if none given */
468 if (stream->multicast_port == 0) {
469 stream->multicast_port = default_port;
473 dest_addr.sin_family = AF_INET;
474 dest_addr.sin_addr = stream->multicast_ip;
475 dest_addr.sin_port = htons(stream->multicast_port);
477 rtp_c = rtp_new_connection(&dest_addr, stream, session_id);
481 if (open_input_stream(rtp_c, "") < 0) {
482 fprintf(stderr, "Could not open input stream for stream '%s'\n",
487 rtp_c->rtp_protocol = RTSP_PROTOCOL_RTP_UDP_MULTICAST;
489 /* open each RTP stream */
490 for(stream_index = 0; stream_index < stream->nb_streams;
492 dest_addr.sin_port = htons(stream->multicast_port +
494 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
495 fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
496 stream->filename, stream_index);
501 /* change state to send data */
502 rtp_c->state = HTTPSTATE_SEND_DATA;
507 /* main loop of the http server */
508 static int http_server(void)
510 int server_fd, ret, rtsp_server_fd, delay, delay1;
511 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
512 HTTPContext *c, *c_next;
514 server_fd = socket_open_listen(&my_http_addr);
518 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
519 if (rtsp_server_fd < 0)
522 http_log("ffserver started.\n");
524 start_children(first_feed);
526 first_http_ctx = NULL;
528 first_http_ctx = NULL;
533 poll_entry = poll_table;
534 poll_entry->fd = server_fd;
535 poll_entry->events = POLLIN;
538 poll_entry->fd = rtsp_server_fd;
539 poll_entry->events = POLLIN;
542 /* wait for events on each HTTP handle */
549 case HTTPSTATE_SEND_HEADER:
550 case RTSPSTATE_SEND_REPLY:
551 c->poll_entry = poll_entry;
553 poll_entry->events = POLLOUT;
556 case HTTPSTATE_SEND_DATA_HEADER:
557 case HTTPSTATE_SEND_DATA:
558 case HTTPSTATE_SEND_DATA_TRAILER:
559 if (!c->is_packetized) {
560 /* for TCP, we output as much as we can (may need to put a limit) */
561 c->poll_entry = poll_entry;
563 poll_entry->events = POLLOUT;
566 /* not strictly correct, but currently cannot add
567 more than one fd in poll entry */
571 case HTTPSTATE_WAIT_REQUEST:
572 case HTTPSTATE_RECEIVE_DATA:
573 case HTTPSTATE_WAIT_FEED:
574 case RTSPSTATE_WAIT_REQUEST:
575 /* need to catch errors */
576 c->poll_entry = poll_entry;
578 poll_entry->events = POLLIN;/* Maybe this will work */
582 c->poll_entry = NULL;
583 delay1 = compute_send_delay(c);
587 case HTTPSTATE_WAIT_SHORT:
588 c->poll_entry = NULL;
589 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
594 c->poll_entry = NULL;
600 /* wait for an event on one connection. We poll at least every
601 second to handle timeouts */
603 ret = poll(poll_table, poll_entry - poll_table, delay);
606 cur_time = gettime_ms();
608 if (need_to_start_children) {
609 need_to_start_children = 0;
610 start_children(first_feed);
613 /* now handle the events */
614 for(c = first_http_ctx; c != NULL; c = c_next) {
616 if (handle_connection(c) < 0) {
617 /* close and free the connection */
623 poll_entry = poll_table;
624 /* new HTTP connection request ? */
625 if (poll_entry->revents & POLLIN) {
626 new_connection(server_fd, 0);
629 /* new RTSP connection request ? */
630 if (poll_entry->revents & POLLIN) {
631 new_connection(rtsp_server_fd, 1);
636 /* start waiting for a new HTTP/RTSP request */
637 static void start_wait_request(HTTPContext *c, int is_rtsp)
639 c->buffer_ptr = c->buffer;
640 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
643 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
644 c->state = RTSPSTATE_WAIT_REQUEST;
646 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
647 c->state = HTTPSTATE_WAIT_REQUEST;
651 static void new_connection(int server_fd, int is_rtsp)
653 struct sockaddr_in from_addr;
655 HTTPContext *c = NULL;
657 len = sizeof(from_addr);
658 fd = accept(server_fd, (struct sockaddr *)&from_addr,
662 fcntl(fd, F_SETFL, O_NONBLOCK);
664 /* XXX: should output a warning page when coming
665 close to the connection limit */
666 if (nb_connections >= nb_max_connections)
669 /* add a new connection */
670 c = av_mallocz(sizeof(HTTPContext));
674 c->next = first_http_ctx;
677 c->poll_entry = NULL;
678 c->from_addr = from_addr;
679 c->buffer_size = IOBUFFER_INIT_SIZE;
680 c->buffer = av_malloc(c->buffer_size);
685 start_wait_request(c, is_rtsp);
697 static void close_connection(HTTPContext *c)
699 HTTPContext **cp, *c1;
701 AVFormatContext *ctx;
705 /* remove connection from list */
706 cp = &first_http_ctx;
707 while ((*cp) != NULL) {
716 /* remove connection associated resources */
720 /* close each frame parser */
721 for(i=0;i<c->fmt_in->nb_streams;i++) {
722 st = c->fmt_in->streams[i];
723 if (st->codec.codec) {
724 avcodec_close(&st->codec);
727 av_close_input_file(c->fmt_in);
730 /* free RTP output streams if any */
733 nb_streams = c->stream->nb_streams;
735 for(i=0;i<nb_streams;i++) {
738 av_write_trailer(ctx);
741 h = c->rtp_handles[i];
747 if (!c->last_packet_sent) {
751 if (url_open_dyn_buf(&ctx->pb) >= 0) {
752 av_write_trailer(ctx);
753 (void) url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
759 current_bandwidth -= c->stream->bandwidth;
760 av_freep(&c->pb_buffer);
766 static int handle_connection(HTTPContext *c)
771 case HTTPSTATE_WAIT_REQUEST:
772 case RTSPSTATE_WAIT_REQUEST:
774 if ((c->timeout - cur_time) < 0)
776 if (c->poll_entry->revents & (POLLERR | POLLHUP))
779 /* no need to read if no events */
780 if (!(c->poll_entry->revents & POLLIN))
783 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
785 if (errno != EAGAIN && errno != EINTR)
787 } else if (len == 0) {
790 /* search for end of request. XXX: not fully correct since garbage could come after the end */
792 c->buffer_ptr += len;
794 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
795 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
796 /* request found : parse it and reply */
797 if (c->state == HTTPSTATE_WAIT_REQUEST) {
798 ret = http_parse_request(c);
800 ret = rtsp_parse_request(c);
804 } else if (ptr >= c->buffer_end) {
805 /* request too long: cannot do anything */
811 case HTTPSTATE_SEND_HEADER:
812 if (c->poll_entry->revents & (POLLERR | POLLHUP))
815 /* no need to write if no events */
816 if (!(c->poll_entry->revents & POLLOUT))
818 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
820 if (errno != EAGAIN && errno != EINTR) {
821 /* error : close connection */
822 av_freep(&c->pb_buffer);
826 c->buffer_ptr += len;
828 c->stream->bytes_served += len;
829 c->data_count += len;
830 if (c->buffer_ptr >= c->buffer_end) {
831 av_freep(&c->pb_buffer);
836 /* all the buffer was sent : synchronize to the incoming stream */
837 c->state = HTTPSTATE_SEND_DATA_HEADER;
838 c->buffer_ptr = c->buffer_end = c->buffer;
843 case HTTPSTATE_SEND_DATA:
844 case HTTPSTATE_SEND_DATA_HEADER:
845 case HTTPSTATE_SEND_DATA_TRAILER:
846 /* for packetized output, we consider we can always write (the
847 input streams sets the speed). It may be better to verify
848 that we do not rely too much on the kernel queues */
849 if (!c->is_packetized) {
850 if (c->poll_entry->revents & (POLLERR | POLLHUP))
853 /* no need to read if no events */
854 if (!(c->poll_entry->revents & POLLOUT))
857 if (http_send_data(c) < 0)
860 case HTTPSTATE_RECEIVE_DATA:
861 /* no need to read if no events */
862 if (c->poll_entry->revents & (POLLERR | POLLHUP))
864 if (!(c->poll_entry->revents & POLLIN))
866 if (http_receive_data(c) < 0)
869 case HTTPSTATE_WAIT_FEED:
870 /* no need to read if no events */
871 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
874 /* nothing to do, we'll be waken up by incoming feed packets */
878 /* if the delay expired, we can send new packets */
879 if (compute_send_delay(c) <= 0)
880 c->state = HTTPSTATE_SEND_DATA;
882 case HTTPSTATE_WAIT_SHORT:
883 /* just return back to send data */
884 c->state = HTTPSTATE_SEND_DATA;
887 case RTSPSTATE_SEND_REPLY:
888 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
889 av_freep(&c->pb_buffer);
892 /* no need to write if no events */
893 if (!(c->poll_entry->revents & POLLOUT))
895 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
897 if (errno != EAGAIN && errno != EINTR) {
898 /* error : close connection */
899 av_freep(&c->pb_buffer);
903 c->buffer_ptr += len;
904 c->data_count += len;
905 if (c->buffer_ptr >= c->buffer_end) {
906 /* all the buffer was sent : wait for a new request */
907 av_freep(&c->pb_buffer);
908 start_wait_request(c, 1);
912 case HTTPSTATE_READY:
921 static int extract_rates(char *rates, int ratelen, const char *request)
925 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
926 if (strncasecmp(p, "Pragma:", 7) == 0) {
927 const char *q = p + 7;
929 while (*q && *q != '\n' && isspace(*q))
932 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
938 memset(rates, 0xff, ratelen);
941 while (*q && *q != '\n' && *q != ':')
944 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
948 if (stream_no < ratelen && stream_no >= 0) {
949 rates[stream_no] = rate_no;
952 while (*q && *q != '\n' && !isspace(*q))
969 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
972 int best_bitrate = 100000000;
975 for (i = 0; i < feed->nb_streams; i++) {
976 AVCodecContext *feed_codec = &feed->streams[i]->codec;
978 if (feed_codec->codec_id != codec->codec_id ||
979 feed_codec->sample_rate != codec->sample_rate ||
980 feed_codec->width != codec->width ||
981 feed_codec->height != codec->height) {
985 /* Potential stream */
987 /* We want the fastest stream less than bit_rate, or the slowest
988 * faster than bit_rate
991 if (feed_codec->bit_rate <= bit_rate) {
992 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
993 best_bitrate = feed_codec->bit_rate;
997 if (feed_codec->bit_rate < best_bitrate) {
998 best_bitrate = feed_codec->bit_rate;
1007 static int modify_current_stream(HTTPContext *c, char *rates)
1010 FFStream *req = c->stream;
1011 int action_required = 0;
1013 /* Not much we can do for a feed */
1017 for (i = 0; i < req->nb_streams; i++) {
1018 AVCodecContext *codec = &req->streams[i]->codec;
1022 c->switch_feed_streams[i] = req->feed_streams[i];
1025 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1028 /* Wants off or slow */
1029 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1031 /* This doesn't work well when it turns off the only stream! */
1032 c->switch_feed_streams[i] = -2;
1033 c->feed_streams[i] = -2;
1038 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1039 action_required = 1;
1042 return action_required;
1046 static void do_switch_stream(HTTPContext *c, int i)
1048 if (c->switch_feed_streams[i] >= 0) {
1050 c->feed_streams[i] = c->switch_feed_streams[i];
1053 /* Now update the stream */
1055 c->switch_feed_streams[i] = -1;
1058 /* XXX: factorize in utils.c ? */
1059 /* XXX: take care with different space meaning */
1060 static void skip_spaces(const char **pp)
1064 while (*p == ' ' || *p == '\t')
1069 static void get_word(char *buf, int buf_size, const char **pp)
1077 while (!isspace(*p) && *p != '\0') {
1078 if ((q - buf) < buf_size - 1)
1087 static int validate_acl(FFStream *stream, HTTPContext *c)
1089 enum IPAddressAction last_action = IP_DENY;
1091 struct in_addr *src = &c->from_addr.sin_addr;
1092 unsigned long src_addr = ntohl(src->s_addr);
1094 for (acl = stream->acl; acl; acl = acl->next) {
1095 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr) {
1096 return (acl->action == IP_ALLOW) ? 1 : 0;
1098 last_action = acl->action;
1101 /* Nothing matched, so return not the last action */
1102 return (last_action == IP_DENY) ? 1 : 0;
1105 /* compute the real filename of a file by matching it without its
1106 extensions to all the stream filenames */
1107 static void compute_real_filename(char *filename, int max_size)
1114 /* compute filename by matching without the file extensions */
1115 pstrcpy(file1, sizeof(file1), filename);
1116 p = strrchr(file1, '.');
1119 for(stream = first_stream; stream != NULL; stream = stream->next) {
1120 pstrcpy(file2, sizeof(file2), stream->filename);
1121 p = strrchr(file2, '.');
1124 if (!strcmp(file1, file2)) {
1125 pstrcpy(filename, max_size, stream->filename);
1140 /* parse http request and prepare header */
1141 static int http_parse_request(HTTPContext *c)
1145 enum RedirType redir_type;
1147 char info[1024], *filename;
1151 const char *mime_type;
1155 char *useragent = 0;
1158 get_word(cmd, sizeof(cmd), (const char **)&p);
1159 pstrcpy(c->method, sizeof(c->method), cmd);
1161 if (!strcmp(cmd, "GET"))
1163 else if (!strcmp(cmd, "POST"))
1168 get_word(url, sizeof(url), (const char **)&p);
1169 pstrcpy(c->url, sizeof(c->url), url);
1171 get_word(protocol, sizeof(protocol), (const char **)&p);
1172 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1175 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
1177 /* find the filename and the optional info string in the request */
1184 pstrcpy(info, sizeof(info), p);
1190 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1191 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1193 if (*useragent && *useragent != '\n' && isspace(*useragent))
1197 p = strchr(p, '\n');
1204 redir_type = REDIR_NONE;
1205 if (match_ext(filename, "asx")) {
1206 redir_type = REDIR_ASX;
1207 filename[strlen(filename)-1] = 'f';
1208 } else if (match_ext(filename, "asf") &&
1209 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1210 /* if this isn't WMP or lookalike, return the redirector file */
1211 redir_type = REDIR_ASF;
1212 } else if (match_ext(filename, "rpm,ram")) {
1213 redir_type = REDIR_RAM;
1214 strcpy(filename + strlen(filename)-2, "m");
1215 } else if (match_ext(filename, "rtsp")) {
1216 redir_type = REDIR_RTSP;
1217 compute_real_filename(filename, sizeof(url) - 1);
1218 } else if (match_ext(filename, "sdp")) {
1219 redir_type = REDIR_SDP;
1220 compute_real_filename(filename, sizeof(url) - 1);
1223 stream = first_stream;
1224 while (stream != NULL) {
1225 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1227 stream = stream->next;
1229 if (stream == NULL) {
1230 sprintf(msg, "File '%s' not found", url);
1235 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1236 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1238 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1239 c->http_error = 301;
1241 q += sprintf(q, "HTTP/1.0 301 Moved\r\n");
1242 q += sprintf(q, "Location: %s\r\n", stream->feed_filename);
1243 q += sprintf(q, "Content-type: text/html\r\n");
1244 q += sprintf(q, "\r\n");
1245 q += sprintf(q, "<html><head><title>Moved</title></head><body>\r\n");
1246 q += sprintf(q, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1247 q += sprintf(q, "</body></html>\r\n");
1249 /* prepare output buffer */
1250 c->buffer_ptr = c->buffer;
1252 c->state = HTTPSTATE_SEND_HEADER;
1256 /* If this is WMP, get the rate information */
1257 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1258 if (modify_current_stream(c, ratebuf)) {
1259 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1260 if (c->switch_feed_streams[i] >= 0)
1261 do_switch_stream(c, i);
1266 if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1267 current_bandwidth += stream->bandwidth;
1270 if (post == 0 && max_bandwidth < current_bandwidth) {
1271 c->http_error = 200;
1273 q += sprintf(q, "HTTP/1.0 200 Server too busy\r\n");
1274 q += sprintf(q, "Content-type: text/html\r\n");
1275 q += sprintf(q, "\r\n");
1276 q += sprintf(q, "<html><head><title>Too busy</title></head><body>\r\n");
1277 q += sprintf(q, "The server is too busy to serve your request at this time.<p>\r\n");
1278 q += sprintf(q, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
1279 current_bandwidth, max_bandwidth);
1280 q += sprintf(q, "</body></html>\r\n");
1282 /* prepare output buffer */
1283 c->buffer_ptr = c->buffer;
1285 c->state = HTTPSTATE_SEND_HEADER;
1289 if (redir_type != REDIR_NONE) {
1292 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1293 if (strncasecmp(p, "Host:", 5) == 0) {
1297 p = strchr(p, '\n');
1308 while (isspace(*hostinfo))
1311 eoh = strchr(hostinfo, '\n');
1313 if (eoh[-1] == '\r')
1316 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1317 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1318 hostbuf[eoh - hostinfo] = 0;
1320 c->http_error = 200;
1322 switch(redir_type) {
1324 q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n");
1325 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1326 q += sprintf(q, "\r\n");
1327 q += sprintf(q, "<ASX Version=\"3\">\r\n");
1328 q += sprintf(q, "<!-- Autogenerated by ffserver -->\r\n");
1329 q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1330 hostbuf, filename, info);
1331 q += sprintf(q, "</ASX>\r\n");
1334 q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n");
1335 q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n");
1336 q += sprintf(q, "\r\n");
1337 q += sprintf(q, "# Autogenerated by ffserver\r\n");
1338 q += sprintf(q, "http://%s/%s%s\r\n",
1339 hostbuf, filename, info);
1342 q += sprintf(q, "HTTP/1.0 200 ASF Redirect follows\r\n");
1343 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1344 q += sprintf(q, "\r\n");
1345 q += sprintf(q, "[Reference]\r\n");
1346 q += sprintf(q, "Ref1=http://%s/%s%s\r\n",
1347 hostbuf, filename, info);
1351 char hostname[256], *p;
1352 /* extract only hostname */
1353 pstrcpy(hostname, sizeof(hostname), hostbuf);
1354 p = strrchr(hostname, ':');
1357 q += sprintf(q, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1358 /* XXX: incorrect mime type ? */
1359 q += sprintf(q, "Content-type: application/x-rtsp\r\n");
1360 q += sprintf(q, "\r\n");
1361 q += sprintf(q, "rtsp://%s:%d/%s\r\n",
1362 hostname, ntohs(my_rtsp_addr.sin_port),
1369 int sdp_data_size, len;
1370 struct sockaddr_in my_addr;
1372 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1373 q += sprintf(q, "Content-type: application/sdp\r\n");
1374 q += sprintf(q, "\r\n");
1376 len = sizeof(my_addr);
1377 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1379 /* XXX: should use a dynamic buffer */
1380 sdp_data_size = prepare_sdp_description(stream,
1383 if (sdp_data_size > 0) {
1384 memcpy(q, sdp_data, sdp_data_size);
1396 /* prepare output buffer */
1397 c->buffer_ptr = c->buffer;
1399 c->state = HTTPSTATE_SEND_HEADER;
1405 sprintf(msg, "ASX/RAM file not handled");
1409 stream->conns_served++;
1411 /* XXX: add there authenticate and IP match */
1414 /* if post, it means a feed is being sent */
1415 if (!stream->is_feed) {
1416 /* However it might be a status report from WMP! Lets log the data
1417 * as it might come in handy one day
1422 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1423 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1427 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1428 client_id = strtol(p + 18, 0, 10);
1430 p = strchr(p, '\n');
1438 char *eol = strchr(logline, '\n');
1443 if (eol[-1] == '\r')
1445 http_log("%.*s\n", eol - logline, logline);
1446 c->suppress_log = 1;
1451 http_log("\nGot request:\n%s\n", c->buffer);
1454 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1457 /* Now we have to find the client_id */
1458 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1459 if (wmpc->wmp_client_id == client_id)
1464 if (modify_current_stream(wmpc, ratebuf)) {
1465 wmpc->switch_pending = 1;
1470 sprintf(msg, "POST command not handled");
1474 if (http_start_receive_data(c) < 0) {
1475 sprintf(msg, "could not open feed");
1479 c->state = HTTPSTATE_RECEIVE_DATA;
1484 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1485 http_log("\nGot request:\n%s\n", c->buffer);
1489 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1492 /* open input stream */
1493 if (open_input_stream(c, info) < 0) {
1494 sprintf(msg, "Input stream corresponding to '%s' not found", url);
1498 /* prepare http header */
1500 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1501 mime_type = c->stream->fmt->mime_type;
1503 mime_type = "application/x-octet_stream";
1504 q += sprintf(q, "Pragma: no-cache\r\n");
1506 /* for asf, we need extra headers */
1507 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1508 /* Need to allocate a client id */
1510 c->wmp_client_id = random() & 0x7fffffff;
1512 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);
1514 q += sprintf(q, "Content-Type: %s\r\n", mime_type);
1515 q += sprintf(q, "\r\n");
1517 /* prepare output buffer */
1519 c->buffer_ptr = c->buffer;
1521 c->state = HTTPSTATE_SEND_HEADER;
1524 c->http_error = 404;
1526 q += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
1527 q += sprintf(q, "Content-type: %s\r\n", "text/html");
1528 q += sprintf(q, "\r\n");
1529 q += sprintf(q, "<HTML>\n");
1530 q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1531 q += sprintf(q, "<BODY>%s</BODY>\n", msg);
1532 q += sprintf(q, "</HTML>\n");
1534 /* prepare output buffer */
1535 c->buffer_ptr = c->buffer;
1537 c->state = HTTPSTATE_SEND_HEADER;
1541 c->http_error = 200; /* horrible : we use this value to avoid
1542 going to the send data state */
1543 c->state = HTTPSTATE_SEND_HEADER;
1547 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1549 static const char *suffix = " kMGTP";
1552 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1555 url_fprintf(pb, "%lld%c", count, *s);
1558 static void compute_stats(HTTPContext *c)
1565 ByteIOContext pb1, *pb = &pb1;
1567 if (url_open_dyn_buf(pb) < 0) {
1568 /* XXX: return an error ? */
1569 c->buffer_ptr = c->buffer;
1570 c->buffer_end = c->buffer;
1574 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1575 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1576 url_fprintf(pb, "Pragma: no-cache\r\n");
1577 url_fprintf(pb, "\r\n");
1579 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1580 if (c->stream->feed_filename) {
1581 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1583 url_fprintf(pb, "</HEAD>\n<BODY>");
1584 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1586 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1587 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1588 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");
1589 stream = first_stream;
1590 while (stream != NULL) {
1591 char sfilename[1024];
1594 if (stream->feed != stream) {
1595 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1596 eosf = sfilename + strlen(sfilename);
1597 if (eosf - sfilename >= 4) {
1598 if (strcmp(eosf - 4, ".asf") == 0) {
1599 strcpy(eosf - 4, ".asx");
1600 } else if (strcmp(eosf - 3, ".rm") == 0) {
1601 strcpy(eosf - 3, ".ram");
1602 } else if (stream->fmt == &rtp_mux) {
1603 /* generate a sample RTSP director if
1604 unicast. Generate an SDP redirector if
1606 eosf = strrchr(sfilename, '.');
1608 eosf = sfilename + strlen(sfilename);
1609 if (stream->is_multicast)
1610 strcpy(eosf, ".sdp");
1612 strcpy(eosf, ".rtsp");
1616 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1617 sfilename, stream->filename);
1618 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1619 stream->conns_served);
1620 fmt_bytecount(pb, stream->bytes_served);
1621 switch(stream->stream_type) {
1622 case STREAM_TYPE_LIVE:
1624 int audio_bit_rate = 0;
1625 int video_bit_rate = 0;
1626 const char *audio_codec_name = "";
1627 const char *video_codec_name = "";
1628 const char *audio_codec_name_extra = "";
1629 const char *video_codec_name_extra = "";
1631 for(i=0;i<stream->nb_streams;i++) {
1632 AVStream *st = stream->streams[i];
1633 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1634 switch(st->codec.codec_type) {
1635 case CODEC_TYPE_AUDIO:
1636 audio_bit_rate += st->codec.bit_rate;
1638 if (*audio_codec_name)
1639 audio_codec_name_extra = "...";
1640 audio_codec_name = codec->name;
1643 case CODEC_TYPE_VIDEO:
1644 video_bit_rate += st->codec.bit_rate;
1646 if (*video_codec_name)
1647 video_codec_name_extra = "...";
1648 video_codec_name = codec->name;
1655 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",
1658 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1659 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1661 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1663 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1665 url_fprintf(pb, "\n");
1669 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1673 stream = stream->next;
1675 url_fprintf(pb, "</TABLE>\n");
1677 stream = first_stream;
1678 while (stream != NULL) {
1679 if (stream->feed == stream) {
1680 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1682 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1684 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1689 /* This is somewhat linux specific I guess */
1690 snprintf(ps_cmd, sizeof(ps_cmd),
1691 "ps -o \"%%cpu,cputime\" --no-headers %d",
1694 pid_stat = popen(ps_cmd, "r");
1699 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1701 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1709 url_fprintf(pb, "<p>");
1711 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");
1713 for (i = 0; i < stream->nb_streams; i++) {
1714 AVStream *st = stream->streams[i];
1715 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1716 const char *type = "unknown";
1717 char parameters[64];
1721 switch(st->codec.codec_type) {
1722 case CODEC_TYPE_AUDIO:
1725 case CODEC_TYPE_VIDEO:
1727 sprintf(parameters, "%dx%d, q=%d-%d, fps=%d", st->codec.width, st->codec.height,
1728 st->codec.qmin, st->codec.qmax, st->codec.frame_rate / st->codec.frame_rate_base);
1733 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1734 i, type, st->codec.bit_rate/1000, codec ? codec->name : "", parameters);
1736 url_fprintf(pb, "</table>\n");
1739 stream = stream->next;
1745 AVCodecContext *enc;
1749 stream = first_feed;
1750 while (stream != NULL) {
1751 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1752 url_fprintf(pb, "<TABLE>\n");
1753 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1754 for(i=0;i<stream->nb_streams;i++) {
1755 AVStream *st = stream->streams[i];
1756 FeedData *fdata = st->priv_data;
1759 avcodec_string(buf, sizeof(buf), enc);
1760 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1761 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1762 avg /= enc->frame_size;
1763 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1764 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1766 url_fprintf(pb, "</TABLE>\n");
1767 stream = stream->next_feed;
1772 /* connection status */
1773 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1775 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1776 nb_connections, nb_max_connections);
1778 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1779 current_bandwidth, max_bandwidth);
1781 url_fprintf(pb, "<TABLE>\n");
1782 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");
1783 c1 = first_http_ctx;
1785 while (c1 != NULL) {
1791 for (j = 0; j < c1->stream->nb_streams; j++) {
1792 if (!c1->stream->feed) {
1793 bitrate += c1->stream->streams[j]->codec.bit_rate;
1795 if (c1->feed_streams[j] >= 0) {
1796 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec.bit_rate;
1803 p = inet_ntoa(c1->from_addr.sin_addr);
1804 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1806 c1->stream ? c1->stream->filename : "",
1807 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1810 http_state[c1->state]);
1811 fmt_bytecount(pb, bitrate);
1812 url_fprintf(pb, "<td align=right>");
1813 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1814 url_fprintf(pb, "<td align=right>");
1815 fmt_bytecount(pb, c1->data_count);
1816 url_fprintf(pb, "\n");
1819 url_fprintf(pb, "</TABLE>\n");
1824 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1825 url_fprintf(pb, "</BODY>\n</HTML>\n");
1827 len = url_close_dyn_buf(pb, &c->pb_buffer);
1828 c->buffer_ptr = c->pb_buffer;
1829 c->buffer_end = c->pb_buffer + len;
1832 /* check if the parser needs to be opened for stream i */
1833 static void open_parser(AVFormatContext *s, int i)
1835 AVStream *st = s->streams[i];
1838 if (!st->codec.codec) {
1839 codec = avcodec_find_decoder(st->codec.codec_id);
1840 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1841 st->codec.parse_only = 1;
1842 if (avcodec_open(&st->codec, codec) < 0) {
1843 st->codec.parse_only = 0;
1849 static int open_input_stream(HTTPContext *c, const char *info)
1852 char input_filename[1024];
1857 /* find file name */
1858 if (c->stream->feed) {
1859 strcpy(input_filename, c->stream->feed->feed_filename);
1860 buf_size = FFM_PACKET_SIZE;
1861 /* compute position (absolute time) */
1862 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1863 stream_pos = parse_date(buf, 0);
1864 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1865 int prebuffer = strtol(buf, 0, 10);
1866 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1868 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1871 strcpy(input_filename, c->stream->feed_filename);
1873 /* compute position (relative time) */
1874 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1875 stream_pos = parse_date(buf, 1);
1880 if (input_filename[0] == '\0')
1884 { time_t when = stream_pos / 1000000;
1885 http_log("Stream pos = %lld, time=%s", stream_pos, ctime(&when));
1890 if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0) {
1891 http_log("%s not found", input_filename);
1896 /* open each parser */
1897 for(i=0;i<s->nb_streams;i++)
1900 /* choose stream as clock source (we favorize video stream if
1901 present) for packet sending */
1902 c->pts_stream_index = 0;
1903 for(i=0;i<c->stream->nb_streams;i++) {
1904 if (c->pts_stream_index == 0 &&
1905 c->stream->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO) {
1906 c->pts_stream_index = i;
1910 if (c->fmt_in->iformat->read_seek) {
1911 c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
1913 /* set the start time (needed for maxtime and RTP packet timing) */
1914 c->start_time = cur_time;
1915 c->first_pts = AV_NOPTS_VALUE;
1919 /* currently desactivated because the new PTS handling is not
1921 //#define AV_READ_FRAME
1922 #ifdef AV_READ_FRAME
1924 /* XXX: generalize that in ffmpeg for picture/audio/data. Currently
1925 the return packet MUST NOT be freed */
1926 int av_read_frame(AVFormatContext *s, AVPacket *pkt)
1929 int len, ret, old_nb_streams, i;
1931 /* see if remaining frames must be parsed */
1933 if (s->cur_len > 0) {
1934 st = s->streams[s->cur_pkt.stream_index];
1935 len = avcodec_parse_frame(&st->codec, &pkt->data, &pkt->size,
1936 s->cur_ptr, s->cur_len);
1938 /* error: get next packet */
1944 /* init pts counter if not done */
1945 if (st->pts.den == 0) {
1946 switch(st->codec.codec_type) {
1947 case CODEC_TYPE_AUDIO:
1948 st->pts_incr = (int64_t)s->pts_den;
1949 av_frac_init(&st->pts, st->pts.val, 0,
1950 (int64_t)s->pts_num * st->codec.sample_rate);
1952 case CODEC_TYPE_VIDEO:
1953 st->pts_incr = (int64_t)s->pts_den * st->codec.frame_rate_base;
1954 av_frac_init(&st->pts, st->pts.val, 0,
1955 (int64_t)s->pts_num * st->codec.frame_rate);
1962 /* a frame was read: return it */
1963 pkt->pts = st->pts.val;
1965 printf("add pts=%Lx num=%Lx den=%Lx incr=%Lx\n",
1966 st->pts.val, st->pts.num, st->pts.den, st->pts_incr);
1968 switch(st->codec.codec_type) {
1969 case CODEC_TYPE_AUDIO:
1970 av_frac_add(&st->pts, st->pts_incr * st->codec.frame_size);
1972 case CODEC_TYPE_VIDEO:
1973 av_frac_add(&st->pts, st->pts_incr);
1978 pkt->stream_index = s->cur_pkt.stream_index;
1979 /* we use the codec indication because it is
1980 more accurate than the demux flags */
1982 if (st->codec.coded_frame->key_frame)
1983 pkt->flags |= PKT_FLAG_KEY;
1988 /* free previous packet */
1989 av_free_packet(&s->cur_pkt);
1991 old_nb_streams = s->nb_streams;
1992 ret = av_read_packet(s, &s->cur_pkt);
1995 /* open parsers for each new streams */
1996 for(i = old_nb_streams; i < s->nb_streams; i++)
1998 st = s->streams[s->cur_pkt.stream_index];
2000 /* update current pts (XXX: dts handling) from packet, or
2001 use current pts if none given */
2002 if (s->cur_pkt.pts != AV_NOPTS_VALUE) {
2003 av_frac_set(&st->pts, s->cur_pkt.pts);
2005 s->cur_pkt.pts = st->pts.val;
2007 if (!st->codec.codec) {
2008 /* no codec opened: just return the raw packet */
2011 /* no codec opened: just update the pts by considering we
2012 have one frame and free the packet */
2013 if (st->pts.den == 0) {
2014 switch(st->codec.codec_type) {
2015 case CODEC_TYPE_AUDIO:
2016 st->pts_incr = (int64_t)s->pts_den * st->codec.frame_size;
2017 av_frac_init(&st->pts, st->pts.val, 0,
2018 (int64_t)s->pts_num * st->codec.sample_rate);
2020 case CODEC_TYPE_VIDEO:
2021 st->pts_incr = (int64_t)s->pts_den * st->codec.frame_rate_base;
2022 av_frac_init(&st->pts, st->pts.val, 0,
2023 (int64_t)s->pts_num * st->codec.frame_rate);
2029 av_frac_add(&st->pts, st->pts_incr);
2032 s->cur_ptr = s->cur_pkt.data;
2033 s->cur_len = s->cur_pkt.size;
2039 static int compute_send_delay(HTTPContext *c)
2041 int64_t cur_pts, delta_pts, next_pts;
2044 /* compute current pts value from system time */
2045 cur_pts = ((int64_t)(cur_time - c->start_time) * c->fmt_in->pts_den) /
2046 (c->fmt_in->pts_num * 1000LL);
2047 /* compute the delta from the stream we choose as
2048 main clock (we do that to avoid using explicit
2049 buffers to do exact packet reordering for each
2051 /* XXX: really need to fix the number of streams */
2052 if (c->pts_stream_index >= c->fmt_in->nb_streams)
2055 next_pts = c->fmt_in->streams[c->pts_stream_index]->pts.val;
2056 delta_pts = next_pts - cur_pts;
2057 if (delta_pts <= 0) {
2060 delay1 = (delta_pts * 1000 * c->fmt_in->pts_num) / c->fmt_in->pts_den;
2066 /* just fall backs */
2067 static int av_read_frame(AVFormatContext *s, AVPacket *pkt)
2069 return av_read_packet(s, pkt);
2072 static int compute_send_delay(HTTPContext *c)
2074 int datarate = 8 * get_longterm_datarate(&c->datarate, c->data_count);
2076 if (datarate > c->stream->bandwidth * 2000) {
2084 static int http_prepare_data(HTTPContext *c)
2087 AVFormatContext *ctx;
2090 case HTTPSTATE_SEND_DATA_HEADER:
2091 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2092 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
2094 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
2095 c->stream->comment);
2096 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
2097 c->stream->copyright);
2098 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
2101 /* open output stream by using specified codecs */
2102 c->fmt_ctx.oformat = c->stream->fmt;
2103 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2104 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2106 st = av_mallocz(sizeof(AVStream));
2107 c->fmt_ctx.streams[i] = st;
2108 /* if file or feed, then just take streams from FFStream struct */
2109 if (!c->stream->feed ||
2110 c->stream->feed == c->stream)
2111 memcpy(st, c->stream->streams[i], sizeof(AVStream));
2113 memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]],
2115 st->codec.frame_number = 0; /* XXX: should be done in
2116 AVStream, not in codec */
2117 /* I'm pretty sure that this is not correct...
2118 * However, without it, we crash
2120 st->codec.coded_frame = &dummy_frame;
2122 c->got_key_frame = 0;
2124 /* prepare header and save header data in a stream */
2125 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2126 /* XXX: potential leak */
2129 c->fmt_ctx.pb.is_streamed = 1;
2131 av_set_parameters(&c->fmt_ctx, NULL);
2132 av_write_header(&c->fmt_ctx);
2134 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2135 c->buffer_ptr = c->pb_buffer;
2136 c->buffer_end = c->pb_buffer + len;
2138 c->state = HTTPSTATE_SEND_DATA;
2139 c->last_packet_sent = 0;
2141 case HTTPSTATE_SEND_DATA:
2142 /* find a new packet */
2146 /* read a packet from the input stream */
2147 if (c->stream->feed) {
2148 ffm_set_write_index(c->fmt_in,
2149 c->stream->feed->feed_write_index,
2150 c->stream->feed->feed_size);
2153 if (c->stream->max_time &&
2154 c->stream->max_time + c->start_time - cur_time < 0) {
2155 /* We have timed out */
2156 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2158 if (1 || c->is_packetized) {
2159 if (compute_send_delay(c) > 0) {
2160 c->state = HTTPSTATE_WAIT;
2161 return 1; /* state changed */
2165 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2166 if (c->stream->feed && c->stream->feed->feed_opened) {
2167 /* if coming from feed, it means we reached the end of the
2168 ffm file, so must wait for more data */
2169 c->state = HTTPSTATE_WAIT_FEED;
2170 return 1; /* state changed */
2172 if (c->stream->loop) {
2173 av_close_input_file(c->fmt_in);
2175 if (open_input_stream(c, "") < 0)
2180 /* must send trailer now because eof or error */
2181 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2185 /* update first pts if needed */
2186 if (c->first_pts == AV_NOPTS_VALUE)
2187 c->first_pts = pkt.pts;
2189 /* send it to the appropriate stream */
2190 if (c->stream->feed) {
2191 /* if coming from a feed, select the right stream */
2192 if (c->switch_pending) {
2193 c->switch_pending = 0;
2194 for(i=0;i<c->stream->nb_streams;i++) {
2195 if (c->switch_feed_streams[i] == pkt.stream_index) {
2196 if (pkt.flags & PKT_FLAG_KEY) {
2197 do_switch_stream(c, i);
2200 if (c->switch_feed_streams[i] >= 0) {
2201 c->switch_pending = 1;
2205 for(i=0;i<c->stream->nb_streams;i++) {
2206 if (c->feed_streams[i] == pkt.stream_index) {
2207 pkt.stream_index = i;
2208 if (pkt.flags & PKT_FLAG_KEY) {
2209 c->got_key_frame |= 1 << i;
2211 /* See if we have all the key frames, then
2212 * we start to send. This logic is not quite
2213 * right, but it works for the case of a
2214 * single video stream with one or more
2215 * audio streams (for which every frame is
2216 * typically a key frame).
2218 if (!c->stream->send_on_key ||
2219 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2225 AVCodecContext *codec;
2228 /* specific handling for RTP: we use several
2229 output stream (one for each RTP
2230 connection). XXX: need more abstract handling */
2231 if (c->is_packetized) {
2232 c->packet_stream_index = pkt.stream_index;
2233 ctx = c->rtp_ctx[c->packet_stream_index];
2234 codec = &ctx->streams[0]->codec;
2235 /* only one stream per RTP connection */
2236 pkt.stream_index = 0;
2240 codec = &ctx->streams[pkt.stream_index]->codec;
2243 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2246 if (codec->codec_type == CODEC_TYPE_AUDIO) {
2247 codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
2248 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
2252 if (c->is_packetized) {
2253 ret = url_open_dyn_packet_buf(&ctx->pb,
2254 url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]));
2255 c->packet_byte_count = 0;
2256 c->packet_start_time_us = av_gettime();
2258 ret = url_open_dyn_buf(&ctx->pb);
2261 /* XXX: potential leak */
2264 if (av_write_frame(ctx, pkt.stream_index, pkt.data, pkt.size)) {
2265 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2268 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2269 c->buffer_ptr = c->pb_buffer;
2270 c->buffer_end = c->pb_buffer + len;
2272 codec->frame_number++;
2274 #ifndef AV_READ_FRAME
2275 av_free_packet(&pkt);
2282 case HTTPSTATE_SEND_DATA_TRAILER:
2283 /* last packet test ? */
2284 if (c->last_packet_sent || c->is_packetized)
2287 /* prepare header */
2288 if (url_open_dyn_buf(&ctx->pb) < 0) {
2289 /* XXX: potential leak */
2292 av_write_trailer(ctx);
2293 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2294 c->buffer_ptr = c->pb_buffer;
2295 c->buffer_end = c->pb_buffer + len;
2297 c->last_packet_sent = 1;
2304 #define SHORT_TERM_BANDWIDTH 8000000
2306 /* should convert the format at the same time */
2307 static int http_send_data(HTTPContext *c)
2311 while (c->buffer_ptr >= c->buffer_end) {
2312 av_freep(&c->pb_buffer);
2313 ret = http_prepare_data(c);
2316 else if (ret == 0) {
2319 /* state change requested */
2324 if (c->buffer_ptr < c->buffer_end) {
2325 if (c->is_packetized) {
2326 /* RTP/UDP data output */
2327 len = c->buffer_end - c->buffer_ptr;
2329 /* fail safe - should never happen */
2331 c->buffer_ptr = c->buffer_end;
2334 len = (c->buffer_ptr[0] << 24) |
2335 (c->buffer_ptr[1] << 16) |
2336 (c->buffer_ptr[2] << 8) |
2338 if (len > (c->buffer_end - c->buffer_ptr))
2341 /* short term bandwidth limitation */
2342 dt = av_gettime() - c->packet_start_time_us;
2346 if ((c->packet_byte_count + len) * (int64_t)1000000 >=
2347 (SHORT_TERM_BANDWIDTH / 8) * (int64_t)dt) {
2348 /* bandwidth overflow : wait at most one tick and retry */
2349 c->state = HTTPSTATE_WAIT_SHORT;
2354 url_write(c->rtp_handles[c->packet_stream_index],
2355 c->buffer_ptr, len);
2356 c->buffer_ptr += len;
2357 c->packet_byte_count += len;
2359 /* TCP data output */
2360 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2362 if (errno != EAGAIN && errno != EINTR) {
2363 /* error : close connection */
2369 c->buffer_ptr += len;
2372 c->data_count += len;
2373 update_datarate(&c->datarate, c->data_count);
2375 c->stream->bytes_served += len;
2380 static int http_start_receive_data(HTTPContext *c)
2384 if (c->stream->feed_opened)
2388 fd = open(c->stream->feed_filename, O_RDWR);
2393 c->stream->feed_write_index = ffm_read_write_index(fd);
2394 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2395 lseek(fd, 0, SEEK_SET);
2397 /* init buffer input */
2398 c->buffer_ptr = c->buffer;
2399 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2400 c->stream->feed_opened = 1;
2404 static int http_receive_data(HTTPContext *c)
2408 if (c->buffer_end > c->buffer_ptr) {
2411 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2413 if (errno != EAGAIN && errno != EINTR) {
2414 /* error : close connection */
2417 } else if (len == 0) {
2418 /* end of connection : close it */
2421 c->buffer_ptr += len;
2422 c->data_count += len;
2423 update_datarate(&c->datarate, c->data_count);
2427 if (c->buffer_ptr >= c->buffer_end) {
2428 FFStream *feed = c->stream;
2429 /* a packet has been received : write it in the store, except
2431 if (c->data_count > FFM_PACKET_SIZE) {
2433 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2434 /* XXX: use llseek or url_seek */
2435 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2436 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2438 feed->feed_write_index += FFM_PACKET_SIZE;
2439 /* update file size */
2440 if (feed->feed_write_index > c->stream->feed_size)
2441 feed->feed_size = feed->feed_write_index;
2443 /* handle wrap around if max file size reached */
2444 if (feed->feed_write_index >= c->stream->feed_max_size)
2445 feed->feed_write_index = FFM_PACKET_SIZE;
2448 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2450 /* wake up any waiting connections */
2451 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2452 if (c1->state == HTTPSTATE_WAIT_FEED &&
2453 c1->stream->feed == c->stream->feed) {
2454 c1->state = HTTPSTATE_SEND_DATA;
2458 /* We have a header in our hands that contains useful data */
2460 AVInputFormat *fmt_in;
2461 ByteIOContext *pb = &s.pb;
2464 memset(&s, 0, sizeof(s));
2466 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2467 pb->buf_end = c->buffer_end; /* ?? */
2468 pb->is_streamed = 1;
2470 /* use feed output format name to find corresponding input format */
2471 fmt_in = av_find_input_format(feed->fmt->name);
2475 if (fmt_in->priv_data_size > 0) {
2476 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2482 if (fmt_in->read_header(&s, 0) < 0) {
2483 av_freep(&s.priv_data);
2487 /* Now we have the actual streams */
2488 if (s.nb_streams != feed->nb_streams) {
2489 av_freep(&s.priv_data);
2492 for (i = 0; i < s.nb_streams; i++) {
2493 memcpy(&feed->streams[i]->codec,
2494 &s.streams[i]->codec, sizeof(AVCodecContext));
2496 av_freep(&s.priv_data);
2498 c->buffer_ptr = c->buffer;
2503 c->stream->feed_opened = 0;
2508 /********************************************************************/
2511 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2518 switch(error_number) {
2519 #define DEF(n, c, s) case c: str = s; break;
2520 #include "rtspcodes.h"
2523 str = "Unknown Error";
2527 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2528 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2530 /* output GMT time */
2534 p = buf2 + strlen(p) - 1;
2537 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2540 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2542 rtsp_reply_header(c, error_number);
2543 url_fprintf(c->pb, "\r\n");
2546 static int rtsp_parse_request(HTTPContext *c)
2548 const char *p, *p1, *p2;
2555 RTSPHeader header1, *header = &header1;
2557 c->buffer_ptr[0] = '\0';
2560 get_word(cmd, sizeof(cmd), &p);
2561 get_word(url, sizeof(url), &p);
2562 get_word(protocol, sizeof(protocol), &p);
2564 pstrcpy(c->method, sizeof(c->method), cmd);
2565 pstrcpy(c->url, sizeof(c->url), url);
2566 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2569 if (url_open_dyn_buf(c->pb) < 0) {
2570 /* XXX: cannot do more */
2571 c->pb = NULL; /* safety */
2575 /* check version name */
2576 if (strcmp(protocol, "RTSP/1.0") != 0) {
2577 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2581 /* parse each header line */
2582 memset(header, 0, sizeof(RTSPHeader));
2583 /* skip to next line */
2584 while (*p != '\n' && *p != '\0')
2588 while (*p != '\0') {
2589 p1 = strchr(p, '\n');
2593 if (p2 > p && p2[-1] == '\r')
2595 /* skip empty line */
2599 if (len > sizeof(line) - 1)
2600 len = sizeof(line) - 1;
2601 memcpy(line, p, len);
2603 rtsp_parse_line(header, line);
2607 /* handle sequence number */
2608 c->seq = header->seq;
2610 if (!strcmp(cmd, "DESCRIBE")) {
2611 rtsp_cmd_describe(c, url);
2612 } else if (!strcmp(cmd, "SETUP")) {
2613 rtsp_cmd_setup(c, url, header);
2614 } else if (!strcmp(cmd, "PLAY")) {
2615 rtsp_cmd_play(c, url, header);
2616 } else if (!strcmp(cmd, "PAUSE")) {
2617 rtsp_cmd_pause(c, url, header);
2618 } else if (!strcmp(cmd, "TEARDOWN")) {
2619 rtsp_cmd_teardown(c, url, header);
2621 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2624 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2625 c->pb = NULL; /* safety */
2627 /* XXX: cannot do more */
2630 c->buffer_ptr = c->pb_buffer;
2631 c->buffer_end = c->pb_buffer + len;
2632 c->state = RTSPSTATE_SEND_REPLY;
2636 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2638 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2639 struct in_addr my_ip)
2641 ByteIOContext pb1, *pb = &pb1;
2642 int i, payload_type, port, private_payload_type, j;
2643 const char *ipstr, *title, *mediatype;
2646 if (url_open_dyn_buf(pb) < 0)
2649 /* general media info */
2651 url_fprintf(pb, "v=0\n");
2652 ipstr = inet_ntoa(my_ip);
2653 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2654 title = stream->title;
2655 if (title[0] == '\0')
2657 url_fprintf(pb, "s=%s\n", title);
2658 if (stream->comment[0] != '\0')
2659 url_fprintf(pb, "i=%s\n", stream->comment);
2660 if (stream->is_multicast) {
2661 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2663 /* for each stream, we output the necessary info */
2664 private_payload_type = 96;
2665 for(i = 0; i < stream->nb_streams; i++) {
2666 st = stream->streams[i];
2667 switch(st->codec.codec_type) {
2668 case CODEC_TYPE_AUDIO:
2669 mediatype = "audio";
2671 case CODEC_TYPE_VIDEO:
2672 mediatype = "video";
2675 mediatype = "application";
2678 /* NOTE: the port indication is not correct in case of
2679 unicast. It is not an issue because RTSP gives it */
2680 payload_type = rtp_get_payload_type(&st->codec);
2681 if (payload_type < 0)
2682 payload_type = private_payload_type++;
2683 if (stream->is_multicast) {
2684 port = stream->multicast_port + 2 * i;
2688 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2689 mediatype, port, payload_type);
2690 if (payload_type >= 96) {
2691 /* for private payload type, we need to give more info */
2692 switch(st->codec.codec_id) {
2693 case CODEC_ID_MPEG4:
2696 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2697 payload_type, 90000);
2698 /* we must also add the mpeg4 header */
2699 data = st->codec.extradata;
2701 url_fprintf(pb, "a=fmtp:%d config=");
2702 for(j=0;j<st->codec.extradata_size;j++) {
2703 url_fprintf(pb, "%02x", data[j]);
2705 url_fprintf(pb, "\n");
2710 /* XXX: add other codecs ? */
2714 url_fprintf(pb, "a=control:streamid=%d\n", i);
2716 return url_close_dyn_buf(pb, pbuffer);
2718 url_close_dyn_buf(pb, pbuffer);
2723 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2729 int content_length, len;
2730 struct sockaddr_in my_addr;
2732 /* find which url is asked */
2733 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2738 for(stream = first_stream; stream != NULL; stream = stream->next) {
2739 if (!stream->is_feed && stream->fmt == &rtp_mux &&
2740 !strcmp(path, stream->filename)) {
2744 /* no stream found */
2745 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2749 /* prepare the media description in sdp format */
2751 /* get the host IP */
2752 len = sizeof(my_addr);
2753 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2755 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2756 if (content_length < 0) {
2757 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2760 rtsp_reply_header(c, RTSP_STATUS_OK);
2761 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2762 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2763 url_fprintf(c->pb, "\r\n");
2764 put_buffer(c->pb, content, content_length);
2767 static HTTPContext *find_rtp_session(const char *session_id)
2771 if (session_id[0] == '\0')
2774 for(c = first_http_ctx; c != NULL; c = c->next) {
2775 if (!strcmp(c->session_id, session_id))
2781 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2783 RTSPTransportField *th;
2786 for(i=0;i<h->nb_transports;i++) {
2787 th = &h->transports[i];
2788 if (th->protocol == protocol)
2794 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2798 int stream_index, port;
2803 RTSPTransportField *th;
2804 struct sockaddr_in dest_addr;
2805 RTSPActionServerSetup setup;
2807 /* find which url is asked */
2808 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2813 /* now check each stream */
2814 for(stream = first_stream; stream != NULL; stream = stream->next) {
2815 if (!stream->is_feed && stream->fmt == &rtp_mux) {
2816 /* accept aggregate filenames only if single stream */
2817 if (!strcmp(path, stream->filename)) {
2818 if (stream->nb_streams != 1) {
2819 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2826 for(stream_index = 0; stream_index < stream->nb_streams;
2828 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2829 stream->filename, stream_index);
2830 if (!strcmp(path, buf))
2835 /* no stream found */
2836 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2840 /* generate session id if needed */
2841 if (h->session_id[0] == '\0') {
2842 snprintf(h->session_id, sizeof(h->session_id),
2843 "%08x%08x", (int)random(), (int)random());
2846 /* find rtp session, and create it if none found */
2847 rtp_c = find_rtp_session(h->session_id);
2849 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id);
2851 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2855 /* open input stream */
2856 if (open_input_stream(rtp_c, "") < 0) {
2857 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2861 /* always prefer UDP */
2862 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2864 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2866 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2870 rtp_c->rtp_protocol = th->protocol;
2873 /* test if stream is OK (test needed because several SETUP needs
2874 to be done for a given file) */
2875 if (rtp_c->stream != stream) {
2876 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2880 /* test if stream is already set up */
2881 if (rtp_c->rtp_ctx[stream_index]) {
2882 rtsp_reply_error(c, RTSP_STATUS_STATE);
2886 /* check transport */
2887 th = find_transport(h, rtp_c->rtp_protocol);
2888 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2889 th->client_port_min <= 0)) {
2890 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2894 /* setup default options */
2895 setup.transport_option[0] = '\0';
2896 dest_addr = rtp_c->from_addr;
2897 dest_addr.sin_port = htons(th->client_port_min);
2899 /* add transport option if needed */
2900 if (ff_rtsp_callback) {
2901 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2902 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2903 (char *)&setup, sizeof(setup),
2904 stream->rtsp_option) < 0) {
2905 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2908 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2912 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
2913 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2917 /* now everything is OK, so we can send the connection parameters */
2918 rtsp_reply_header(c, RTSP_STATUS_OK);
2920 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2922 switch(rtp_c->rtp_protocol) {
2923 case RTSP_PROTOCOL_RTP_UDP:
2924 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2925 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2926 "client_port=%d-%d;server_port=%d-%d",
2927 th->client_port_min, th->client_port_min + 1,
2930 case RTSP_PROTOCOL_RTP_TCP:
2931 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2932 stream_index * 2, stream_index * 2 + 1);
2937 if (setup.transport_option[0] != '\0') {
2938 url_fprintf(c->pb, ";%s", setup.transport_option);
2940 url_fprintf(c->pb, "\r\n");
2943 url_fprintf(c->pb, "\r\n");
2947 /* find an rtp connection by using the session ID. Check consistency
2949 static HTTPContext *find_rtp_session_with_url(const char *url,
2950 const char *session_id)
2956 rtp_c = find_rtp_session(session_id);
2960 /* find which url is asked */
2961 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2965 if (strcmp(path, rtp_c->stream->filename) != 0)
2970 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2974 rtp_c = find_rtp_session_with_url(url, h->session_id);
2976 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2980 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2981 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2982 rtp_c->state != HTTPSTATE_READY) {
2983 rtsp_reply_error(c, RTSP_STATUS_STATE);
2987 rtp_c->state = HTTPSTATE_SEND_DATA;
2989 /* now everything is OK, so we can send the connection parameters */
2990 rtsp_reply_header(c, RTSP_STATUS_OK);
2992 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2993 url_fprintf(c->pb, "\r\n");
2996 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3000 rtp_c = find_rtp_session_with_url(url, h->session_id);
3002 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3006 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3007 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3008 rtsp_reply_error(c, RTSP_STATUS_STATE);
3012 rtp_c->state = HTTPSTATE_READY;
3014 /* now everything is OK, so we can send the connection parameters */
3015 rtsp_reply_header(c, RTSP_STATUS_OK);
3017 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3018 url_fprintf(c->pb, "\r\n");
3021 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3025 rtp_c = find_rtp_session_with_url(url, h->session_id);
3027 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3031 /* abort the session */
3032 close_connection(rtp_c);
3034 if (ff_rtsp_callback) {
3035 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
3037 rtp_c->stream->rtsp_option);
3040 /* now everything is OK, so we can send the connection parameters */
3041 rtsp_reply_header(c, RTSP_STATUS_OK);
3043 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3044 url_fprintf(c->pb, "\r\n");
3048 /********************************************************************/
3051 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3052 FFStream *stream, const char *session_id)
3054 HTTPContext *c = NULL;
3056 /* XXX: should output a warning page when coming
3057 close to the connection limit */
3058 if (nb_connections >= nb_max_connections)
3061 /* add a new connection */
3062 c = av_mallocz(sizeof(HTTPContext));
3067 c->poll_entry = NULL;
3068 c->from_addr = *from_addr;
3069 c->buffer_size = IOBUFFER_INIT_SIZE;
3070 c->buffer = av_malloc(c->buffer_size);
3075 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
3076 c->state = HTTPSTATE_READY;
3077 c->is_packetized = 1;
3078 /* protocol is shown in statistics */
3079 pstrcpy(c->protocol, sizeof(c->protocol), "RTP");
3081 current_bandwidth += stream->bandwidth;
3083 c->next = first_http_ctx;
3095 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3096 command). if dest_addr is NULL, then TCP tunneling in RTSP is
3098 static int rtp_new_av_stream(HTTPContext *c,
3099 int stream_index, struct sockaddr_in *dest_addr)
3101 AVFormatContext *ctx;
3108 /* now we can open the relevant output stream */
3109 ctx = av_mallocz(sizeof(AVFormatContext));
3112 ctx->oformat = &rtp_mux;
3114 st = av_mallocz(sizeof(AVStream));
3117 ctx->nb_streams = 1;
3118 ctx->streams[0] = st;
3120 if (!c->stream->feed ||
3121 c->stream->feed == c->stream) {
3122 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3125 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3130 /* build destination RTP address */
3131 ipaddr = inet_ntoa(dest_addr->sin_addr);
3133 /* XXX: also pass as parameter to function ? */
3134 if (c->stream->is_multicast) {
3136 ttl = c->stream->multicast_ttl;
3139 snprintf(ctx->filename, sizeof(ctx->filename),
3140 "rtp://%s:%d?multicast=1&ttl=%d",
3141 ipaddr, ntohs(dest_addr->sin_port), ttl);
3143 snprintf(ctx->filename, sizeof(ctx->filename),
3144 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3147 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3149 c->rtp_handles[stream_index] = h;
3154 http_log("%s:%d - - [%s] \"RTPSTART %s/streamid=%d\"\n",
3155 ipaddr, ntohs(dest_addr->sin_port),
3157 c->stream->filename, stream_index);
3159 /* normally, no packets should be output here, but the packet size may be checked */
3160 if (url_open_dyn_packet_buf(&ctx->pb,
3161 url_get_max_packet_size(h)) < 0) {
3162 /* XXX: close stream */
3165 av_set_parameters(ctx, NULL);
3166 if (av_write_header(ctx) < 0) {
3173 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3176 c->rtp_ctx[stream_index] = ctx;
3180 /********************************************************************/
3181 /* ffserver initialization */
3183 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3187 fst = av_mallocz(sizeof(AVStream));
3190 fst->priv_data = av_mallocz(sizeof(FeedData));
3191 memcpy(&fst->codec, codec, sizeof(AVCodecContext));
3192 fst->codec.coded_frame = &dummy_frame;
3193 stream->streams[stream->nb_streams++] = fst;
3197 /* return the stream number in the feed */
3198 static int add_av_stream(FFStream *feed, AVStream *st)
3201 AVCodecContext *av, *av1;
3205 for(i=0;i<feed->nb_streams;i++) {
3206 st = feed->streams[i];
3208 if (av1->codec_id == av->codec_id &&
3209 av1->codec_type == av->codec_type &&
3210 av1->bit_rate == av->bit_rate) {
3212 switch(av->codec_type) {
3213 case CODEC_TYPE_AUDIO:
3214 if (av1->channels == av->channels &&
3215 av1->sample_rate == av->sample_rate)
3218 case CODEC_TYPE_VIDEO:
3219 if (av1->width == av->width &&
3220 av1->height == av->height &&
3221 av1->frame_rate == av->frame_rate &&
3222 av1->frame_rate_base == av->frame_rate_base &&
3223 av1->gop_size == av->gop_size)
3232 fst = add_av_stream1(feed, av);
3235 return feed->nb_streams - 1;
3240 static void remove_stream(FFStream *stream)
3244 while (*ps != NULL) {
3245 if (*ps == stream) {
3253 /* specific mpeg4 handling : we extract the raw parameters */
3254 static void extract_mpeg4_header(AVFormatContext *infile)
3256 int mpeg4_count, i, size;
3262 for(i=0;i<infile->nb_streams;i++) {
3263 st = infile->streams[i];
3264 if (st->codec.codec_id == CODEC_ID_MPEG4 &&
3265 st->codec.extradata == NULL) {
3272 printf("MPEG4 without extra data: trying to find header\n");
3273 while (mpeg4_count > 0) {
3274 if (av_read_packet(infile, &pkt) < 0)
3276 st = infile->streams[pkt.stream_index];
3277 if (st->codec.codec_id == CODEC_ID_MPEG4 &&
3278 st->codec.extradata == NULL) {
3279 /* fill extradata with the header */
3280 /* XXX: we make hard suppositions here ! */
3282 while (p < pkt.data + pkt.size - 4) {
3283 /* stop when vop header is found */
3284 if (p[0] == 0x00 && p[1] == 0x00 &&
3285 p[2] == 0x01 && p[3] == 0xb6) {
3286 size = p - pkt.data;
3287 // av_hex_dump(pkt.data, size);
3288 st->codec.extradata = av_malloc(size);
3289 st->codec.extradata_size = size;
3290 memcpy(st->codec.extradata, pkt.data, size);
3297 av_free_packet(&pkt);
3301 /* compute the needed AVStream for each file */
3302 static void build_file_streams(void)
3304 FFStream *stream, *stream_next;
3305 AVFormatContext *infile;
3308 /* gather all streams */
3309 for(stream = first_stream; stream != NULL; stream = stream_next) {
3310 stream_next = stream->next;
3311 if (stream->stream_type == STREAM_TYPE_LIVE &&
3313 /* the stream comes from a file */
3314 /* try to open the file */
3316 if (av_open_input_file(&infile, stream->feed_filename,
3317 NULL, 0, NULL) < 0) {
3318 http_log("%s not found", stream->feed_filename);
3319 /* remove stream (no need to spend more time on it) */
3321 remove_stream(stream);
3323 /* find all the AVStreams inside and reference them in
3325 if (av_find_stream_info(infile) < 0) {
3326 http_log("Could not find codec parameters from '%s'",
3327 stream->feed_filename);
3328 av_close_input_file(infile);
3331 extract_mpeg4_header(infile);
3333 for(i=0;i<infile->nb_streams;i++) {
3334 add_av_stream1(stream, &infile->streams[i]->codec);
3336 av_close_input_file(infile);
3342 /* compute the needed AVStream for each feed */
3343 static void build_feed_streams(void)
3345 FFStream *stream, *feed;
3348 /* gather all streams */
3349 for(stream = first_stream; stream != NULL; stream = stream->next) {
3350 feed = stream->feed;
3352 if (!stream->is_feed) {
3353 /* we handle a stream coming from a feed */
3354 for(i=0;i<stream->nb_streams;i++) {
3355 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3361 /* gather all streams */
3362 for(stream = first_stream; stream != NULL; stream = stream->next) {
3363 feed = stream->feed;
3365 if (stream->is_feed) {
3366 for(i=0;i<stream->nb_streams;i++) {
3367 stream->feed_streams[i] = i;
3373 /* create feed files if needed */
3374 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3377 if (url_exist(feed->feed_filename)) {
3378 /* See if it matches */
3382 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3383 /* Now see if it matches */
3384 if (s->nb_streams == feed->nb_streams) {
3386 for(i=0;i<s->nb_streams;i++) {
3388 sf = feed->streams[i];
3391 if (sf->index != ss->index ||
3393 printf("Index & Id do not match for stream %d\n", i);
3396 AVCodecContext *ccf, *ccs;
3400 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3402 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3403 printf("Codecs do not match for stream %d\n", i);
3405 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3406 printf("Codec bitrates do not match for stream %d\n", i);
3408 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3409 if (CHECK_CODEC(frame_rate) ||
3410 CHECK_CODEC(frame_rate_base) ||
3411 CHECK_CODEC(width) ||
3412 CHECK_CODEC(height)) {
3413 printf("Codec width, height and framerate do not match for stream %d\n", i);
3416 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3417 if (CHECK_CODEC(sample_rate) ||
3418 CHECK_CODEC(channels) ||
3419 CHECK_CODEC(frame_size)) {
3420 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3424 printf("Unknown codec type\n");
3433 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3434 feed->feed_filename, s->nb_streams, feed->nb_streams);
3437 av_close_input_file(s);
3439 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3440 feed->feed_filename);
3443 unlink(feed->feed_filename);
3445 if (!url_exist(feed->feed_filename)) {
3446 AVFormatContext s1, *s = &s1;
3448 /* only write the header of the ffm file */
3449 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3450 fprintf(stderr, "Could not open output feed file '%s'\n",
3451 feed->feed_filename);
3454 s->oformat = feed->fmt;
3455 s->nb_streams = feed->nb_streams;
3456 for(i=0;i<s->nb_streams;i++) {
3458 st = feed->streams[i];
3461 av_set_parameters(s, NULL);
3463 /* XXX: need better api */
3464 av_freep(&s->priv_data);
3467 /* get feed size and write index */
3468 fd = open(feed->feed_filename, O_RDONLY);
3470 fprintf(stderr, "Could not open output feed file '%s'\n",
3471 feed->feed_filename);
3475 feed->feed_write_index = ffm_read_write_index(fd);
3476 feed->feed_size = lseek(fd, 0, SEEK_END);
3477 /* ensure that we do not wrap before the end of file */
3478 if (feed->feed_max_size < feed->feed_size)
3479 feed->feed_max_size = feed->feed_size;
3485 /* compute the bandwidth used by each stream */
3486 static void compute_bandwidth(void)
3491 for(stream = first_stream; stream != NULL; stream = stream->next) {
3493 for(i=0;i<stream->nb_streams;i++) {
3494 AVStream *st = stream->streams[i];
3495 switch(st->codec.codec_type) {
3496 case CODEC_TYPE_AUDIO:
3497 case CODEC_TYPE_VIDEO:
3498 bandwidth += st->codec.bit_rate;
3504 stream->bandwidth = (bandwidth + 999) / 1000;
3508 static void get_arg(char *buf, int buf_size, const char **pp)
3515 while (isspace(*p)) p++;
3518 if (*p == '\"' || *p == '\'')
3530 if ((q - buf) < buf_size - 1)
3535 if (quote && *p == quote)
3540 /* add a codec and set the default parameters */
3541 static void add_codec(FFStream *stream, AVCodecContext *av)
3545 /* compute default parameters */
3546 switch(av->codec_type) {
3547 case CODEC_TYPE_AUDIO:
3548 if (av->bit_rate == 0)
3549 av->bit_rate = 64000;
3550 if (av->sample_rate == 0)
3551 av->sample_rate = 22050;
3552 if (av->channels == 0)
3555 case CODEC_TYPE_VIDEO:
3556 if (av->bit_rate == 0)
3557 av->bit_rate = 64000;
3558 if (av->frame_rate == 0){
3560 av->frame_rate_base = 1;
3562 if (av->width == 0 || av->height == 0) {
3566 /* Bitrate tolerance is less for streaming */
3567 if (av->bit_rate_tolerance == 0)
3568 av->bit_rate_tolerance = av->bit_rate / 4;
3573 if (av->max_qdiff == 0)
3575 av->qcompress = 0.5;
3579 av->rc_eq = "tex^qComp";
3580 if (!av->i_quant_factor)
3581 av->i_quant_factor = -0.8;
3582 if (!av->b_quant_factor)
3583 av->b_quant_factor = 1.25;
3584 if (!av->b_quant_offset)
3585 av->b_quant_offset = 1.25;
3586 if (!av->rc_min_rate)
3587 av->rc_min_rate = av->bit_rate / 2;
3588 if (!av->rc_max_rate)
3589 av->rc_max_rate = av->bit_rate * 2;
3596 st = av_mallocz(sizeof(AVStream));
3599 stream->streams[stream->nb_streams++] = st;
3600 memcpy(&st->codec, av, sizeof(AVCodecContext));
3603 static int opt_audio_codec(const char *arg)
3609 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3614 return CODEC_ID_NONE;
3620 static int opt_video_codec(const char *arg)
3626 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3631 return CODEC_ID_NONE;
3637 /* simplistic plugin support */
3639 #ifdef CONFIG_HAVE_DLOPEN
3640 void load_module(const char *filename)
3643 void (*init_func)(void);
3644 dll = dlopen(filename, RTLD_NOW);
3646 fprintf(stderr, "Could not load module '%s' - %s\n",
3647 filename, dlerror());
3651 init_func = dlsym(dll, "ffserver_module_init");
3654 "%s: init function 'ffserver_module_init()' not found\n",
3663 static int parse_ffconfig(const char *filename)
3670 int val, errors, line_num;
3671 FFStream **last_stream, *stream, *redirect;
3672 FFStream **last_feed, *feed;
3673 AVCodecContext audio_enc, video_enc;
3674 int audio_id, video_id;
3676 f = fopen(filename, "r");
3684 first_stream = NULL;
3685 last_stream = &first_stream;
3687 last_feed = &first_feed;
3691 audio_id = CODEC_ID_NONE;
3692 video_id = CODEC_ID_NONE;
3694 if (fgets(line, sizeof(line), f) == NULL)
3700 if (*p == '\0' || *p == '#')
3703 get_arg(cmd, sizeof(cmd), &p);
3705 if (!strcasecmp(cmd, "Port")) {
3706 get_arg(arg, sizeof(arg), &p);
3707 my_http_addr.sin_port = htons (atoi(arg));
3708 } else if (!strcasecmp(cmd, "BindAddress")) {
3709 get_arg(arg, sizeof(arg), &p);
3710 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3711 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3712 filename, line_num, arg);
3715 } else if (!strcasecmp(cmd, "NoDaemon")) {
3716 ffserver_daemon = 0;
3717 } else if (!strcasecmp(cmd, "RTSPPort")) {
3718 get_arg(arg, sizeof(arg), &p);
3719 my_rtsp_addr.sin_port = htons (atoi(arg));
3720 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3721 get_arg(arg, sizeof(arg), &p);
3722 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3723 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3724 filename, line_num, arg);
3727 } else if (!strcasecmp(cmd, "MaxClients")) {
3728 get_arg(arg, sizeof(arg), &p);
3730 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3731 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3732 filename, line_num, arg);
3735 nb_max_connections = val;
3737 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3738 get_arg(arg, sizeof(arg), &p);
3740 if (val < 10 || val > 100000) {
3741 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3742 filename, line_num, arg);
3745 max_bandwidth = val;
3747 } else if (!strcasecmp(cmd, "CustomLog")) {
3748 get_arg(logfilename, sizeof(logfilename), &p);
3749 } else if (!strcasecmp(cmd, "<Feed")) {
3750 /*********************************************/
3751 /* Feed related options */
3753 if (stream || feed) {
3754 fprintf(stderr, "%s:%d: Already in a tag\n",
3755 filename, line_num);
3757 feed = av_mallocz(sizeof(FFStream));
3758 /* add in stream list */
3759 *last_stream = feed;
3760 last_stream = &feed->next;
3761 /* add in feed list */
3763 last_feed = &feed->next_feed;
3765 get_arg(feed->filename, sizeof(feed->filename), &p);
3766 q = strrchr(feed->filename, '>');
3769 feed->fmt = guess_format("ffm", NULL, NULL);
3770 /* defaut feed file */
3771 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3772 "/tmp/%s.ffm", feed->filename);
3773 feed->feed_max_size = 5 * 1024 * 1024;
3775 feed->feed = feed; /* self feeding :-) */
3777 } else if (!strcasecmp(cmd, "Launch")) {
3781 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3783 feed->child_argv[0] = av_malloc(7);
3784 strcpy(feed->child_argv[0], "ffmpeg");
3786 for (i = 1; i < 62; i++) {
3789 get_arg(argbuf, sizeof(argbuf), &p);
3793 feed->child_argv[i] = av_malloc(strlen(argbuf + 1));
3794 strcpy(feed->child_argv[i], argbuf);
3797 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3799 snprintf(feed->child_argv[i], 256, "http://127.0.0.1:%d/%s",
3800 ntohs(my_http_addr.sin_port), feed->filename);
3802 } else if (!strcasecmp(cmd, "File")) {
3804 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3805 } else if (stream) {
3806 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3808 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3813 get_arg(arg, sizeof(arg), &p);
3815 fsize = strtod(p1, (char **)&p1);
3816 switch(toupper(*p1)) {
3821 fsize *= 1024 * 1024;
3824 fsize *= 1024 * 1024 * 1024;
3827 feed->feed_max_size = (int64_t)fsize;
3829 } else if (!strcasecmp(cmd, "</Feed>")) {
3831 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3832 filename, line_num);
3836 /* Make sure that we start out clean */
3837 if (unlink(feed->feed_filename) < 0
3838 && errno != ENOENT) {
3839 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3840 filename, line_num, feed->feed_filename, strerror(errno));
3846 } else if (!strcasecmp(cmd, "<Stream")) {
3847 /*********************************************/
3848 /* Stream related options */
3850 if (stream || feed) {
3851 fprintf(stderr, "%s:%d: Already in a tag\n",
3852 filename, line_num);
3854 stream = av_mallocz(sizeof(FFStream));
3855 *last_stream = stream;
3856 last_stream = &stream->next;
3858 get_arg(stream->filename, sizeof(stream->filename), &p);
3859 q = strrchr(stream->filename, '>');
3862 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3863 memset(&audio_enc, 0, sizeof(AVCodecContext));
3864 memset(&video_enc, 0, sizeof(AVCodecContext));
3865 audio_id = CODEC_ID_NONE;
3866 video_id = CODEC_ID_NONE;
3868 audio_id = stream->fmt->audio_codec;
3869 video_id = stream->fmt->video_codec;
3872 } else if (!strcasecmp(cmd, "Feed")) {
3873 get_arg(arg, sizeof(arg), &p);
3878 while (sfeed != NULL) {
3879 if (!strcmp(sfeed->filename, arg))
3881 sfeed = sfeed->next_feed;
3884 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3885 filename, line_num, arg);
3887 stream->feed = sfeed;
3890 } else if (!strcasecmp(cmd, "Format")) {
3891 get_arg(arg, sizeof(arg), &p);
3892 if (!strcmp(arg, "status")) {
3893 stream->stream_type = STREAM_TYPE_STATUS;
3896 stream->stream_type = STREAM_TYPE_LIVE;
3897 /* jpeg cannot be used here, so use single frame jpeg */
3898 if (!strcmp(arg, "jpeg"))
3899 strcpy(arg, "singlejpeg");
3900 stream->fmt = guess_stream_format(arg, NULL, NULL);
3902 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3903 filename, line_num, arg);
3908 audio_id = stream->fmt->audio_codec;
3909 video_id = stream->fmt->video_codec;
3911 } else if (!strcasecmp(cmd, "FaviconURL")) {
3912 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3913 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3915 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3916 filename, line_num);
3919 } else if (!strcasecmp(cmd, "Author")) {
3921 get_arg(stream->author, sizeof(stream->author), &p);
3923 } else if (!strcasecmp(cmd, "Comment")) {
3925 get_arg(stream->comment, sizeof(stream->comment), &p);
3927 } else if (!strcasecmp(cmd, "Copyright")) {
3929 get_arg(stream->copyright, sizeof(stream->copyright), &p);
3931 } else if (!strcasecmp(cmd, "Title")) {
3933 get_arg(stream->title, sizeof(stream->title), &p);
3935 } else if (!strcasecmp(cmd, "Preroll")) {
3936 get_arg(arg, sizeof(arg), &p);
3938 stream->prebuffer = atof(arg) * 1000;
3940 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
3942 stream->send_on_key = 1;
3944 } else if (!strcasecmp(cmd, "AudioCodec")) {
3945 get_arg(arg, sizeof(arg), &p);
3946 audio_id = opt_audio_codec(arg);
3947 if (audio_id == CODEC_ID_NONE) {
3948 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
3949 filename, line_num, arg);
3952 } else if (!strcasecmp(cmd, "VideoCodec")) {
3953 get_arg(arg, sizeof(arg), &p);
3954 video_id = opt_video_codec(arg);
3955 if (video_id == CODEC_ID_NONE) {
3956 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
3957 filename, line_num, arg);
3960 } else if (!strcasecmp(cmd, "MaxTime")) {
3961 get_arg(arg, sizeof(arg), &p);
3963 stream->max_time = atof(arg) * 1000;
3965 } else if (!strcasecmp(cmd, "AudioBitRate")) {
3966 get_arg(arg, sizeof(arg), &p);
3968 audio_enc.bit_rate = atoi(arg) * 1000;
3970 } else if (!strcasecmp(cmd, "AudioChannels")) {
3971 get_arg(arg, sizeof(arg), &p);
3973 audio_enc.channels = atoi(arg);
3975 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
3976 get_arg(arg, sizeof(arg), &p);
3978 audio_enc.sample_rate = atoi(arg);
3980 } else if (!strcasecmp(cmd, "AudioQuality")) {
3981 get_arg(arg, sizeof(arg), &p);
3983 // audio_enc.quality = atof(arg) * 1000;
3985 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
3987 int minrate, maxrate;
3989 get_arg(arg, sizeof(arg), &p);
3991 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
3992 video_enc.rc_min_rate = minrate * 1000;
3993 video_enc.rc_max_rate = maxrate * 1000;
3995 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
3996 filename, line_num, arg);
4000 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4002 get_arg(arg, sizeof(arg), &p);
4003 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4005 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4006 get_arg(arg, sizeof(arg), &p);
4008 video_enc.bit_rate = atoi(arg) * 1000;
4010 } else if (!strcasecmp(cmd, "VideoSize")) {
4011 get_arg(arg, sizeof(arg), &p);
4013 parse_image_size(&video_enc.width, &video_enc.height, arg);
4014 if ((video_enc.width % 16) != 0 ||
4015 (video_enc.height % 16) != 0) {
4016 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4017 filename, line_num);
4021 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4022 get_arg(arg, sizeof(arg), &p);
4024 video_enc.frame_rate_base= DEFAULT_FRAME_RATE_BASE;
4025 video_enc.frame_rate = (int)(strtod(arg, NULL) * video_enc.frame_rate_base);
4027 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4028 get_arg(arg, sizeof(arg), &p);
4030 video_enc.gop_size = atoi(arg);
4032 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4034 video_enc.gop_size = 1;
4036 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4038 video_enc.flags |= CODEC_FLAG_HQ;
4040 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4041 get_arg(arg, sizeof(arg), &p);
4043 video_enc.max_qdiff = atoi(arg);
4044 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4045 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4046 filename, line_num);
4050 } else if (!strcasecmp(cmd, "VideoQMax")) {
4051 get_arg(arg, sizeof(arg), &p);
4053 video_enc.qmax = atoi(arg);
4054 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4055 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4056 filename, line_num);
4060 } else if (!strcasecmp(cmd, "VideoQMin")) {
4061 get_arg(arg, sizeof(arg), &p);
4063 video_enc.qmin = atoi(arg);
4064 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4065 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4066 filename, line_num);
4070 } else if (!strcasecmp(cmd, "LumaElim")) {
4071 get_arg(arg, sizeof(arg), &p);
4073 video_enc.luma_elim_threshold = atoi(arg);
4075 } else if (!strcasecmp(cmd, "ChromaElim")) {
4076 get_arg(arg, sizeof(arg), &p);
4078 video_enc.chroma_elim_threshold = atoi(arg);
4080 } else if (!strcasecmp(cmd, "LumiMask")) {
4081 get_arg(arg, sizeof(arg), &p);
4083 video_enc.lumi_masking = atof(arg);
4085 } else if (!strcasecmp(cmd, "DarkMask")) {
4086 get_arg(arg, sizeof(arg), &p);
4088 video_enc.dark_masking = atof(arg);
4090 } else if (!strcasecmp(cmd, "NoVideo")) {
4091 video_id = CODEC_ID_NONE;
4092 } else if (!strcasecmp(cmd, "NoAudio")) {
4093 audio_id = CODEC_ID_NONE;
4094 } else if (!strcasecmp(cmd, "ACL")) {
4098 get_arg(arg, sizeof(arg), &p);
4099 if (strcasecmp(arg, "allow") == 0) {
4100 acl.action = IP_ALLOW;
4101 } else if (strcasecmp(arg, "deny") == 0) {
4102 acl.action = IP_DENY;
4104 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4105 filename, line_num, arg);
4109 get_arg(arg, sizeof(arg), &p);
4111 he = gethostbyname(arg);
4113 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4114 filename, line_num, arg);
4117 /* Only take the first */
4118 acl.first.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4119 acl.last = acl.first;
4122 get_arg(arg, sizeof(arg), &p);
4125 he = gethostbyname(arg);
4127 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4128 filename, line_num, arg);
4131 /* Only take the first */
4132 acl.last.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4137 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4138 IPAddressACL **naclp = 0;
4144 naclp = &stream->acl;
4148 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4149 filename, line_num);
4155 naclp = &(*naclp)->next;
4160 } else if (!strcasecmp(cmd, "RTSPOption")) {
4161 get_arg(arg, sizeof(arg), &p);
4163 av_freep(&stream->rtsp_option);
4164 /* XXX: av_strdup ? */
4165 stream->rtsp_option = av_malloc(strlen(arg) + 1);
4166 if (stream->rtsp_option) {
4167 strcpy(stream->rtsp_option, arg);
4170 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4171 get_arg(arg, sizeof(arg), &p);
4173 if (!inet_aton(arg, &stream->multicast_ip)) {
4174 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
4175 filename, line_num, arg);
4178 stream->is_multicast = 1;
4179 stream->loop = 1; /* default is looping */
4181 } else if (!strcasecmp(cmd, "MulticastPort")) {
4182 get_arg(arg, sizeof(arg), &p);
4184 stream->multicast_port = atoi(arg);
4186 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4187 get_arg(arg, sizeof(arg), &p);
4189 stream->multicast_ttl = atoi(arg);
4191 } else if (!strcasecmp(cmd, "NoLoop")) {
4195 } else if (!strcasecmp(cmd, "</Stream>")) {
4197 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4198 filename, line_num);
4201 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4202 if (audio_id != CODEC_ID_NONE) {
4203 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4204 audio_enc.codec_id = audio_id;
4205 add_codec(stream, &audio_enc);
4207 if (video_id != CODEC_ID_NONE) {
4208 video_enc.codec_type = CODEC_TYPE_VIDEO;
4209 video_enc.codec_id = video_id;
4210 add_codec(stream, &video_enc);
4214 } else if (!strcasecmp(cmd, "<Redirect")) {
4215 /*********************************************/
4217 if (stream || feed || redirect) {
4218 fprintf(stderr, "%s:%d: Already in a tag\n",
4219 filename, line_num);
4222 redirect = av_mallocz(sizeof(FFStream));
4223 *last_stream = redirect;
4224 last_stream = &redirect->next;
4226 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4227 q = strrchr(redirect->filename, '>');
4230 redirect->stream_type = STREAM_TYPE_REDIRECT;
4232 } else if (!strcasecmp(cmd, "URL")) {
4234 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4236 } else if (!strcasecmp(cmd, "</Redirect>")) {
4238 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4239 filename, line_num);
4242 if (!redirect->feed_filename[0]) {
4243 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4244 filename, line_num);
4248 } else if (!strcasecmp(cmd, "LoadModule")) {
4249 get_arg(arg, sizeof(arg), &p);
4250 #ifdef CONFIG_HAVE_DLOPEN
4253 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4254 filename, line_num, arg);
4258 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4259 filename, line_num, cmd);
4273 static void write_packet(FFCodec *ffenc,
4274 uint8_t *buf, int size)
4277 AVCodecContext *enc = &ffenc->enc;
4279 mk_header(&hdr, enc, size);
4280 wptr = http_fifo.wptr;
4281 fifo_write(&http_fifo, (uint8_t *)&hdr, sizeof(hdr), &wptr);
4282 fifo_write(&http_fifo, buf, size, &wptr);
4283 /* atomic modification of wptr */
4284 http_fifo.wptr = wptr;
4285 ffenc->data_count += size;
4286 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4290 static void help(void)
4292 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4293 "usage: ffserver [-L] [-h] [-f configfile]\n"
4294 "Hyper fast multi format Audio/Video streaming server\n"
4296 "-L : print the LICENCE\n"
4298 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4302 static void licence(void)
4305 "ffserver version " FFMPEG_VERSION "\n"
4306 "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4307 "This library is free software; you can redistribute it and/or\n"
4308 "modify it under the terms of the GNU Lesser General Public\n"
4309 "License as published by the Free Software Foundation; either\n"
4310 "version 2 of the License, or (at your option) any later version.\n"
4312 "This library is distributed in the hope that it will be useful,\n"
4313 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4314 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4315 "Lesser General Public License for more details.\n"
4317 "You should have received a copy of the GNU Lesser General Public\n"
4318 "License along with this library; if not, write to the Free Software\n"
4319 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
4323 static void handle_child_exit(int sig)
4328 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4331 for (feed = first_feed; feed; feed = feed->next) {
4332 if (feed->pid == pid) {
4333 int uptime = time(0) - feed->pid_start;
4336 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4339 /* Turn off any more restarts */
4340 feed->child_argv = 0;
4346 need_to_start_children = 1;
4349 int main(int argc, char **argv)
4351 const char *config_filename;
4353 struct sigaction sigact;
4357 config_filename = "/etc/ffserver.conf";
4359 my_program_name = argv[0];
4360 my_program_dir = getcwd(0, 0);
4361 ffserver_daemon = 1;
4364 c = getopt(argc, argv, "ndLh?f:");
4380 ffserver_daemon = 0;
4383 config_filename = optarg;
4390 putenv("http_proxy"); /* Kill the http_proxy */
4392 srandom(gettime_ms() + (getpid() << 16));
4394 /* address on which the server will handle HTTP connections */
4395 my_http_addr.sin_family = AF_INET;
4396 my_http_addr.sin_port = htons (8080);
4397 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4399 /* address on which the server will handle RTSP connections */
4400 my_rtsp_addr.sin_family = AF_INET;
4401 my_rtsp_addr.sin_port = htons (5454);
4402 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4404 nb_max_connections = 5;
4405 max_bandwidth = 1000;
4406 first_stream = NULL;
4407 logfilename[0] = '\0';
4409 memset(&sigact, 0, sizeof(sigact));
4410 sigact.sa_handler = handle_child_exit;
4411 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4412 sigaction(SIGCHLD, &sigact, 0);
4414 if (parse_ffconfig(config_filename) < 0) {
4415 fprintf(stderr, "Incorrect config file - exiting.\n");
4419 build_file_streams();
4421 build_feed_streams();
4423 compute_bandwidth();
4425 /* put the process in background and detach it from its TTY */
4426 if (ffserver_daemon) {
4433 } else if (pid > 0) {
4441 open("/dev/null", O_RDWR);
4442 if (strcmp(logfilename, "-") != 0) {
4452 signal(SIGPIPE, SIG_IGN);
4454 /* open log file if needed */
4455 if (logfilename[0] != '\0') {
4456 if (!strcmp(logfilename, "-"))
4459 logfile = fopen(logfilename, "w");
4462 if (http_server() < 0) {
4463 fprintf(stderr, "Could not start server\n");