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_options(HTTPContext *c, const char *url);
249 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
250 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
251 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
252 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
255 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
256 struct in_addr my_ip);
259 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
260 FFStream *stream, const char *session_id);
261 static int rtp_new_av_stream(HTTPContext *c,
262 int stream_index, struct sockaddr_in *dest_addr);
264 static const char *my_program_name;
265 static const char *my_program_dir;
267 static int ffserver_debug;
268 static int ffserver_daemon;
269 static int no_launch;
270 static int need_to_start_children;
272 int nb_max_connections;
276 int current_bandwidth;
278 static long cur_time; // Making this global saves on passing it around everywhere
280 static long gettime_ms(void)
284 gettimeofday(&tv,NULL);
285 return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
288 static FILE *logfile = NULL;
290 static void http_log(const char *fmt, ...)
296 vfprintf(logfile, fmt, ap);
302 static char *ctime1(char *buf2)
310 p = buf2 + strlen(p) - 1;
316 static void log_connection(HTTPContext *c)
323 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
324 inet_ntoa(c->from_addr.sin_addr),
325 ctime1(buf2), c->method, c->url,
326 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
329 static void update_datarate(DataRateData *drd, int64_t count)
331 if (!drd->time1 && !drd->count1) {
332 drd->time1 = drd->time2 = cur_time;
333 drd->count1 = drd->count2 = count;
335 if (cur_time - drd->time2 > 5000) {
336 drd->time1 = drd->time2;
337 drd->count1 = drd->count2;
338 drd->time2 = cur_time;
344 /* In bytes per second */
345 static int compute_datarate(DataRateData *drd, int64_t count)
347 if (cur_time == drd->time1)
350 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
353 static int get_longterm_datarate(DataRateData *drd, int64_t count)
355 /* You get the first 3 seconds flat out */
356 if (cur_time - drd->time1 < 3000)
358 return compute_datarate(drd, count);
362 static void start_children(FFStream *feed)
367 for (; feed; feed = feed->next) {
368 if (feed->child_argv && !feed->pid) {
369 feed->pid_start = time(0);
374 fprintf(stderr, "Unable to create children\n");
383 for (i = 3; i < 256; i++) {
387 if (!ffserver_debug) {
388 i = open("/dev/null", O_RDWR);
397 pstrcpy(pathname, sizeof(pathname), my_program_name);
399 slash = strrchr(pathname, '/');
405 strcpy(slash, "ffmpeg");
407 /* This is needed to make relative pathnames work */
408 chdir(my_program_dir);
410 signal(SIGPIPE, SIG_DFL);
412 execvp(pathname, feed->child_argv);
420 /* open a listening socket */
421 static int socket_open_listen(struct sockaddr_in *my_addr)
425 server_fd = socket(AF_INET,SOCK_STREAM,0);
432 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
434 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
436 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
442 if (listen (server_fd, 5) < 0) {
447 fcntl(server_fd, F_SETFL, O_NONBLOCK);
452 /* start all multicast streams */
453 static void start_multicast(void)
458 struct sockaddr_in dest_addr;
459 int default_port, stream_index;
462 for(stream = first_stream; stream != NULL; stream = stream->next) {
463 if (stream->is_multicast) {
464 /* open the RTP connection */
465 snprintf(session_id, sizeof(session_id),
466 "%08x%08x", (int)random(), (int)random());
468 /* choose a port if none given */
469 if (stream->multicast_port == 0) {
470 stream->multicast_port = default_port;
474 dest_addr.sin_family = AF_INET;
475 dest_addr.sin_addr = stream->multicast_ip;
476 dest_addr.sin_port = htons(stream->multicast_port);
478 rtp_c = rtp_new_connection(&dest_addr, stream, session_id);
482 if (open_input_stream(rtp_c, "") < 0) {
483 fprintf(stderr, "Could not open input stream for stream '%s'\n",
488 rtp_c->rtp_protocol = RTSP_PROTOCOL_RTP_UDP_MULTICAST;
490 /* open each RTP stream */
491 for(stream_index = 0; stream_index < stream->nb_streams;
493 dest_addr.sin_port = htons(stream->multicast_port +
495 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
496 fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
497 stream->filename, stream_index);
502 /* change state to send data */
503 rtp_c->state = HTTPSTATE_SEND_DATA;
508 /* main loop of the http server */
509 static int http_server(void)
511 int server_fd, ret, rtsp_server_fd, delay, delay1;
512 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
513 HTTPContext *c, *c_next;
515 server_fd = socket_open_listen(&my_http_addr);
519 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
520 if (rtsp_server_fd < 0)
523 http_log("ffserver started.\n");
525 start_children(first_feed);
527 first_http_ctx = NULL;
529 first_http_ctx = NULL;
534 poll_entry = poll_table;
535 poll_entry->fd = server_fd;
536 poll_entry->events = POLLIN;
539 poll_entry->fd = rtsp_server_fd;
540 poll_entry->events = POLLIN;
543 /* wait for events on each HTTP handle */
550 case HTTPSTATE_SEND_HEADER:
551 case RTSPSTATE_SEND_REPLY:
552 c->poll_entry = poll_entry;
554 poll_entry->events = POLLOUT;
557 case HTTPSTATE_SEND_DATA_HEADER:
558 case HTTPSTATE_SEND_DATA:
559 case HTTPSTATE_SEND_DATA_TRAILER:
560 if (!c->is_packetized) {
561 /* for TCP, we output as much as we can (may need to put a limit) */
562 c->poll_entry = poll_entry;
564 poll_entry->events = POLLOUT;
567 /* not strictly correct, but currently cannot add
568 more than one fd in poll entry */
572 case HTTPSTATE_WAIT_REQUEST:
573 case HTTPSTATE_RECEIVE_DATA:
574 case HTTPSTATE_WAIT_FEED:
575 case RTSPSTATE_WAIT_REQUEST:
576 /* need to catch errors */
577 c->poll_entry = poll_entry;
579 poll_entry->events = POLLIN;/* Maybe this will work */
583 c->poll_entry = NULL;
584 delay1 = compute_send_delay(c);
588 case HTTPSTATE_WAIT_SHORT:
589 c->poll_entry = NULL;
590 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
595 c->poll_entry = NULL;
601 /* wait for an event on one connection. We poll at least every
602 second to handle timeouts */
604 ret = poll(poll_table, poll_entry - poll_table, delay);
607 cur_time = gettime_ms();
609 if (need_to_start_children) {
610 need_to_start_children = 0;
611 start_children(first_feed);
614 /* now handle the events */
615 for(c = first_http_ctx; c != NULL; c = c_next) {
617 if (handle_connection(c) < 0) {
618 /* close and free the connection */
624 poll_entry = poll_table;
625 /* new HTTP connection request ? */
626 if (poll_entry->revents & POLLIN) {
627 new_connection(server_fd, 0);
630 /* new RTSP connection request ? */
631 if (poll_entry->revents & POLLIN) {
632 new_connection(rtsp_server_fd, 1);
637 /* start waiting for a new HTTP/RTSP request */
638 static void start_wait_request(HTTPContext *c, int is_rtsp)
640 c->buffer_ptr = c->buffer;
641 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
644 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
645 c->state = RTSPSTATE_WAIT_REQUEST;
647 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
648 c->state = HTTPSTATE_WAIT_REQUEST;
652 static void new_connection(int server_fd, int is_rtsp)
654 struct sockaddr_in from_addr;
656 HTTPContext *c = NULL;
658 len = sizeof(from_addr);
659 fd = accept(server_fd, (struct sockaddr *)&from_addr,
663 fcntl(fd, F_SETFL, O_NONBLOCK);
665 /* XXX: should output a warning page when coming
666 close to the connection limit */
667 if (nb_connections >= nb_max_connections)
670 /* add a new connection */
671 c = av_mallocz(sizeof(HTTPContext));
675 c->next = first_http_ctx;
678 c->poll_entry = NULL;
679 c->from_addr = from_addr;
680 c->buffer_size = IOBUFFER_INIT_SIZE;
681 c->buffer = av_malloc(c->buffer_size);
686 start_wait_request(c, is_rtsp);
698 static void close_connection(HTTPContext *c)
700 HTTPContext **cp, *c1;
702 AVFormatContext *ctx;
706 /* remove connection from list */
707 cp = &first_http_ctx;
708 while ((*cp) != NULL) {
717 /* remove connection associated resources */
721 /* close each frame parser */
722 for(i=0;i<c->fmt_in->nb_streams;i++) {
723 st = c->fmt_in->streams[i];
724 if (st->codec.codec) {
725 avcodec_close(&st->codec);
728 av_close_input_file(c->fmt_in);
731 /* free RTP output streams if any */
734 nb_streams = c->stream->nb_streams;
736 for(i=0;i<nb_streams;i++) {
739 av_write_trailer(ctx);
742 h = c->rtp_handles[i];
750 for(i=0; i<ctx->nb_streams; i++)
751 av_free(ctx->streams[i]) ;
753 if (!c->last_packet_sent) {
756 if (url_open_dyn_buf(&ctx->pb) >= 0) {
757 av_write_trailer(ctx);
758 (void) url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
764 current_bandwidth -= c->stream->bandwidth;
765 av_freep(&c->pb_buffer);
771 static int handle_connection(HTTPContext *c)
776 case HTTPSTATE_WAIT_REQUEST:
777 case RTSPSTATE_WAIT_REQUEST:
779 if ((c->timeout - cur_time) < 0)
781 if (c->poll_entry->revents & (POLLERR | POLLHUP))
784 /* no need to read if no events */
785 if (!(c->poll_entry->revents & POLLIN))
788 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
790 if (errno != EAGAIN && errno != EINTR)
792 } else if (len == 0) {
795 /* search for end of request. XXX: not fully correct since garbage could come after the end */
797 c->buffer_ptr += len;
799 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
800 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
801 /* request found : parse it and reply */
802 if (c->state == HTTPSTATE_WAIT_REQUEST) {
803 ret = http_parse_request(c);
805 ret = rtsp_parse_request(c);
809 } else if (ptr >= c->buffer_end) {
810 /* request too long: cannot do anything */
816 case HTTPSTATE_SEND_HEADER:
817 if (c->poll_entry->revents & (POLLERR | POLLHUP))
820 /* no need to write if no events */
821 if (!(c->poll_entry->revents & POLLOUT))
823 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
825 if (errno != EAGAIN && errno != EINTR) {
826 /* error : close connection */
827 av_freep(&c->pb_buffer);
831 c->buffer_ptr += len;
833 c->stream->bytes_served += len;
834 c->data_count += len;
835 if (c->buffer_ptr >= c->buffer_end) {
836 av_freep(&c->pb_buffer);
841 /* all the buffer was sent : synchronize to the incoming stream */
842 c->state = HTTPSTATE_SEND_DATA_HEADER;
843 c->buffer_ptr = c->buffer_end = c->buffer;
848 case HTTPSTATE_SEND_DATA:
849 case HTTPSTATE_SEND_DATA_HEADER:
850 case HTTPSTATE_SEND_DATA_TRAILER:
851 /* for packetized output, we consider we can always write (the
852 input streams sets the speed). It may be better to verify
853 that we do not rely too much on the kernel queues */
854 if (!c->is_packetized) {
855 if (c->poll_entry->revents & (POLLERR | POLLHUP))
858 /* no need to read if no events */
859 if (!(c->poll_entry->revents & POLLOUT))
862 if (http_send_data(c) < 0)
865 case HTTPSTATE_RECEIVE_DATA:
866 /* no need to read if no events */
867 if (c->poll_entry->revents & (POLLERR | POLLHUP))
869 if (!(c->poll_entry->revents & POLLIN))
871 if (http_receive_data(c) < 0)
874 case HTTPSTATE_WAIT_FEED:
875 /* no need to read if no events */
876 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
879 /* nothing to do, we'll be waken up by incoming feed packets */
883 /* if the delay expired, we can send new packets */
884 if (compute_send_delay(c) <= 0)
885 c->state = HTTPSTATE_SEND_DATA;
887 case HTTPSTATE_WAIT_SHORT:
888 /* just return back to send data */
889 c->state = HTTPSTATE_SEND_DATA;
892 case RTSPSTATE_SEND_REPLY:
893 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
894 av_freep(&c->pb_buffer);
897 /* no need to write if no events */
898 if (!(c->poll_entry->revents & POLLOUT))
900 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
902 if (errno != EAGAIN && errno != EINTR) {
903 /* error : close connection */
904 av_freep(&c->pb_buffer);
908 c->buffer_ptr += len;
909 c->data_count += len;
910 if (c->buffer_ptr >= c->buffer_end) {
911 /* all the buffer was sent : wait for a new request */
912 av_freep(&c->pb_buffer);
913 start_wait_request(c, 1);
917 case HTTPSTATE_READY:
926 static int extract_rates(char *rates, int ratelen, const char *request)
930 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
931 if (strncasecmp(p, "Pragma:", 7) == 0) {
932 const char *q = p + 7;
934 while (*q && *q != '\n' && isspace(*q))
937 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
943 memset(rates, 0xff, ratelen);
946 while (*q && *q != '\n' && *q != ':')
949 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
953 if (stream_no < ratelen && stream_no >= 0) {
954 rates[stream_no] = rate_no;
957 while (*q && *q != '\n' && !isspace(*q))
974 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
977 int best_bitrate = 100000000;
980 for (i = 0; i < feed->nb_streams; i++) {
981 AVCodecContext *feed_codec = &feed->streams[i]->codec;
983 if (feed_codec->codec_id != codec->codec_id ||
984 feed_codec->sample_rate != codec->sample_rate ||
985 feed_codec->width != codec->width ||
986 feed_codec->height != codec->height) {
990 /* Potential stream */
992 /* We want the fastest stream less than bit_rate, or the slowest
993 * faster than bit_rate
996 if (feed_codec->bit_rate <= bit_rate) {
997 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
998 best_bitrate = feed_codec->bit_rate;
1002 if (feed_codec->bit_rate < best_bitrate) {
1003 best_bitrate = feed_codec->bit_rate;
1012 static int modify_current_stream(HTTPContext *c, char *rates)
1015 FFStream *req = c->stream;
1016 int action_required = 0;
1018 /* Not much we can do for a feed */
1022 for (i = 0; i < req->nb_streams; i++) {
1023 AVCodecContext *codec = &req->streams[i]->codec;
1027 c->switch_feed_streams[i] = req->feed_streams[i];
1030 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1033 /* Wants off or slow */
1034 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1036 /* This doesn't work well when it turns off the only stream! */
1037 c->switch_feed_streams[i] = -2;
1038 c->feed_streams[i] = -2;
1043 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1044 action_required = 1;
1047 return action_required;
1051 static void do_switch_stream(HTTPContext *c, int i)
1053 if (c->switch_feed_streams[i] >= 0) {
1055 c->feed_streams[i] = c->switch_feed_streams[i];
1058 /* Now update the stream */
1060 c->switch_feed_streams[i] = -1;
1063 /* XXX: factorize in utils.c ? */
1064 /* XXX: take care with different space meaning */
1065 static void skip_spaces(const char **pp)
1069 while (*p == ' ' || *p == '\t')
1074 static void get_word(char *buf, int buf_size, const char **pp)
1082 while (!isspace(*p) && *p != '\0') {
1083 if ((q - buf) < buf_size - 1)
1092 static int validate_acl(FFStream *stream, HTTPContext *c)
1094 enum IPAddressAction last_action = IP_DENY;
1096 struct in_addr *src = &c->from_addr.sin_addr;
1097 unsigned long src_addr = ntohl(src->s_addr);
1099 for (acl = stream->acl; acl; acl = acl->next) {
1100 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr) {
1101 return (acl->action == IP_ALLOW) ? 1 : 0;
1103 last_action = acl->action;
1106 /* Nothing matched, so return not the last action */
1107 return (last_action == IP_DENY) ? 1 : 0;
1110 /* compute the real filename of a file by matching it without its
1111 extensions to all the stream filenames */
1112 static void compute_real_filename(char *filename, int max_size)
1119 /* compute filename by matching without the file extensions */
1120 pstrcpy(file1, sizeof(file1), filename);
1121 p = strrchr(file1, '.');
1124 for(stream = first_stream; stream != NULL; stream = stream->next) {
1125 pstrcpy(file2, sizeof(file2), stream->filename);
1126 p = strrchr(file2, '.');
1129 if (!strcmp(file1, file2)) {
1130 pstrcpy(filename, max_size, stream->filename);
1145 /* parse http request and prepare header */
1146 static int http_parse_request(HTTPContext *c)
1150 enum RedirType redir_type;
1152 char info[1024], *filename;
1156 const char *mime_type;
1160 char *useragent = 0;
1163 get_word(cmd, sizeof(cmd), (const char **)&p);
1164 pstrcpy(c->method, sizeof(c->method), cmd);
1166 if (!strcmp(cmd, "GET"))
1168 else if (!strcmp(cmd, "POST"))
1173 get_word(url, sizeof(url), (const char **)&p);
1174 pstrcpy(c->url, sizeof(c->url), url);
1176 get_word(protocol, sizeof(protocol), (const char **)&p);
1177 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1180 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
1182 /* find the filename and the optional info string in the request */
1189 pstrcpy(info, sizeof(info), p);
1195 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1196 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1198 if (*useragent && *useragent != '\n' && isspace(*useragent))
1202 p = strchr(p, '\n');
1209 redir_type = REDIR_NONE;
1210 if (match_ext(filename, "asx")) {
1211 redir_type = REDIR_ASX;
1212 filename[strlen(filename)-1] = 'f';
1213 } else if (match_ext(filename, "asf") &&
1214 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1215 /* if this isn't WMP or lookalike, return the redirector file */
1216 redir_type = REDIR_ASF;
1217 } else if (match_ext(filename, "rpm,ram")) {
1218 redir_type = REDIR_RAM;
1219 strcpy(filename + strlen(filename)-2, "m");
1220 } else if (match_ext(filename, "rtsp")) {
1221 redir_type = REDIR_RTSP;
1222 compute_real_filename(filename, sizeof(url) - 1);
1223 } else if (match_ext(filename, "sdp")) {
1224 redir_type = REDIR_SDP;
1225 compute_real_filename(filename, sizeof(url) - 1);
1228 stream = first_stream;
1229 while (stream != NULL) {
1230 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1232 stream = stream->next;
1234 if (stream == NULL) {
1235 sprintf(msg, "File '%s' not found", url);
1240 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1241 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1243 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1244 c->http_error = 301;
1246 q += sprintf(q, "HTTP/1.0 301 Moved\r\n");
1247 q += sprintf(q, "Location: %s\r\n", stream->feed_filename);
1248 q += sprintf(q, "Content-type: text/html\r\n");
1249 q += sprintf(q, "\r\n");
1250 q += sprintf(q, "<html><head><title>Moved</title></head><body>\r\n");
1251 q += sprintf(q, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1252 q += sprintf(q, "</body></html>\r\n");
1254 /* prepare output buffer */
1255 c->buffer_ptr = c->buffer;
1257 c->state = HTTPSTATE_SEND_HEADER;
1261 /* If this is WMP, get the rate information */
1262 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1263 if (modify_current_stream(c, ratebuf)) {
1264 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1265 if (c->switch_feed_streams[i] >= 0)
1266 do_switch_stream(c, i);
1271 if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1272 current_bandwidth += stream->bandwidth;
1275 if (post == 0 && max_bandwidth < current_bandwidth) {
1276 c->http_error = 200;
1278 q += sprintf(q, "HTTP/1.0 200 Server too busy\r\n");
1279 q += sprintf(q, "Content-type: text/html\r\n");
1280 q += sprintf(q, "\r\n");
1281 q += sprintf(q, "<html><head><title>Too busy</title></head><body>\r\n");
1282 q += sprintf(q, "The server is too busy to serve your request at this time.<p>\r\n");
1283 q += sprintf(q, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
1284 current_bandwidth, max_bandwidth);
1285 q += sprintf(q, "</body></html>\r\n");
1287 /* prepare output buffer */
1288 c->buffer_ptr = c->buffer;
1290 c->state = HTTPSTATE_SEND_HEADER;
1294 if (redir_type != REDIR_NONE) {
1297 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1298 if (strncasecmp(p, "Host:", 5) == 0) {
1302 p = strchr(p, '\n');
1313 while (isspace(*hostinfo))
1316 eoh = strchr(hostinfo, '\n');
1318 if (eoh[-1] == '\r')
1321 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1322 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1323 hostbuf[eoh - hostinfo] = 0;
1325 c->http_error = 200;
1327 switch(redir_type) {
1329 q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n");
1330 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1331 q += sprintf(q, "\r\n");
1332 q += sprintf(q, "<ASX Version=\"3\">\r\n");
1333 q += sprintf(q, "<!-- Autogenerated by ffserver -->\r\n");
1334 q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1335 hostbuf, filename, info);
1336 q += sprintf(q, "</ASX>\r\n");
1339 q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n");
1340 q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n");
1341 q += sprintf(q, "\r\n");
1342 q += sprintf(q, "# Autogenerated by ffserver\r\n");
1343 q += sprintf(q, "http://%s/%s%s\r\n",
1344 hostbuf, filename, info);
1347 q += sprintf(q, "HTTP/1.0 200 ASF Redirect follows\r\n");
1348 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1349 q += sprintf(q, "\r\n");
1350 q += sprintf(q, "[Reference]\r\n");
1351 q += sprintf(q, "Ref1=http://%s/%s%s\r\n",
1352 hostbuf, filename, info);
1356 char hostname[256], *p;
1357 /* extract only hostname */
1358 pstrcpy(hostname, sizeof(hostname), hostbuf);
1359 p = strrchr(hostname, ':');
1362 q += sprintf(q, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1363 /* XXX: incorrect mime type ? */
1364 q += sprintf(q, "Content-type: application/x-rtsp\r\n");
1365 q += sprintf(q, "\r\n");
1366 q += sprintf(q, "rtsp://%s:%d/%s\r\n",
1367 hostname, ntohs(my_rtsp_addr.sin_port),
1374 int sdp_data_size, len;
1375 struct sockaddr_in my_addr;
1377 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1378 q += sprintf(q, "Content-type: application/sdp\r\n");
1379 q += sprintf(q, "\r\n");
1381 len = sizeof(my_addr);
1382 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1384 /* XXX: should use a dynamic buffer */
1385 sdp_data_size = prepare_sdp_description(stream,
1388 if (sdp_data_size > 0) {
1389 memcpy(q, sdp_data, sdp_data_size);
1401 /* prepare output buffer */
1402 c->buffer_ptr = c->buffer;
1404 c->state = HTTPSTATE_SEND_HEADER;
1410 sprintf(msg, "ASX/RAM file not handled");
1414 stream->conns_served++;
1416 /* XXX: add there authenticate and IP match */
1419 /* if post, it means a feed is being sent */
1420 if (!stream->is_feed) {
1421 /* However it might be a status report from WMP! Lets log the data
1422 * as it might come in handy one day
1427 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1428 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1432 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1433 client_id = strtol(p + 18, 0, 10);
1435 p = strchr(p, '\n');
1443 char *eol = strchr(logline, '\n');
1448 if (eol[-1] == '\r')
1450 http_log("%.*s\n", eol - logline, logline);
1451 c->suppress_log = 1;
1456 http_log("\nGot request:\n%s\n", c->buffer);
1459 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1462 /* Now we have to find the client_id */
1463 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1464 if (wmpc->wmp_client_id == client_id)
1469 if (modify_current_stream(wmpc, ratebuf)) {
1470 wmpc->switch_pending = 1;
1475 sprintf(msg, "POST command not handled");
1479 if (http_start_receive_data(c) < 0) {
1480 sprintf(msg, "could not open feed");
1484 c->state = HTTPSTATE_RECEIVE_DATA;
1489 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1490 http_log("\nGot request:\n%s\n", c->buffer);
1494 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1497 /* open input stream */
1498 if (open_input_stream(c, info) < 0) {
1499 sprintf(msg, "Input stream corresponding to '%s' not found", url);
1503 /* prepare http header */
1505 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1506 mime_type = c->stream->fmt->mime_type;
1508 mime_type = "application/x-octet_stream";
1509 q += sprintf(q, "Pragma: no-cache\r\n");
1511 /* for asf, we need extra headers */
1512 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1513 /* Need to allocate a client id */
1515 c->wmp_client_id = random() & 0x7fffffff;
1517 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);
1519 q += sprintf(q, "Content-Type: %s\r\n", mime_type);
1520 q += sprintf(q, "\r\n");
1522 /* prepare output buffer */
1524 c->buffer_ptr = c->buffer;
1526 c->state = HTTPSTATE_SEND_HEADER;
1529 c->http_error = 404;
1531 q += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
1532 q += sprintf(q, "Content-type: %s\r\n", "text/html");
1533 q += sprintf(q, "\r\n");
1534 q += sprintf(q, "<HTML>\n");
1535 q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1536 q += sprintf(q, "<BODY>%s</BODY>\n", msg);
1537 q += sprintf(q, "</HTML>\n");
1539 /* prepare output buffer */
1540 c->buffer_ptr = c->buffer;
1542 c->state = HTTPSTATE_SEND_HEADER;
1546 c->http_error = 200; /* horrible : we use this value to avoid
1547 going to the send data state */
1548 c->state = HTTPSTATE_SEND_HEADER;
1552 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1554 static const char *suffix = " kMGTP";
1557 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1560 url_fprintf(pb, "%lld%c", count, *s);
1563 static void compute_stats(HTTPContext *c)
1570 ByteIOContext pb1, *pb = &pb1;
1572 if (url_open_dyn_buf(pb) < 0) {
1573 /* XXX: return an error ? */
1574 c->buffer_ptr = c->buffer;
1575 c->buffer_end = c->buffer;
1579 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1580 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1581 url_fprintf(pb, "Pragma: no-cache\r\n");
1582 url_fprintf(pb, "\r\n");
1584 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1585 if (c->stream->feed_filename) {
1586 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1588 url_fprintf(pb, "</HEAD>\n<BODY>");
1589 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1591 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1592 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1593 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");
1594 stream = first_stream;
1595 while (stream != NULL) {
1596 char sfilename[1024];
1599 if (stream->feed != stream) {
1600 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1601 eosf = sfilename + strlen(sfilename);
1602 if (eosf - sfilename >= 4) {
1603 if (strcmp(eosf - 4, ".asf") == 0) {
1604 strcpy(eosf - 4, ".asx");
1605 } else if (strcmp(eosf - 3, ".rm") == 0) {
1606 strcpy(eosf - 3, ".ram");
1607 } else if (stream->fmt == &rtp_mux) {
1608 /* generate a sample RTSP director if
1609 unicast. Generate an SDP redirector if
1611 eosf = strrchr(sfilename, '.');
1613 eosf = sfilename + strlen(sfilename);
1614 if (stream->is_multicast)
1615 strcpy(eosf, ".sdp");
1617 strcpy(eosf, ".rtsp");
1621 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1622 sfilename, stream->filename);
1623 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1624 stream->conns_served);
1625 fmt_bytecount(pb, stream->bytes_served);
1626 switch(stream->stream_type) {
1627 case STREAM_TYPE_LIVE:
1629 int audio_bit_rate = 0;
1630 int video_bit_rate = 0;
1631 const char *audio_codec_name = "";
1632 const char *video_codec_name = "";
1633 const char *audio_codec_name_extra = "";
1634 const char *video_codec_name_extra = "";
1636 for(i=0;i<stream->nb_streams;i++) {
1637 AVStream *st = stream->streams[i];
1638 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1639 switch(st->codec.codec_type) {
1640 case CODEC_TYPE_AUDIO:
1641 audio_bit_rate += st->codec.bit_rate;
1643 if (*audio_codec_name)
1644 audio_codec_name_extra = "...";
1645 audio_codec_name = codec->name;
1648 case CODEC_TYPE_VIDEO:
1649 video_bit_rate += st->codec.bit_rate;
1651 if (*video_codec_name)
1652 video_codec_name_extra = "...";
1653 video_codec_name = codec->name;
1660 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",
1663 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1664 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1666 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1668 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1670 url_fprintf(pb, "\n");
1674 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1678 stream = stream->next;
1680 url_fprintf(pb, "</TABLE>\n");
1682 stream = first_stream;
1683 while (stream != NULL) {
1684 if (stream->feed == stream) {
1685 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1687 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1689 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1694 /* This is somewhat linux specific I guess */
1695 snprintf(ps_cmd, sizeof(ps_cmd),
1696 "ps -o \"%%cpu,cputime\" --no-headers %d",
1699 pid_stat = popen(ps_cmd, "r");
1704 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1706 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1714 url_fprintf(pb, "<p>");
1716 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");
1718 for (i = 0; i < stream->nb_streams; i++) {
1719 AVStream *st = stream->streams[i];
1720 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1721 const char *type = "unknown";
1722 char parameters[64];
1726 switch(st->codec.codec_type) {
1727 case CODEC_TYPE_AUDIO:
1730 case CODEC_TYPE_VIDEO:
1732 sprintf(parameters, "%dx%d, q=%d-%d, fps=%d", st->codec.width, st->codec.height,
1733 st->codec.qmin, st->codec.qmax, st->codec.frame_rate / st->codec.frame_rate_base);
1738 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1739 i, type, st->codec.bit_rate/1000, codec ? codec->name : "", parameters);
1741 url_fprintf(pb, "</table>\n");
1744 stream = stream->next;
1750 AVCodecContext *enc;
1754 stream = first_feed;
1755 while (stream != NULL) {
1756 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1757 url_fprintf(pb, "<TABLE>\n");
1758 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1759 for(i=0;i<stream->nb_streams;i++) {
1760 AVStream *st = stream->streams[i];
1761 FeedData *fdata = st->priv_data;
1764 avcodec_string(buf, sizeof(buf), enc);
1765 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1766 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1767 avg /= enc->frame_size;
1768 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1769 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1771 url_fprintf(pb, "</TABLE>\n");
1772 stream = stream->next_feed;
1777 /* connection status */
1778 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1780 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1781 nb_connections, nb_max_connections);
1783 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1784 current_bandwidth, max_bandwidth);
1786 url_fprintf(pb, "<TABLE>\n");
1787 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");
1788 c1 = first_http_ctx;
1790 while (c1 != NULL) {
1796 for (j = 0; j < c1->stream->nb_streams; j++) {
1797 if (!c1->stream->feed) {
1798 bitrate += c1->stream->streams[j]->codec.bit_rate;
1800 if (c1->feed_streams[j] >= 0) {
1801 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec.bit_rate;
1808 p = inet_ntoa(c1->from_addr.sin_addr);
1809 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1811 c1->stream ? c1->stream->filename : "",
1812 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1815 http_state[c1->state]);
1816 fmt_bytecount(pb, bitrate);
1817 url_fprintf(pb, "<td align=right>");
1818 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1819 url_fprintf(pb, "<td align=right>");
1820 fmt_bytecount(pb, c1->data_count);
1821 url_fprintf(pb, "\n");
1824 url_fprintf(pb, "</TABLE>\n");
1829 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1830 url_fprintf(pb, "</BODY>\n</HTML>\n");
1832 len = url_close_dyn_buf(pb, &c->pb_buffer);
1833 c->buffer_ptr = c->pb_buffer;
1834 c->buffer_end = c->pb_buffer + len;
1837 /* check if the parser needs to be opened for stream i */
1838 static void open_parser(AVFormatContext *s, int i)
1840 AVStream *st = s->streams[i];
1843 if (!st->codec.codec) {
1844 codec = avcodec_find_decoder(st->codec.codec_id);
1845 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1846 st->codec.parse_only = 1;
1847 if (avcodec_open(&st->codec, codec) < 0) {
1848 st->codec.parse_only = 0;
1854 static int open_input_stream(HTTPContext *c, const char *info)
1857 char input_filename[1024];
1862 /* find file name */
1863 if (c->stream->feed) {
1864 strcpy(input_filename, c->stream->feed->feed_filename);
1865 buf_size = FFM_PACKET_SIZE;
1866 /* compute position (absolute time) */
1867 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1868 stream_pos = parse_date(buf, 0);
1869 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1870 int prebuffer = strtol(buf, 0, 10);
1871 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1873 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1876 strcpy(input_filename, c->stream->feed_filename);
1878 /* compute position (relative time) */
1879 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1880 stream_pos = parse_date(buf, 1);
1885 if (input_filename[0] == '\0')
1889 { time_t when = stream_pos / 1000000;
1890 http_log("Stream pos = %lld, time=%s", stream_pos, ctime(&when));
1895 if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0) {
1896 http_log("%s not found", input_filename);
1901 /* open each parser */
1902 for(i=0;i<s->nb_streams;i++)
1905 /* choose stream as clock source (we favorize video stream if
1906 present) for packet sending */
1907 c->pts_stream_index = 0;
1908 for(i=0;i<c->stream->nb_streams;i++) {
1909 if (c->pts_stream_index == 0 &&
1910 c->stream->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO) {
1911 c->pts_stream_index = i;
1915 if (c->fmt_in->iformat->read_seek) {
1916 c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
1918 /* set the start time (needed for maxtime and RTP packet timing) */
1919 c->start_time = cur_time;
1920 c->first_pts = AV_NOPTS_VALUE;
1924 /* currently desactivated because the new PTS handling is not
1926 //#define AV_READ_FRAME
1927 #ifdef AV_READ_FRAME
1929 /* XXX: generalize that in ffmpeg for picture/audio/data. Currently
1930 the return packet MUST NOT be freed */
1931 int av_read_frame(AVFormatContext *s, AVPacket *pkt)
1934 int len, ret, old_nb_streams, i;
1936 /* see if remaining frames must be parsed */
1938 if (s->cur_len > 0) {
1939 st = s->streams[s->cur_pkt.stream_index];
1940 len = avcodec_parse_frame(&st->codec, &pkt->data, &pkt->size,
1941 s->cur_ptr, s->cur_len);
1943 /* error: get next packet */
1949 /* init pts counter if not done */
1950 if (st->pts.den == 0) {
1951 switch(st->codec.codec_type) {
1952 case CODEC_TYPE_AUDIO:
1953 st->pts_incr = (int64_t)s->pts_den;
1954 av_frac_init(&st->pts, st->pts.val, 0,
1955 (int64_t)s->pts_num * st->codec.sample_rate);
1957 case CODEC_TYPE_VIDEO:
1958 st->pts_incr = (int64_t)s->pts_den * st->codec.frame_rate_base;
1959 av_frac_init(&st->pts, st->pts.val, 0,
1960 (int64_t)s->pts_num * st->codec.frame_rate);
1967 /* a frame was read: return it */
1968 pkt->pts = st->pts.val;
1970 printf("add pts=%Lx num=%Lx den=%Lx incr=%Lx\n",
1971 st->pts.val, st->pts.num, st->pts.den, st->pts_incr);
1973 switch(st->codec.codec_type) {
1974 case CODEC_TYPE_AUDIO:
1975 av_frac_add(&st->pts, st->pts_incr * st->codec.frame_size);
1977 case CODEC_TYPE_VIDEO:
1978 av_frac_add(&st->pts, st->pts_incr);
1983 pkt->stream_index = s->cur_pkt.stream_index;
1984 /* we use the codec indication because it is
1985 more accurate than the demux flags */
1987 if (st->codec.coded_frame->key_frame)
1988 pkt->flags |= PKT_FLAG_KEY;
1993 /* free previous packet */
1994 av_free_packet(&s->cur_pkt);
1996 old_nb_streams = s->nb_streams;
1997 ret = av_read_packet(s, &s->cur_pkt);
2000 /* open parsers for each new streams */
2001 for(i = old_nb_streams; i < s->nb_streams; i++)
2003 st = s->streams[s->cur_pkt.stream_index];
2005 /* update current pts (XXX: dts handling) from packet, or
2006 use current pts if none given */
2007 if (s->cur_pkt.pts != AV_NOPTS_VALUE) {
2008 av_frac_set(&st->pts, s->cur_pkt.pts);
2010 s->cur_pkt.pts = st->pts.val;
2012 if (!st->codec.codec) {
2013 /* no codec opened: just return the raw packet */
2016 /* no codec opened: just update the pts by considering we
2017 have one frame and free the packet */
2018 if (st->pts.den == 0) {
2019 switch(st->codec.codec_type) {
2020 case CODEC_TYPE_AUDIO:
2021 st->pts_incr = (int64_t)s->pts_den * st->codec.frame_size;
2022 av_frac_init(&st->pts, st->pts.val, 0,
2023 (int64_t)s->pts_num * st->codec.sample_rate);
2025 case CODEC_TYPE_VIDEO:
2026 st->pts_incr = (int64_t)s->pts_den * st->codec.frame_rate_base;
2027 av_frac_init(&st->pts, st->pts.val, 0,
2028 (int64_t)s->pts_num * st->codec.frame_rate);
2034 av_frac_add(&st->pts, st->pts_incr);
2037 s->cur_ptr = s->cur_pkt.data;
2038 s->cur_len = s->cur_pkt.size;
2044 static int compute_send_delay(HTTPContext *c)
2046 int64_t cur_pts, delta_pts, next_pts;
2049 /* compute current pts value from system time */
2050 cur_pts = ((int64_t)(cur_time - c->start_time) * c->fmt_in->pts_den) /
2051 (c->fmt_in->pts_num * 1000LL);
2052 /* compute the delta from the stream we choose as
2053 main clock (we do that to avoid using explicit
2054 buffers to do exact packet reordering for each
2056 /* XXX: really need to fix the number of streams */
2057 if (c->pts_stream_index >= c->fmt_in->nb_streams)
2060 next_pts = c->fmt_in->streams[c->pts_stream_index]->pts.val;
2061 delta_pts = next_pts - cur_pts;
2062 if (delta_pts <= 0) {
2065 delay1 = (delta_pts * 1000 * c->fmt_in->pts_num) / c->fmt_in->pts_den;
2071 /* just fall backs */
2072 static int av_read_frame(AVFormatContext *s, AVPacket *pkt)
2074 return av_read_packet(s, pkt);
2077 static int compute_send_delay(HTTPContext *c)
2079 int datarate = 8 * get_longterm_datarate(&c->datarate, c->data_count);
2081 if (datarate > c->stream->bandwidth * 2000) {
2089 static int http_prepare_data(HTTPContext *c)
2092 AVFormatContext *ctx;
2095 case HTTPSTATE_SEND_DATA_HEADER:
2096 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2097 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
2099 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
2100 c->stream->comment);
2101 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
2102 c->stream->copyright);
2103 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
2106 /* open output stream by using specified codecs */
2107 c->fmt_ctx.oformat = c->stream->fmt;
2108 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2109 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2111 st = av_mallocz(sizeof(AVStream));
2112 c->fmt_ctx.streams[i] = st;
2113 /* if file or feed, then just take streams from FFStream struct */
2114 if (!c->stream->feed ||
2115 c->stream->feed == c->stream)
2116 memcpy(st, c->stream->streams[i], sizeof(AVStream));
2118 memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]],
2120 st->codec.frame_number = 0; /* XXX: should be done in
2121 AVStream, not in codec */
2122 /* I'm pretty sure that this is not correct...
2123 * However, without it, we crash
2125 st->codec.coded_frame = &dummy_frame;
2127 c->got_key_frame = 0;
2129 /* prepare header and save header data in a stream */
2130 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2131 /* XXX: potential leak */
2134 c->fmt_ctx.pb.is_streamed = 1;
2136 av_set_parameters(&c->fmt_ctx, NULL);
2137 av_write_header(&c->fmt_ctx);
2139 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2140 c->buffer_ptr = c->pb_buffer;
2141 c->buffer_end = c->pb_buffer + len;
2143 c->state = HTTPSTATE_SEND_DATA;
2144 c->last_packet_sent = 0;
2146 case HTTPSTATE_SEND_DATA:
2147 /* find a new packet */
2151 /* read a packet from the input stream */
2152 if (c->stream->feed) {
2153 ffm_set_write_index(c->fmt_in,
2154 c->stream->feed->feed_write_index,
2155 c->stream->feed->feed_size);
2158 if (c->stream->max_time &&
2159 c->stream->max_time + c->start_time - cur_time < 0) {
2160 /* We have timed out */
2161 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2163 if (1 || c->is_packetized) {
2164 if (compute_send_delay(c) > 0) {
2165 c->state = HTTPSTATE_WAIT;
2166 return 1; /* state changed */
2170 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2171 if (c->stream->feed && c->stream->feed->feed_opened) {
2172 /* if coming from feed, it means we reached the end of the
2173 ffm file, so must wait for more data */
2174 c->state = HTTPSTATE_WAIT_FEED;
2175 return 1; /* state changed */
2177 if (c->stream->loop) {
2178 av_close_input_file(c->fmt_in);
2180 if (open_input_stream(c, "") < 0)
2185 /* must send trailer now because eof or error */
2186 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2190 /* update first pts if needed */
2191 if (c->first_pts == AV_NOPTS_VALUE)
2192 c->first_pts = pkt.pts;
2194 /* send it to the appropriate stream */
2195 if (c->stream->feed) {
2196 /* if coming from a feed, select the right stream */
2197 if (c->switch_pending) {
2198 c->switch_pending = 0;
2199 for(i=0;i<c->stream->nb_streams;i++) {
2200 if (c->switch_feed_streams[i] == pkt.stream_index) {
2201 if (pkt.flags & PKT_FLAG_KEY) {
2202 do_switch_stream(c, i);
2205 if (c->switch_feed_streams[i] >= 0) {
2206 c->switch_pending = 1;
2210 for(i=0;i<c->stream->nb_streams;i++) {
2211 if (c->feed_streams[i] == pkt.stream_index) {
2212 pkt.stream_index = i;
2213 if (pkt.flags & PKT_FLAG_KEY) {
2214 c->got_key_frame |= 1 << i;
2216 /* See if we have all the key frames, then
2217 * we start to send. This logic is not quite
2218 * right, but it works for the case of a
2219 * single video stream with one or more
2220 * audio streams (for which every frame is
2221 * typically a key frame).
2223 if (!c->stream->send_on_key ||
2224 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2230 AVCodecContext *codec;
2233 /* specific handling for RTP: we use several
2234 output stream (one for each RTP
2235 connection). XXX: need more abstract handling */
2236 if (c->is_packetized) {
2237 c->packet_stream_index = pkt.stream_index;
2238 ctx = c->rtp_ctx[c->packet_stream_index];
2239 codec = &ctx->streams[0]->codec;
2240 /* only one stream per RTP connection */
2241 pkt.stream_index = 0;
2245 codec = &ctx->streams[pkt.stream_index]->codec;
2248 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2251 if (codec->codec_type == CODEC_TYPE_AUDIO) {
2252 codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
2253 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
2257 if (c->is_packetized) {
2258 ret = url_open_dyn_packet_buf(&ctx->pb,
2259 url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]));
2260 c->packet_byte_count = 0;
2261 c->packet_start_time_us = av_gettime();
2263 ret = url_open_dyn_buf(&ctx->pb);
2266 /* XXX: potential leak */
2269 if (av_write_frame(ctx, pkt.stream_index, pkt.data, pkt.size)) {
2270 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2273 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2274 c->buffer_ptr = c->pb_buffer;
2275 c->buffer_end = c->pb_buffer + len;
2277 codec->frame_number++;
2279 #ifndef AV_READ_FRAME
2280 av_free_packet(&pkt);
2287 case HTTPSTATE_SEND_DATA_TRAILER:
2288 /* last packet test ? */
2289 if (c->last_packet_sent || c->is_packetized)
2292 /* prepare header */
2293 if (url_open_dyn_buf(&ctx->pb) < 0) {
2294 /* XXX: potential leak */
2297 av_write_trailer(ctx);
2298 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2299 c->buffer_ptr = c->pb_buffer;
2300 c->buffer_end = c->pb_buffer + len;
2302 c->last_packet_sent = 1;
2309 #define SHORT_TERM_BANDWIDTH 8000000
2311 /* should convert the format at the same time */
2312 static int http_send_data(HTTPContext *c)
2316 while (c->buffer_ptr >= c->buffer_end) {
2317 av_freep(&c->pb_buffer);
2318 ret = http_prepare_data(c);
2321 else if (ret == 0) {
2324 /* state change requested */
2329 if (c->buffer_ptr < c->buffer_end) {
2330 if (c->is_packetized) {
2331 /* RTP/UDP data output */
2332 len = c->buffer_end - c->buffer_ptr;
2334 /* fail safe - should never happen */
2336 c->buffer_ptr = c->buffer_end;
2339 len = (c->buffer_ptr[0] << 24) |
2340 (c->buffer_ptr[1] << 16) |
2341 (c->buffer_ptr[2] << 8) |
2343 if (len > (c->buffer_end - c->buffer_ptr))
2346 /* short term bandwidth limitation */
2347 dt = av_gettime() - c->packet_start_time_us;
2351 if ((c->packet_byte_count + len) * (int64_t)1000000 >=
2352 (SHORT_TERM_BANDWIDTH / 8) * (int64_t)dt) {
2353 /* bandwidth overflow : wait at most one tick and retry */
2354 c->state = HTTPSTATE_WAIT_SHORT;
2359 url_write(c->rtp_handles[c->packet_stream_index],
2360 c->buffer_ptr, len);
2361 c->buffer_ptr += len;
2362 c->packet_byte_count += len;
2364 /* TCP data output */
2365 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2367 if (errno != EAGAIN && errno != EINTR) {
2368 /* error : close connection */
2374 c->buffer_ptr += len;
2377 c->data_count += len;
2378 update_datarate(&c->datarate, c->data_count);
2380 c->stream->bytes_served += len;
2385 static int http_start_receive_data(HTTPContext *c)
2389 if (c->stream->feed_opened)
2393 fd = open(c->stream->feed_filename, O_RDWR);
2398 c->stream->feed_write_index = ffm_read_write_index(fd);
2399 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2400 lseek(fd, 0, SEEK_SET);
2402 /* init buffer input */
2403 c->buffer_ptr = c->buffer;
2404 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2405 c->stream->feed_opened = 1;
2409 static int http_receive_data(HTTPContext *c)
2413 if (c->buffer_end > c->buffer_ptr) {
2416 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2418 if (errno != EAGAIN && errno != EINTR) {
2419 /* error : close connection */
2422 } else if (len == 0) {
2423 /* end of connection : close it */
2426 c->buffer_ptr += len;
2427 c->data_count += len;
2428 update_datarate(&c->datarate, c->data_count);
2432 if (c->buffer_ptr >= c->buffer_end) {
2433 FFStream *feed = c->stream;
2434 /* a packet has been received : write it in the store, except
2436 if (c->data_count > FFM_PACKET_SIZE) {
2438 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2439 /* XXX: use llseek or url_seek */
2440 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2441 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2443 feed->feed_write_index += FFM_PACKET_SIZE;
2444 /* update file size */
2445 if (feed->feed_write_index > c->stream->feed_size)
2446 feed->feed_size = feed->feed_write_index;
2448 /* handle wrap around if max file size reached */
2449 if (feed->feed_write_index >= c->stream->feed_max_size)
2450 feed->feed_write_index = FFM_PACKET_SIZE;
2453 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2455 /* wake up any waiting connections */
2456 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2457 if (c1->state == HTTPSTATE_WAIT_FEED &&
2458 c1->stream->feed == c->stream->feed) {
2459 c1->state = HTTPSTATE_SEND_DATA;
2463 /* We have a header in our hands that contains useful data */
2465 AVInputFormat *fmt_in;
2466 ByteIOContext *pb = &s.pb;
2469 memset(&s, 0, sizeof(s));
2471 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2472 pb->buf_end = c->buffer_end; /* ?? */
2473 pb->is_streamed = 1;
2475 /* use feed output format name to find corresponding input format */
2476 fmt_in = av_find_input_format(feed->fmt->name);
2480 if (fmt_in->priv_data_size > 0) {
2481 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2487 if (fmt_in->read_header(&s, 0) < 0) {
2488 av_freep(&s.priv_data);
2492 /* Now we have the actual streams */
2493 if (s.nb_streams != feed->nb_streams) {
2494 av_freep(&s.priv_data);
2497 for (i = 0; i < s.nb_streams; i++) {
2498 memcpy(&feed->streams[i]->codec,
2499 &s.streams[i]->codec, sizeof(AVCodecContext));
2501 av_freep(&s.priv_data);
2503 c->buffer_ptr = c->buffer;
2508 c->stream->feed_opened = 0;
2513 /********************************************************************/
2516 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2523 switch(error_number) {
2524 #define DEF(n, c, s) case c: str = s; break;
2525 #include "rtspcodes.h"
2528 str = "Unknown Error";
2532 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2533 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2535 /* output GMT time */
2539 p = buf2 + strlen(p) - 1;
2542 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2545 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2547 rtsp_reply_header(c, error_number);
2548 url_fprintf(c->pb, "\r\n");
2551 static int rtsp_parse_request(HTTPContext *c)
2553 const char *p, *p1, *p2;
2560 RTSPHeader header1, *header = &header1;
2562 c->buffer_ptr[0] = '\0';
2565 get_word(cmd, sizeof(cmd), &p);
2566 get_word(url, sizeof(url), &p);
2567 get_word(protocol, sizeof(protocol), &p);
2569 pstrcpy(c->method, sizeof(c->method), cmd);
2570 pstrcpy(c->url, sizeof(c->url), url);
2571 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2574 if (url_open_dyn_buf(c->pb) < 0) {
2575 /* XXX: cannot do more */
2576 c->pb = NULL; /* safety */
2580 /* check version name */
2581 if (strcmp(protocol, "RTSP/1.0") != 0) {
2582 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2586 /* parse each header line */
2587 memset(header, 0, sizeof(RTSPHeader));
2588 /* skip to next line */
2589 while (*p != '\n' && *p != '\0')
2593 while (*p != '\0') {
2594 p1 = strchr(p, '\n');
2598 if (p2 > p && p2[-1] == '\r')
2600 /* skip empty line */
2604 if (len > sizeof(line) - 1)
2605 len = sizeof(line) - 1;
2606 memcpy(line, p, len);
2608 rtsp_parse_line(header, line);
2612 /* handle sequence number */
2613 c->seq = header->seq;
2615 if (!strcmp(cmd, "DESCRIBE")) {
2616 rtsp_cmd_describe(c, url);
2617 } else if (!strcmp(cmd, "OPTIONS")) {
2618 rtsp_cmd_options(c, url);
2619 } else if (!strcmp(cmd, "SETUP")) {
2620 rtsp_cmd_setup(c, url, header);
2621 } else if (!strcmp(cmd, "PLAY")) {
2622 rtsp_cmd_play(c, url, header);
2623 } else if (!strcmp(cmd, "PAUSE")) {
2624 rtsp_cmd_pause(c, url, header);
2625 } else if (!strcmp(cmd, "TEARDOWN")) {
2626 rtsp_cmd_teardown(c, url, header);
2628 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2631 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2632 c->pb = NULL; /* safety */
2634 /* XXX: cannot do more */
2637 c->buffer_ptr = c->pb_buffer;
2638 c->buffer_end = c->pb_buffer + len;
2639 c->state = RTSPSTATE_SEND_REPLY;
2643 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2645 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2646 struct in_addr my_ip)
2648 ByteIOContext pb1, *pb = &pb1;
2649 int i, payload_type, port, private_payload_type, j;
2650 const char *ipstr, *title, *mediatype;
2653 if (url_open_dyn_buf(pb) < 0)
2656 /* general media info */
2658 url_fprintf(pb, "v=0\n");
2659 ipstr = inet_ntoa(my_ip);
2660 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2661 title = stream->title;
2662 if (title[0] == '\0')
2664 url_fprintf(pb, "s=%s\n", title);
2665 if (stream->comment[0] != '\0')
2666 url_fprintf(pb, "i=%s\n", stream->comment);
2667 if (stream->is_multicast) {
2668 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2670 /* for each stream, we output the necessary info */
2671 private_payload_type = 96;
2672 for(i = 0; i < stream->nb_streams; i++) {
2673 st = stream->streams[i];
2674 switch(st->codec.codec_type) {
2675 case CODEC_TYPE_AUDIO:
2676 mediatype = "audio";
2678 case CODEC_TYPE_VIDEO:
2679 mediatype = "video";
2682 mediatype = "application";
2685 /* NOTE: the port indication is not correct in case of
2686 unicast. It is not an issue because RTSP gives it */
2687 payload_type = rtp_get_payload_type(&st->codec);
2688 if (payload_type < 0)
2689 payload_type = private_payload_type++;
2690 if (stream->is_multicast) {
2691 port = stream->multicast_port + 2 * i;
2695 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2696 mediatype, port, payload_type);
2697 if (payload_type >= 96) {
2698 /* for private payload type, we need to give more info */
2699 switch(st->codec.codec_id) {
2700 case CODEC_ID_MPEG4:
2703 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2704 payload_type, 90000);
2705 /* we must also add the mpeg4 header */
2706 data = st->codec.extradata;
2708 url_fprintf(pb, "a=fmtp:%d config=");
2709 for(j=0;j<st->codec.extradata_size;j++) {
2710 url_fprintf(pb, "%02x", data[j]);
2712 url_fprintf(pb, "\n");
2717 /* XXX: add other codecs ? */
2721 url_fprintf(pb, "a=control:streamid=%d\n", i);
2723 return url_close_dyn_buf(pb, pbuffer);
2725 url_close_dyn_buf(pb, pbuffer);
2730 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2732 // rtsp_reply_header(c, RTSP_STATUS_OK);
2733 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2734 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2735 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2736 url_fprintf(c->pb, "\r\n");
2739 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2745 int content_length, len;
2746 struct sockaddr_in my_addr;
2748 /* find which url is asked */
2749 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2754 for(stream = first_stream; stream != NULL; stream = stream->next) {
2755 if (!stream->is_feed && stream->fmt == &rtp_mux &&
2756 !strcmp(path, stream->filename)) {
2760 /* no stream found */
2761 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2765 /* prepare the media description in sdp format */
2767 /* get the host IP */
2768 len = sizeof(my_addr);
2769 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2771 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2772 if (content_length < 0) {
2773 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2776 rtsp_reply_header(c, RTSP_STATUS_OK);
2777 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2778 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2779 url_fprintf(c->pb, "\r\n");
2780 put_buffer(c->pb, content, content_length);
2783 static HTTPContext *find_rtp_session(const char *session_id)
2787 if (session_id[0] == '\0')
2790 for(c = first_http_ctx; c != NULL; c = c->next) {
2791 if (!strcmp(c->session_id, session_id))
2797 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2799 RTSPTransportField *th;
2802 for(i=0;i<h->nb_transports;i++) {
2803 th = &h->transports[i];
2804 if (th->protocol == protocol)
2810 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2814 int stream_index, port;
2819 RTSPTransportField *th;
2820 struct sockaddr_in dest_addr;
2821 RTSPActionServerSetup setup;
2823 /* find which url is asked */
2824 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2829 /* now check each stream */
2830 for(stream = first_stream; stream != NULL; stream = stream->next) {
2831 if (!stream->is_feed && stream->fmt == &rtp_mux) {
2832 /* accept aggregate filenames only if single stream */
2833 if (!strcmp(path, stream->filename)) {
2834 if (stream->nb_streams != 1) {
2835 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2842 for(stream_index = 0; stream_index < stream->nb_streams;
2844 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2845 stream->filename, stream_index);
2846 if (!strcmp(path, buf))
2851 /* no stream found */
2852 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2856 /* generate session id if needed */
2857 if (h->session_id[0] == '\0') {
2858 snprintf(h->session_id, sizeof(h->session_id),
2859 "%08x%08x", (int)random(), (int)random());
2862 /* find rtp session, and create it if none found */
2863 rtp_c = find_rtp_session(h->session_id);
2865 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id);
2867 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2871 /* open input stream */
2872 if (open_input_stream(rtp_c, "") < 0) {
2873 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2877 /* always prefer UDP */
2878 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2880 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2882 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2886 rtp_c->rtp_protocol = th->protocol;
2889 /* test if stream is OK (test needed because several SETUP needs
2890 to be done for a given file) */
2891 if (rtp_c->stream != stream) {
2892 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2896 /* test if stream is already set up */
2897 if (rtp_c->rtp_ctx[stream_index]) {
2898 rtsp_reply_error(c, RTSP_STATUS_STATE);
2902 /* check transport */
2903 th = find_transport(h, rtp_c->rtp_protocol);
2904 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2905 th->client_port_min <= 0)) {
2906 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2910 /* setup default options */
2911 setup.transport_option[0] = '\0';
2912 dest_addr = rtp_c->from_addr;
2913 dest_addr.sin_port = htons(th->client_port_min);
2915 /* add transport option if needed */
2916 if (ff_rtsp_callback) {
2917 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2918 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2919 (char *)&setup, sizeof(setup),
2920 stream->rtsp_option) < 0) {
2921 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2924 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2928 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
2929 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2933 /* now everything is OK, so we can send the connection parameters */
2934 rtsp_reply_header(c, RTSP_STATUS_OK);
2936 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2938 switch(rtp_c->rtp_protocol) {
2939 case RTSP_PROTOCOL_RTP_UDP:
2940 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2941 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2942 "client_port=%d-%d;server_port=%d-%d",
2943 th->client_port_min, th->client_port_min + 1,
2946 case RTSP_PROTOCOL_RTP_TCP:
2947 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2948 stream_index * 2, stream_index * 2 + 1);
2953 if (setup.transport_option[0] != '\0') {
2954 url_fprintf(c->pb, ";%s", setup.transport_option);
2956 url_fprintf(c->pb, "\r\n");
2959 url_fprintf(c->pb, "\r\n");
2963 /* find an rtp connection by using the session ID. Check consistency
2965 static HTTPContext *find_rtp_session_with_url(const char *url,
2966 const char *session_id)
2972 rtp_c = find_rtp_session(session_id);
2976 /* find which url is asked */
2977 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2981 if (strcmp(path, rtp_c->stream->filename) != 0)
2986 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2990 rtp_c = find_rtp_session_with_url(url, h->session_id);
2992 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2996 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2997 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2998 rtp_c->state != HTTPSTATE_READY) {
2999 rtsp_reply_error(c, RTSP_STATUS_STATE);
3003 rtp_c->state = HTTPSTATE_SEND_DATA;
3005 /* now everything is OK, so we can send the connection parameters */
3006 rtsp_reply_header(c, RTSP_STATUS_OK);
3008 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3009 url_fprintf(c->pb, "\r\n");
3012 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3016 rtp_c = find_rtp_session_with_url(url, h->session_id);
3018 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3022 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3023 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3024 rtsp_reply_error(c, RTSP_STATUS_STATE);
3028 rtp_c->state = HTTPSTATE_READY;
3030 /* now everything is OK, so we can send the connection parameters */
3031 rtsp_reply_header(c, RTSP_STATUS_OK);
3033 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3034 url_fprintf(c->pb, "\r\n");
3037 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3041 rtp_c = find_rtp_session_with_url(url, h->session_id);
3043 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3047 /* abort the session */
3048 close_connection(rtp_c);
3050 if (ff_rtsp_callback) {
3051 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
3053 rtp_c->stream->rtsp_option);
3056 /* now everything is OK, so we can send the connection parameters */
3057 rtsp_reply_header(c, RTSP_STATUS_OK);
3059 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3060 url_fprintf(c->pb, "\r\n");
3064 /********************************************************************/
3067 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3068 FFStream *stream, const char *session_id)
3070 HTTPContext *c = NULL;
3072 /* XXX: should output a warning page when coming
3073 close to the connection limit */
3074 if (nb_connections >= nb_max_connections)
3077 /* add a new connection */
3078 c = av_mallocz(sizeof(HTTPContext));
3083 c->poll_entry = NULL;
3084 c->from_addr = *from_addr;
3085 c->buffer_size = IOBUFFER_INIT_SIZE;
3086 c->buffer = av_malloc(c->buffer_size);
3091 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
3092 c->state = HTTPSTATE_READY;
3093 c->is_packetized = 1;
3094 /* protocol is shown in statistics */
3095 pstrcpy(c->protocol, sizeof(c->protocol), "RTP");
3097 current_bandwidth += stream->bandwidth;
3099 c->next = first_http_ctx;
3111 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3112 command). if dest_addr is NULL, then TCP tunneling in RTSP is
3114 static int rtp_new_av_stream(HTTPContext *c,
3115 int stream_index, struct sockaddr_in *dest_addr)
3117 AVFormatContext *ctx;
3124 /* now we can open the relevant output stream */
3125 ctx = av_mallocz(sizeof(AVFormatContext));
3128 ctx->oformat = &rtp_mux;
3130 st = av_mallocz(sizeof(AVStream));
3133 ctx->nb_streams = 1;
3134 ctx->streams[0] = st;
3136 if (!c->stream->feed ||
3137 c->stream->feed == c->stream) {
3138 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3141 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3146 /* build destination RTP address */
3147 ipaddr = inet_ntoa(dest_addr->sin_addr);
3149 /* XXX: also pass as parameter to function ? */
3150 if (c->stream->is_multicast) {
3152 ttl = c->stream->multicast_ttl;
3155 snprintf(ctx->filename, sizeof(ctx->filename),
3156 "rtp://%s:%d?multicast=1&ttl=%d",
3157 ipaddr, ntohs(dest_addr->sin_port), ttl);
3159 snprintf(ctx->filename, sizeof(ctx->filename),
3160 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3163 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3165 c->rtp_handles[stream_index] = h;
3170 http_log("%s:%d - - [%s] \"RTPSTART %s/streamid=%d\"\n",
3171 ipaddr, ntohs(dest_addr->sin_port),
3173 c->stream->filename, stream_index);
3175 /* normally, no packets should be output here, but the packet size may be checked */
3176 if (url_open_dyn_packet_buf(&ctx->pb,
3177 url_get_max_packet_size(h)) < 0) {
3178 /* XXX: close stream */
3181 av_set_parameters(ctx, NULL);
3182 if (av_write_header(ctx) < 0) {
3189 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3192 c->rtp_ctx[stream_index] = ctx;
3196 /********************************************************************/
3197 /* ffserver initialization */
3199 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3203 fst = av_mallocz(sizeof(AVStream));
3206 fst->priv_data = av_mallocz(sizeof(FeedData));
3207 memcpy(&fst->codec, codec, sizeof(AVCodecContext));
3208 fst->codec.coded_frame = &dummy_frame;
3209 stream->streams[stream->nb_streams++] = fst;
3213 /* return the stream number in the feed */
3214 static int add_av_stream(FFStream *feed, AVStream *st)
3217 AVCodecContext *av, *av1;
3221 for(i=0;i<feed->nb_streams;i++) {
3222 st = feed->streams[i];
3224 if (av1->codec_id == av->codec_id &&
3225 av1->codec_type == av->codec_type &&
3226 av1->bit_rate == av->bit_rate) {
3228 switch(av->codec_type) {
3229 case CODEC_TYPE_AUDIO:
3230 if (av1->channels == av->channels &&
3231 av1->sample_rate == av->sample_rate)
3234 case CODEC_TYPE_VIDEO:
3235 if (av1->width == av->width &&
3236 av1->height == av->height &&
3237 av1->frame_rate == av->frame_rate &&
3238 av1->frame_rate_base == av->frame_rate_base &&
3239 av1->gop_size == av->gop_size)
3248 fst = add_av_stream1(feed, av);
3251 return feed->nb_streams - 1;
3256 static void remove_stream(FFStream *stream)
3260 while (*ps != NULL) {
3261 if (*ps == stream) {
3269 /* specific mpeg4 handling : we extract the raw parameters */
3270 static void extract_mpeg4_header(AVFormatContext *infile)
3272 int mpeg4_count, i, size;
3278 for(i=0;i<infile->nb_streams;i++) {
3279 st = infile->streams[i];
3280 if (st->codec.codec_id == CODEC_ID_MPEG4 &&
3281 st->codec.extradata == NULL) {
3288 printf("MPEG4 without extra data: trying to find header\n");
3289 while (mpeg4_count > 0) {
3290 if (av_read_packet(infile, &pkt) < 0)
3292 st = infile->streams[pkt.stream_index];
3293 if (st->codec.codec_id == CODEC_ID_MPEG4 &&
3294 st->codec.extradata == NULL) {
3295 /* fill extradata with the header */
3296 /* XXX: we make hard suppositions here ! */
3298 while (p < pkt.data + pkt.size - 4) {
3299 /* stop when vop header is found */
3300 if (p[0] == 0x00 && p[1] == 0x00 &&
3301 p[2] == 0x01 && p[3] == 0xb6) {
3302 size = p - pkt.data;
3303 // av_hex_dump(pkt.data, size);
3304 st->codec.extradata = av_malloc(size);
3305 st->codec.extradata_size = size;
3306 memcpy(st->codec.extradata, pkt.data, size);
3313 av_free_packet(&pkt);
3317 /* compute the needed AVStream for each file */
3318 static void build_file_streams(void)
3320 FFStream *stream, *stream_next;
3321 AVFormatContext *infile;
3324 /* gather all streams */
3325 for(stream = first_stream; stream != NULL; stream = stream_next) {
3326 stream_next = stream->next;
3327 if (stream->stream_type == STREAM_TYPE_LIVE &&
3329 /* the stream comes from a file */
3330 /* try to open the file */
3332 if (av_open_input_file(&infile, stream->feed_filename,
3333 NULL, 0, NULL) < 0) {
3334 http_log("%s not found", stream->feed_filename);
3335 /* remove stream (no need to spend more time on it) */
3337 remove_stream(stream);
3339 /* find all the AVStreams inside and reference them in
3341 if (av_find_stream_info(infile) < 0) {
3342 http_log("Could not find codec parameters from '%s'",
3343 stream->feed_filename);
3344 av_close_input_file(infile);
3347 extract_mpeg4_header(infile);
3349 for(i=0;i<infile->nb_streams;i++) {
3350 add_av_stream1(stream, &infile->streams[i]->codec);
3352 av_close_input_file(infile);
3358 /* compute the needed AVStream for each feed */
3359 static void build_feed_streams(void)
3361 FFStream *stream, *feed;
3364 /* gather all streams */
3365 for(stream = first_stream; stream != NULL; stream = stream->next) {
3366 feed = stream->feed;
3368 if (!stream->is_feed) {
3369 /* we handle a stream coming from a feed */
3370 for(i=0;i<stream->nb_streams;i++) {
3371 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3377 /* gather all streams */
3378 for(stream = first_stream; stream != NULL; stream = stream->next) {
3379 feed = stream->feed;
3381 if (stream->is_feed) {
3382 for(i=0;i<stream->nb_streams;i++) {
3383 stream->feed_streams[i] = i;
3389 /* create feed files if needed */
3390 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3393 if (url_exist(feed->feed_filename)) {
3394 /* See if it matches */
3398 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3399 /* Now see if it matches */
3400 if (s->nb_streams == feed->nb_streams) {
3402 for(i=0;i<s->nb_streams;i++) {
3404 sf = feed->streams[i];
3407 if (sf->index != ss->index ||
3409 printf("Index & Id do not match for stream %d\n", i);
3412 AVCodecContext *ccf, *ccs;
3416 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3418 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3419 printf("Codecs do not match for stream %d\n", i);
3421 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3422 printf("Codec bitrates do not match for stream %d\n", i);
3424 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3425 if (CHECK_CODEC(frame_rate) ||
3426 CHECK_CODEC(frame_rate_base) ||
3427 CHECK_CODEC(width) ||
3428 CHECK_CODEC(height)) {
3429 printf("Codec width, height and framerate do not match for stream %d\n", i);
3432 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3433 if (CHECK_CODEC(sample_rate) ||
3434 CHECK_CODEC(channels) ||
3435 CHECK_CODEC(frame_size)) {
3436 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3440 printf("Unknown codec type\n");
3449 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3450 feed->feed_filename, s->nb_streams, feed->nb_streams);
3453 av_close_input_file(s);
3455 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3456 feed->feed_filename);
3459 unlink(feed->feed_filename);
3461 if (!url_exist(feed->feed_filename)) {
3462 AVFormatContext s1, *s = &s1;
3464 /* only write the header of the ffm file */
3465 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3466 fprintf(stderr, "Could not open output feed file '%s'\n",
3467 feed->feed_filename);
3470 s->oformat = feed->fmt;
3471 s->nb_streams = feed->nb_streams;
3472 for(i=0;i<s->nb_streams;i++) {
3474 st = feed->streams[i];
3477 av_set_parameters(s, NULL);
3479 /* XXX: need better api */
3480 av_freep(&s->priv_data);
3483 /* get feed size and write index */
3484 fd = open(feed->feed_filename, O_RDONLY);
3486 fprintf(stderr, "Could not open output feed file '%s'\n",
3487 feed->feed_filename);
3491 feed->feed_write_index = ffm_read_write_index(fd);
3492 feed->feed_size = lseek(fd, 0, SEEK_END);
3493 /* ensure that we do not wrap before the end of file */
3494 if (feed->feed_max_size < feed->feed_size)
3495 feed->feed_max_size = feed->feed_size;
3501 /* compute the bandwidth used by each stream */
3502 static void compute_bandwidth(void)
3507 for(stream = first_stream; stream != NULL; stream = stream->next) {
3509 for(i=0;i<stream->nb_streams;i++) {
3510 AVStream *st = stream->streams[i];
3511 switch(st->codec.codec_type) {
3512 case CODEC_TYPE_AUDIO:
3513 case CODEC_TYPE_VIDEO:
3514 bandwidth += st->codec.bit_rate;
3520 stream->bandwidth = (bandwidth + 999) / 1000;
3524 static void get_arg(char *buf, int buf_size, const char **pp)
3531 while (isspace(*p)) p++;
3534 if (*p == '\"' || *p == '\'')
3546 if ((q - buf) < buf_size - 1)
3551 if (quote && *p == quote)
3556 /* add a codec and set the default parameters */
3557 static void add_codec(FFStream *stream, AVCodecContext *av)
3561 /* compute default parameters */
3562 switch(av->codec_type) {
3563 case CODEC_TYPE_AUDIO:
3564 if (av->bit_rate == 0)
3565 av->bit_rate = 64000;
3566 if (av->sample_rate == 0)
3567 av->sample_rate = 22050;
3568 if (av->channels == 0)
3571 case CODEC_TYPE_VIDEO:
3572 if (av->bit_rate == 0)
3573 av->bit_rate = 64000;
3574 if (av->frame_rate == 0){
3576 av->frame_rate_base = 1;
3578 if (av->width == 0 || av->height == 0) {
3582 /* Bitrate tolerance is less for streaming */
3583 if (av->bit_rate_tolerance == 0)
3584 av->bit_rate_tolerance = av->bit_rate / 4;
3589 if (av->max_qdiff == 0)
3591 av->qcompress = 0.5;
3595 av->rc_eq = "tex^qComp";
3596 if (!av->i_quant_factor)
3597 av->i_quant_factor = -0.8;
3598 if (!av->b_quant_factor)
3599 av->b_quant_factor = 1.25;
3600 if (!av->b_quant_offset)
3601 av->b_quant_offset = 1.25;
3602 if (!av->rc_min_rate)
3603 av->rc_min_rate = av->bit_rate / 2;
3604 if (!av->rc_max_rate)
3605 av->rc_max_rate = av->bit_rate * 2;
3612 st = av_mallocz(sizeof(AVStream));
3615 stream->streams[stream->nb_streams++] = st;
3616 memcpy(&st->codec, av, sizeof(AVCodecContext));
3619 static int opt_audio_codec(const char *arg)
3625 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3630 return CODEC_ID_NONE;
3636 static int opt_video_codec(const char *arg)
3642 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3647 return CODEC_ID_NONE;
3653 /* simplistic plugin support */
3655 #ifdef CONFIG_HAVE_DLOPEN
3656 void load_module(const char *filename)
3659 void (*init_func)(void);
3660 dll = dlopen(filename, RTLD_NOW);
3662 fprintf(stderr, "Could not load module '%s' - %s\n",
3663 filename, dlerror());
3667 init_func = dlsym(dll, "ffserver_module_init");
3670 "%s: init function 'ffserver_module_init()' not found\n",
3679 static int parse_ffconfig(const char *filename)
3686 int val, errors, line_num;
3687 FFStream **last_stream, *stream, *redirect;
3688 FFStream **last_feed, *feed;
3689 AVCodecContext audio_enc, video_enc;
3690 int audio_id, video_id;
3692 f = fopen(filename, "r");
3700 first_stream = NULL;
3701 last_stream = &first_stream;
3703 last_feed = &first_feed;
3707 audio_id = CODEC_ID_NONE;
3708 video_id = CODEC_ID_NONE;
3710 if (fgets(line, sizeof(line), f) == NULL)
3716 if (*p == '\0' || *p == '#')
3719 get_arg(cmd, sizeof(cmd), &p);
3721 if (!strcasecmp(cmd, "Port")) {
3722 get_arg(arg, sizeof(arg), &p);
3723 my_http_addr.sin_port = htons (atoi(arg));
3724 } else if (!strcasecmp(cmd, "BindAddress")) {
3725 get_arg(arg, sizeof(arg), &p);
3726 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3727 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3728 filename, line_num, arg);
3731 } else if (!strcasecmp(cmd, "NoDaemon")) {
3732 ffserver_daemon = 0;
3733 } else if (!strcasecmp(cmd, "RTSPPort")) {
3734 get_arg(arg, sizeof(arg), &p);
3735 my_rtsp_addr.sin_port = htons (atoi(arg));
3736 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3737 get_arg(arg, sizeof(arg), &p);
3738 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3739 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3740 filename, line_num, arg);
3743 } else if (!strcasecmp(cmd, "MaxClients")) {
3744 get_arg(arg, sizeof(arg), &p);
3746 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3747 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3748 filename, line_num, arg);
3751 nb_max_connections = val;
3753 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3754 get_arg(arg, sizeof(arg), &p);
3756 if (val < 10 || val > 100000) {
3757 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3758 filename, line_num, arg);
3761 max_bandwidth = val;
3763 } else if (!strcasecmp(cmd, "CustomLog")) {
3764 get_arg(logfilename, sizeof(logfilename), &p);
3765 } else if (!strcasecmp(cmd, "<Feed")) {
3766 /*********************************************/
3767 /* Feed related options */
3769 if (stream || feed) {
3770 fprintf(stderr, "%s:%d: Already in a tag\n",
3771 filename, line_num);
3773 feed = av_mallocz(sizeof(FFStream));
3774 /* add in stream list */
3775 *last_stream = feed;
3776 last_stream = &feed->next;
3777 /* add in feed list */
3779 last_feed = &feed->next_feed;
3781 get_arg(feed->filename, sizeof(feed->filename), &p);
3782 q = strrchr(feed->filename, '>');
3785 feed->fmt = guess_format("ffm", NULL, NULL);
3786 /* defaut feed file */
3787 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3788 "/tmp/%s.ffm", feed->filename);
3789 feed->feed_max_size = 5 * 1024 * 1024;
3791 feed->feed = feed; /* self feeding :-) */
3793 } else if (!strcasecmp(cmd, "Launch")) {
3797 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3799 feed->child_argv[0] = av_malloc(7);
3800 strcpy(feed->child_argv[0], "ffmpeg");
3802 for (i = 1; i < 62; i++) {
3805 get_arg(argbuf, sizeof(argbuf), &p);
3809 feed->child_argv[i] = av_malloc(strlen(argbuf + 1));
3810 strcpy(feed->child_argv[i], argbuf);
3813 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3815 snprintf(feed->child_argv[i], 256, "http://127.0.0.1:%d/%s",
3816 ntohs(my_http_addr.sin_port), feed->filename);
3818 } else if (!strcasecmp(cmd, "File")) {
3820 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3821 } else if (stream) {
3822 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3824 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3829 get_arg(arg, sizeof(arg), &p);
3831 fsize = strtod(p1, (char **)&p1);
3832 switch(toupper(*p1)) {
3837 fsize *= 1024 * 1024;
3840 fsize *= 1024 * 1024 * 1024;
3843 feed->feed_max_size = (int64_t)fsize;
3845 } else if (!strcasecmp(cmd, "</Feed>")) {
3847 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3848 filename, line_num);
3852 /* Make sure that we start out clean */
3853 if (unlink(feed->feed_filename) < 0
3854 && errno != ENOENT) {
3855 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3856 filename, line_num, feed->feed_filename, strerror(errno));
3862 } else if (!strcasecmp(cmd, "<Stream")) {
3863 /*********************************************/
3864 /* Stream related options */
3866 if (stream || feed) {
3867 fprintf(stderr, "%s:%d: Already in a tag\n",
3868 filename, line_num);
3870 stream = av_mallocz(sizeof(FFStream));
3871 *last_stream = stream;
3872 last_stream = &stream->next;
3874 get_arg(stream->filename, sizeof(stream->filename), &p);
3875 q = strrchr(stream->filename, '>');
3878 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3879 memset(&audio_enc, 0, sizeof(AVCodecContext));
3880 memset(&video_enc, 0, sizeof(AVCodecContext));
3881 audio_id = CODEC_ID_NONE;
3882 video_id = CODEC_ID_NONE;
3884 audio_id = stream->fmt->audio_codec;
3885 video_id = stream->fmt->video_codec;
3888 } else if (!strcasecmp(cmd, "Feed")) {
3889 get_arg(arg, sizeof(arg), &p);
3894 while (sfeed != NULL) {
3895 if (!strcmp(sfeed->filename, arg))
3897 sfeed = sfeed->next_feed;
3900 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3901 filename, line_num, arg);
3903 stream->feed = sfeed;
3906 } else if (!strcasecmp(cmd, "Format")) {
3907 get_arg(arg, sizeof(arg), &p);
3908 if (!strcmp(arg, "status")) {
3909 stream->stream_type = STREAM_TYPE_STATUS;
3912 stream->stream_type = STREAM_TYPE_LIVE;
3913 /* jpeg cannot be used here, so use single frame jpeg */
3914 if (!strcmp(arg, "jpeg"))
3915 strcpy(arg, "singlejpeg");
3916 stream->fmt = guess_stream_format(arg, NULL, NULL);
3918 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3919 filename, line_num, arg);
3924 audio_id = stream->fmt->audio_codec;
3925 video_id = stream->fmt->video_codec;
3927 } else if (!strcasecmp(cmd, "FaviconURL")) {
3928 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3929 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3931 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3932 filename, line_num);
3935 } else if (!strcasecmp(cmd, "Author")) {
3937 get_arg(stream->author, sizeof(stream->author), &p);
3939 } else if (!strcasecmp(cmd, "Comment")) {
3941 get_arg(stream->comment, sizeof(stream->comment), &p);
3943 } else if (!strcasecmp(cmd, "Copyright")) {
3945 get_arg(stream->copyright, sizeof(stream->copyright), &p);
3947 } else if (!strcasecmp(cmd, "Title")) {
3949 get_arg(stream->title, sizeof(stream->title), &p);
3951 } else if (!strcasecmp(cmd, "Preroll")) {
3952 get_arg(arg, sizeof(arg), &p);
3954 stream->prebuffer = atof(arg) * 1000;
3956 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
3958 stream->send_on_key = 1;
3960 } else if (!strcasecmp(cmd, "AudioCodec")) {
3961 get_arg(arg, sizeof(arg), &p);
3962 audio_id = opt_audio_codec(arg);
3963 if (audio_id == CODEC_ID_NONE) {
3964 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
3965 filename, line_num, arg);
3968 } else if (!strcasecmp(cmd, "VideoCodec")) {
3969 get_arg(arg, sizeof(arg), &p);
3970 video_id = opt_video_codec(arg);
3971 if (video_id == CODEC_ID_NONE) {
3972 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
3973 filename, line_num, arg);
3976 } else if (!strcasecmp(cmd, "MaxTime")) {
3977 get_arg(arg, sizeof(arg), &p);
3979 stream->max_time = atof(arg) * 1000;
3981 } else if (!strcasecmp(cmd, "AudioBitRate")) {
3982 get_arg(arg, sizeof(arg), &p);
3984 audio_enc.bit_rate = atoi(arg) * 1000;
3986 } else if (!strcasecmp(cmd, "AudioChannels")) {
3987 get_arg(arg, sizeof(arg), &p);
3989 audio_enc.channels = atoi(arg);
3991 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
3992 get_arg(arg, sizeof(arg), &p);
3994 audio_enc.sample_rate = atoi(arg);
3996 } else if (!strcasecmp(cmd, "AudioQuality")) {
3997 get_arg(arg, sizeof(arg), &p);
3999 // audio_enc.quality = atof(arg) * 1000;
4001 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4003 int minrate, maxrate;
4005 get_arg(arg, sizeof(arg), &p);
4007 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4008 video_enc.rc_min_rate = minrate * 1000;
4009 video_enc.rc_max_rate = maxrate * 1000;
4011 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4012 filename, line_num, arg);
4016 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4018 get_arg(arg, sizeof(arg), &p);
4019 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4021 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4022 get_arg(arg, sizeof(arg), &p);
4024 video_enc.bit_rate = atoi(arg) * 1000;
4026 } else if (!strcasecmp(cmd, "VideoSize")) {
4027 get_arg(arg, sizeof(arg), &p);
4029 parse_image_size(&video_enc.width, &video_enc.height, arg);
4030 if ((video_enc.width % 16) != 0 ||
4031 (video_enc.height % 16) != 0) {
4032 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4033 filename, line_num);
4037 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4038 get_arg(arg, sizeof(arg), &p);
4040 video_enc.frame_rate_base= DEFAULT_FRAME_RATE_BASE;
4041 video_enc.frame_rate = (int)(strtod(arg, NULL) * video_enc.frame_rate_base);
4043 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4044 get_arg(arg, sizeof(arg), &p);
4046 video_enc.gop_size = atoi(arg);
4048 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4050 video_enc.gop_size = 1;
4052 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4054 video_enc.flags |= CODEC_FLAG_HQ;
4056 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4057 get_arg(arg, sizeof(arg), &p);
4059 video_enc.max_qdiff = atoi(arg);
4060 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4061 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4062 filename, line_num);
4066 } else if (!strcasecmp(cmd, "VideoQMax")) {
4067 get_arg(arg, sizeof(arg), &p);
4069 video_enc.qmax = atoi(arg);
4070 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4071 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4072 filename, line_num);
4076 } else if (!strcasecmp(cmd, "VideoQMin")) {
4077 get_arg(arg, sizeof(arg), &p);
4079 video_enc.qmin = atoi(arg);
4080 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4081 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4082 filename, line_num);
4086 } else if (!strcasecmp(cmd, "LumaElim")) {
4087 get_arg(arg, sizeof(arg), &p);
4089 video_enc.luma_elim_threshold = atoi(arg);
4091 } else if (!strcasecmp(cmd, "ChromaElim")) {
4092 get_arg(arg, sizeof(arg), &p);
4094 video_enc.chroma_elim_threshold = atoi(arg);
4096 } else if (!strcasecmp(cmd, "LumiMask")) {
4097 get_arg(arg, sizeof(arg), &p);
4099 video_enc.lumi_masking = atof(arg);
4101 } else if (!strcasecmp(cmd, "DarkMask")) {
4102 get_arg(arg, sizeof(arg), &p);
4104 video_enc.dark_masking = atof(arg);
4106 } else if (!strcasecmp(cmd, "NoVideo")) {
4107 video_id = CODEC_ID_NONE;
4108 } else if (!strcasecmp(cmd, "NoAudio")) {
4109 audio_id = CODEC_ID_NONE;
4110 } else if (!strcasecmp(cmd, "ACL")) {
4114 get_arg(arg, sizeof(arg), &p);
4115 if (strcasecmp(arg, "allow") == 0) {
4116 acl.action = IP_ALLOW;
4117 } else if (strcasecmp(arg, "deny") == 0) {
4118 acl.action = IP_DENY;
4120 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4121 filename, line_num, arg);
4125 get_arg(arg, sizeof(arg), &p);
4127 he = gethostbyname(arg);
4129 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4130 filename, line_num, arg);
4133 /* Only take the first */
4134 acl.first.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4135 acl.last = acl.first;
4138 get_arg(arg, sizeof(arg), &p);
4141 he = gethostbyname(arg);
4143 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4144 filename, line_num, arg);
4147 /* Only take the first */
4148 acl.last.s_addr = ntohl(((struct in_addr *) he->h_addr_list[0])->s_addr);
4153 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4154 IPAddressACL **naclp = 0;
4160 naclp = &stream->acl;
4164 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4165 filename, line_num);
4171 naclp = &(*naclp)->next;
4176 } else if (!strcasecmp(cmd, "RTSPOption")) {
4177 get_arg(arg, sizeof(arg), &p);
4179 av_freep(&stream->rtsp_option);
4180 /* XXX: av_strdup ? */
4181 stream->rtsp_option = av_malloc(strlen(arg) + 1);
4182 if (stream->rtsp_option) {
4183 strcpy(stream->rtsp_option, arg);
4186 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4187 get_arg(arg, sizeof(arg), &p);
4189 if (!inet_aton(arg, &stream->multicast_ip)) {
4190 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
4191 filename, line_num, arg);
4194 stream->is_multicast = 1;
4195 stream->loop = 1; /* default is looping */
4197 } else if (!strcasecmp(cmd, "MulticastPort")) {
4198 get_arg(arg, sizeof(arg), &p);
4200 stream->multicast_port = atoi(arg);
4202 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4203 get_arg(arg, sizeof(arg), &p);
4205 stream->multicast_ttl = atoi(arg);
4207 } else if (!strcasecmp(cmd, "NoLoop")) {
4211 } else if (!strcasecmp(cmd, "</Stream>")) {
4213 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4214 filename, line_num);
4217 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4218 if (audio_id != CODEC_ID_NONE) {
4219 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4220 audio_enc.codec_id = audio_id;
4221 add_codec(stream, &audio_enc);
4223 if (video_id != CODEC_ID_NONE) {
4224 video_enc.codec_type = CODEC_TYPE_VIDEO;
4225 video_enc.codec_id = video_id;
4226 add_codec(stream, &video_enc);
4230 } else if (!strcasecmp(cmd, "<Redirect")) {
4231 /*********************************************/
4233 if (stream || feed || redirect) {
4234 fprintf(stderr, "%s:%d: Already in a tag\n",
4235 filename, line_num);
4238 redirect = av_mallocz(sizeof(FFStream));
4239 *last_stream = redirect;
4240 last_stream = &redirect->next;
4242 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4243 q = strrchr(redirect->filename, '>');
4246 redirect->stream_type = STREAM_TYPE_REDIRECT;
4248 } else if (!strcasecmp(cmd, "URL")) {
4250 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4252 } else if (!strcasecmp(cmd, "</Redirect>")) {
4254 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4255 filename, line_num);
4258 if (!redirect->feed_filename[0]) {
4259 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4260 filename, line_num);
4264 } else if (!strcasecmp(cmd, "LoadModule")) {
4265 get_arg(arg, sizeof(arg), &p);
4266 #ifdef CONFIG_HAVE_DLOPEN
4269 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4270 filename, line_num, arg);
4274 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4275 filename, line_num, cmd);
4289 static void write_packet(FFCodec *ffenc,
4290 uint8_t *buf, int size)
4293 AVCodecContext *enc = &ffenc->enc;
4295 mk_header(&hdr, enc, size);
4296 wptr = http_fifo.wptr;
4297 fifo_write(&http_fifo, (uint8_t *)&hdr, sizeof(hdr), &wptr);
4298 fifo_write(&http_fifo, buf, size, &wptr);
4299 /* atomic modification of wptr */
4300 http_fifo.wptr = wptr;
4301 ffenc->data_count += size;
4302 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4306 static void help(void)
4308 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4309 "usage: ffserver [-L] [-h] [-f configfile]\n"
4310 "Hyper fast multi format Audio/Video streaming server\n"
4312 "-L : print the LICENCE\n"
4314 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4318 static void licence(void)
4321 "ffserver version " FFMPEG_VERSION "\n"
4322 "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4323 "This library is free software; you can redistribute it and/or\n"
4324 "modify it under the terms of the GNU Lesser General Public\n"
4325 "License as published by the Free Software Foundation; either\n"
4326 "version 2 of the License, or (at your option) any later version.\n"
4328 "This library is distributed in the hope that it will be useful,\n"
4329 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4330 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4331 "Lesser General Public License for more details.\n"
4333 "You should have received a copy of the GNU Lesser General Public\n"
4334 "License along with this library; if not, write to the Free Software\n"
4335 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
4339 static void handle_child_exit(int sig)
4344 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4347 for (feed = first_feed; feed; feed = feed->next) {
4348 if (feed->pid == pid) {
4349 int uptime = time(0) - feed->pid_start;
4352 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4355 /* Turn off any more restarts */
4356 feed->child_argv = 0;
4362 need_to_start_children = 1;
4365 int main(int argc, char **argv)
4367 const char *config_filename;
4369 struct sigaction sigact;
4373 config_filename = "/etc/ffserver.conf";
4375 my_program_name = argv[0];
4376 my_program_dir = getcwd(0, 0);
4377 ffserver_daemon = 1;
4380 c = getopt(argc, argv, "ndLh?f:");
4396 ffserver_daemon = 0;
4399 config_filename = optarg;
4406 putenv("http_proxy"); /* Kill the http_proxy */
4408 srandom(gettime_ms() + (getpid() << 16));
4410 /* address on which the server will handle HTTP connections */
4411 my_http_addr.sin_family = AF_INET;
4412 my_http_addr.sin_port = htons (8080);
4413 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4415 /* address on which the server will handle RTSP connections */
4416 my_rtsp_addr.sin_family = AF_INET;
4417 my_rtsp_addr.sin_port = htons (5454);
4418 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4420 nb_max_connections = 5;
4421 max_bandwidth = 1000;
4422 first_stream = NULL;
4423 logfilename[0] = '\0';
4425 memset(&sigact, 0, sizeof(sigact));
4426 sigact.sa_handler = handle_child_exit;
4427 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4428 sigaction(SIGCHLD, &sigact, 0);
4430 if (parse_ffconfig(config_filename) < 0) {
4431 fprintf(stderr, "Incorrect config file - exiting.\n");
4435 build_file_streams();
4437 build_feed_streams();
4439 compute_bandwidth();
4441 /* put the process in background and detach it from its TTY */
4442 if (ffserver_daemon) {
4449 } else if (pid > 0) {
4457 open("/dev/null", O_RDWR);
4458 if (strcmp(logfilename, "-") != 0) {
4468 signal(SIGPIPE, SIG_IGN);
4470 /* open log file if needed */
4471 if (logfilename[0] != '\0') {
4472 if (!strcmp(logfilename, "-"))
4475 logfile = fopen(logfilename, "w");
4478 if (http_server() < 0) {
4479 fprintf(stderr, "Could not start server\n");