2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #define HAVE_AV_CONFIG_H
25 #include <sys/ioctl.h>
30 #include <sys/types.h>
31 #include <sys/socket.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
38 #ifdef CONFIG_HAVE_DLFCN
44 /* maximum number of simultaneous HTTP connections */
45 #define HTTP_MAX_CONNECTIONS 2000
48 HTTPSTATE_WAIT_REQUEST,
49 HTTPSTATE_SEND_HEADER,
50 HTTPSTATE_SEND_DATA_HEADER,
51 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
52 HTTPSTATE_SEND_DATA_TRAILER,
53 HTTPSTATE_RECEIVE_DATA,
54 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
55 HTTPSTATE_WAIT, /* wait before sending next packets */
56 HTTPSTATE_WAIT_SHORT, /* short wait for short term
57 bandwidth limitation */
60 RTSPSTATE_WAIT_REQUEST,
64 const char *http_state[] = {
81 #define IOBUFFER_INIT_SIZE 8192
83 /* coef for exponential mean for bitrate estimation in statistics */
86 /* timeouts are in ms */
87 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
88 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
90 #define SYNC_TIMEOUT (10 * 1000)
93 int64_t count1, count2;
97 /* context associated with one connection */
98 typedef struct HTTPContext {
100 int fd; /* socket file descriptor */
101 struct sockaddr_in from_addr; /* origin */
102 struct pollfd *poll_entry; /* used when polling */
104 uint8_t *buffer_ptr, *buffer_end;
106 struct HTTPContext *next;
107 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
111 /* input format handling */
112 AVFormatContext *fmt_in;
113 long start_time; /* In milliseconds - this wraps fairly often */
114 int64_t first_pts; /* initial pts value */
115 int pts_stream_index; /* stream we choose as clock reference */
116 /* output format handling */
117 struct FFStream *stream;
118 /* -1 is invalid stream */
119 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
120 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
122 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
123 int last_packet_sent; /* true if last data packet was sent */
125 DataRateData datarate;
132 int is_packetized; /* if true, the stream is packetized */
133 int packet_stream_index; /* current stream for output in state machine */
135 /* RTSP state specific */
136 uint8_t *pb_buffer; /* XXX: use that in all the code */
138 int seq; /* RTSP sequence number */
140 /* RTP state specific */
141 enum RTSPProtocol rtp_protocol;
142 char session_id[32]; /* session id */
143 AVFormatContext *rtp_ctx[MAX_STREAMS];
144 URLContext *rtp_handles[MAX_STREAMS];
145 /* RTP short term bandwidth limitation */
146 int packet_byte_count;
147 int packet_start_time_us; /* used for short durations (a few
151 static AVFrame dummy_frame;
153 /* each generated stream is described here */
157 STREAM_TYPE_REDIRECT,
160 enum IPAddressAction {
165 typedef struct IPAddressACL {
166 struct IPAddressACL *next;
167 enum IPAddressAction action;
168 struct in_addr first;
172 /* description of each stream of the ffserver.conf file */
173 typedef struct FFStream {
174 enum StreamType stream_type;
175 char filename[1024]; /* stream filename */
176 struct FFStream *feed; /* feed we are using (can be null if
181 int prebuffer; /* Number of millseconds early to start */
182 long max_time; /* Number of milliseconds to run */
184 AVStream *streams[MAX_STREAMS];
185 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
186 char feed_filename[1024]; /* file name of the feed storage, or
187 input file name for a stream */
192 pid_t pid; /* Of ffmpeg process */
193 time_t pid_start; /* Of ffmpeg process */
195 struct FFStream *next;
196 int bandwidth; /* bandwidth, in kbits/s */
199 /* multicast specific */
201 struct in_addr multicast_ip;
202 int multicast_port; /* first port used for multicast */
204 int loop; /* if true, send the stream in loops (only meaningful if file) */
207 int feed_opened; /* true if someone is writing to the feed */
208 int is_feed; /* true if it is a feed */
210 int64_t bytes_served;
211 int64_t feed_max_size; /* maximum storage size */
212 int64_t feed_write_index; /* current write position in feed (it wraps round) */
213 int64_t feed_size; /* current size of feed */
214 struct FFStream *next_feed;
217 typedef struct FeedData {
218 long long data_count;
219 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
222 struct sockaddr_in my_http_addr;
223 struct sockaddr_in my_rtsp_addr;
225 char logfilename[1024];
226 HTTPContext *first_http_ctx;
227 FFStream *first_feed; /* contains only feeds */
228 FFStream *first_stream; /* contains all streams, including feeds */
230 static void new_connection(int server_fd, int is_rtsp);
231 static void close_connection(HTTPContext *c);
234 static int handle_connection(HTTPContext *c);
235 static int http_parse_request(HTTPContext *c);
236 static int http_send_data(HTTPContext *c);
237 static void compute_stats(HTTPContext *c);
238 static int open_input_stream(HTTPContext *c, const char *info);
239 static int http_start_receive_data(HTTPContext *c);
240 static int http_receive_data(HTTPContext *c);
241 static int compute_send_delay(HTTPContext *c);
244 static int rtsp_parse_request(HTTPContext *c);
245 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
246 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
247 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
248 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
249 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
252 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
253 struct in_addr my_ip);
256 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
257 FFStream *stream, const char *session_id);
258 static int rtp_new_av_stream(HTTPContext *c,
259 int stream_index, struct sockaddr_in *dest_addr);
261 static const char *my_program_name;
262 static const char *my_program_dir;
264 static int ffserver_debug;
265 static int ffserver_daemon;
266 static int no_launch;
267 static int need_to_start_children;
269 int nb_max_connections;
273 int current_bandwidth;
275 static long cur_time; // Making this global saves on passing it around everywhere
277 static long gettime_ms(void)
281 gettimeofday(&tv,NULL);
282 return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
285 static FILE *logfile = NULL;
287 static void http_log(const char *fmt, ...)
293 vfprintf(logfile, fmt, ap);
299 static char *ctime1(char *buf2)
307 p = buf2 + strlen(p) - 1;
313 static void log_connection(HTTPContext *c)
320 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
321 inet_ntoa(c->from_addr.sin_addr),
322 ctime1(buf2), c->method, c->url,
323 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
326 static void update_datarate(DataRateData *drd, int64_t count)
328 if (!drd->time1 && !drd->count1) {
329 drd->time1 = drd->time2 = cur_time;
330 drd->count1 = drd->count2 = count;
332 if (cur_time - drd->time2 > 5000) {
333 drd->time1 = drd->time2;
334 drd->count1 = drd->count2;
335 drd->time2 = cur_time;
341 /* In bytes per second */
342 static int compute_datarate(DataRateData *drd, int64_t count)
344 if (cur_time == drd->time1)
347 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
350 static int get_longterm_datarate(DataRateData *drd, int64_t count)
352 /* You get the first 3 seconds flat out */
353 if (cur_time - drd->time1 < 3000)
355 return compute_datarate(drd, count);
359 static void start_children(FFStream *feed)
364 for (; feed; feed = feed->next) {
365 if (feed->child_argv && !feed->pid) {
366 feed->pid_start = time(0);
371 fprintf(stderr, "Unable to create children\n");
380 for (i = 3; i < 256; i++) {
384 if (!ffserver_debug) {
385 i = open("/dev/null", O_RDWR);
394 pstrcpy(pathname, sizeof(pathname), my_program_name);
396 slash = strrchr(pathname, '/');
402 strcpy(slash, "ffmpeg");
404 /* This is needed to make relative pathnames work */
405 chdir(my_program_dir);
407 signal(SIGPIPE, SIG_DFL);
409 execvp(pathname, feed->child_argv);
417 /* open a listening socket */
418 static int socket_open_listen(struct sockaddr_in *my_addr)
422 server_fd = socket(AF_INET,SOCK_STREAM,0);
429 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
431 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
433 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
439 if (listen (server_fd, 5) < 0) {
444 fcntl(server_fd, F_SETFL, O_NONBLOCK);
449 /* start all multicast streams */
450 static void start_multicast(void)
455 struct sockaddr_in dest_addr;
456 int default_port, stream_index;
459 for(stream = first_stream; stream != NULL; stream = stream->next) {
460 if (stream->is_multicast) {
461 /* open the RTP connection */
462 snprintf(session_id, sizeof(session_id),
463 "%08x%08x", (int)random(), (int)random());
465 /* choose a port if none given */
466 if (stream->multicast_port == 0) {
467 stream->multicast_port = default_port;
471 dest_addr.sin_family = AF_INET;
472 dest_addr.sin_addr = stream->multicast_ip;
473 dest_addr.sin_port = htons(stream->multicast_port);
475 rtp_c = rtp_new_connection(&dest_addr, stream, session_id);
479 if (open_input_stream(rtp_c, "") < 0) {
480 fprintf(stderr, "Could not open input stream for stream '%s'\n",
485 rtp_c->rtp_protocol = RTSP_PROTOCOL_RTP_UDP_MULTICAST;
487 /* open each RTP stream */
488 for(stream_index = 0; stream_index < stream->nb_streams;
490 dest_addr.sin_port = htons(stream->multicast_port +
492 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
493 fprintf(stderr, "Could not open output stream '%s/streamid=%d'\n",
494 stream->filename, stream_index);
499 /* change state to send data */
500 rtp_c->state = HTTPSTATE_SEND_DATA;
505 /* main loop of the http server */
506 static int http_server(void)
508 int server_fd, ret, rtsp_server_fd, delay, delay1;
509 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
510 HTTPContext *c, *c_next;
512 server_fd = socket_open_listen(&my_http_addr);
516 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
517 if (rtsp_server_fd < 0)
520 http_log("ffserver started.\n");
522 start_children(first_feed);
524 first_http_ctx = NULL;
526 first_http_ctx = NULL;
531 poll_entry = poll_table;
532 poll_entry->fd = server_fd;
533 poll_entry->events = POLLIN;
536 poll_entry->fd = rtsp_server_fd;
537 poll_entry->events = POLLIN;
540 /* wait for events on each HTTP handle */
547 case HTTPSTATE_SEND_HEADER:
548 case RTSPSTATE_SEND_REPLY:
549 c->poll_entry = poll_entry;
551 poll_entry->events = POLLOUT;
554 case HTTPSTATE_SEND_DATA_HEADER:
555 case HTTPSTATE_SEND_DATA:
556 case HTTPSTATE_SEND_DATA_TRAILER:
557 if (!c->is_packetized) {
558 /* for TCP, we output as much as we can (may need to put a limit) */
559 c->poll_entry = poll_entry;
561 poll_entry->events = POLLOUT;
564 /* not strictly correct, but currently cannot add
565 more than one fd in poll entry */
569 case HTTPSTATE_WAIT_REQUEST:
570 case HTTPSTATE_RECEIVE_DATA:
571 case HTTPSTATE_WAIT_FEED:
572 case RTSPSTATE_WAIT_REQUEST:
573 /* need to catch errors */
574 c->poll_entry = poll_entry;
576 poll_entry->events = POLLIN;/* Maybe this will work */
580 c->poll_entry = NULL;
581 delay1 = compute_send_delay(c);
585 case HTTPSTATE_WAIT_SHORT:
586 c->poll_entry = NULL;
587 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
592 c->poll_entry = NULL;
598 /* wait for an event on one connection. We poll at least every
599 second to handle timeouts */
601 ret = poll(poll_table, poll_entry - poll_table, delay);
604 cur_time = gettime_ms();
606 if (need_to_start_children) {
607 need_to_start_children = 0;
608 start_children(first_feed);
611 /* now handle the events */
612 for(c = first_http_ctx; c != NULL; c = c_next) {
614 if (handle_connection(c) < 0) {
615 /* close and free the connection */
621 poll_entry = poll_table;
622 /* new HTTP connection request ? */
623 if (poll_entry->revents & POLLIN) {
624 new_connection(server_fd, 0);
627 /* new RTSP connection request ? */
628 if (poll_entry->revents & POLLIN) {
629 new_connection(rtsp_server_fd, 1);
634 /* start waiting for a new HTTP/RTSP request */
635 static void start_wait_request(HTTPContext *c, int is_rtsp)
637 c->buffer_ptr = c->buffer;
638 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
641 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
642 c->state = RTSPSTATE_WAIT_REQUEST;
644 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
645 c->state = HTTPSTATE_WAIT_REQUEST;
649 static void new_connection(int server_fd, int is_rtsp)
651 struct sockaddr_in from_addr;
653 HTTPContext *c = NULL;
655 len = sizeof(from_addr);
656 fd = accept(server_fd, (struct sockaddr *)&from_addr,
660 fcntl(fd, F_SETFL, O_NONBLOCK);
662 /* XXX: should output a warning page when coming
663 close to the connection limit */
664 if (nb_connections >= nb_max_connections)
667 /* add a new connection */
668 c = av_mallocz(sizeof(HTTPContext));
672 c->next = first_http_ctx;
675 c->poll_entry = NULL;
676 c->from_addr = from_addr;
677 c->buffer_size = IOBUFFER_INIT_SIZE;
678 c->buffer = av_malloc(c->buffer_size);
683 start_wait_request(c, is_rtsp);
695 static void close_connection(HTTPContext *c)
697 HTTPContext **cp, *c1;
699 AVFormatContext *ctx;
703 /* remove connection from list */
704 cp = &first_http_ctx;
705 while ((*cp) != NULL) {
714 /* remove connection associated resources */
718 /* close each frame parser */
719 for(i=0;i<c->fmt_in->nb_streams;i++) {
720 st = c->fmt_in->streams[i];
721 if (st->codec.codec) {
722 avcodec_close(&st->codec);
725 av_close_input_file(c->fmt_in);
728 /* free RTP output streams if any */
731 nb_streams = c->stream->nb_streams;
733 for(i=0;i<nb_streams;i++) {
736 av_write_trailer(ctx);
739 h = c->rtp_handles[i];
746 current_bandwidth -= c->stream->bandwidth;
747 av_freep(&c->pb_buffer);
753 static int handle_connection(HTTPContext *c)
758 case HTTPSTATE_WAIT_REQUEST:
759 case RTSPSTATE_WAIT_REQUEST:
761 if ((c->timeout - cur_time) < 0)
763 if (c->poll_entry->revents & (POLLERR | POLLHUP))
766 /* no need to read if no events */
767 if (!(c->poll_entry->revents & POLLIN))
770 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
772 if (errno != EAGAIN && errno != EINTR)
774 } else if (len == 0) {
777 /* search for end of request. XXX: not fully correct since garbage could come after the end */
779 c->buffer_ptr += len;
781 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
782 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
783 /* request found : parse it and reply */
784 if (c->state == HTTPSTATE_WAIT_REQUEST) {
785 ret = http_parse_request(c);
787 ret = rtsp_parse_request(c);
791 } else if (ptr >= c->buffer_end) {
792 /* request too long: cannot do anything */
798 case HTTPSTATE_SEND_HEADER:
799 if (c->poll_entry->revents & (POLLERR | POLLHUP))
802 /* no need to write if no events */
803 if (!(c->poll_entry->revents & POLLOUT))
805 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
807 if (errno != EAGAIN && errno != EINTR) {
808 /* error : close connection */
809 av_freep(&c->pb_buffer);
813 c->buffer_ptr += len;
815 c->stream->bytes_served += len;
816 c->data_count += len;
817 if (c->buffer_ptr >= c->buffer_end) {
818 av_freep(&c->pb_buffer);
823 /* all the buffer was sent : synchronize to the incoming stream */
824 c->state = HTTPSTATE_SEND_DATA_HEADER;
825 c->buffer_ptr = c->buffer_end = c->buffer;
830 case HTTPSTATE_SEND_DATA:
831 case HTTPSTATE_SEND_DATA_HEADER:
832 case HTTPSTATE_SEND_DATA_TRAILER:
833 /* for packetized output, we consider we can always write (the
834 input streams sets the speed). It may be better to verify
835 that we do not rely too much on the kernel queues */
836 if (!c->is_packetized) {
837 if (c->poll_entry->revents & (POLLERR | POLLHUP))
840 /* no need to read if no events */
841 if (!(c->poll_entry->revents & POLLOUT))
844 if (http_send_data(c) < 0)
847 case HTTPSTATE_RECEIVE_DATA:
848 /* no need to read if no events */
849 if (c->poll_entry->revents & (POLLERR | POLLHUP))
851 if (!(c->poll_entry->revents & POLLIN))
853 if (http_receive_data(c) < 0)
856 case HTTPSTATE_WAIT_FEED:
857 /* no need to read if no events */
858 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
861 /* nothing to do, we'll be waken up by incoming feed packets */
865 /* if the delay expired, we can send new packets */
866 if (compute_send_delay(c) <= 0)
867 c->state = HTTPSTATE_SEND_DATA;
869 case HTTPSTATE_WAIT_SHORT:
870 /* just return back to send data */
871 c->state = HTTPSTATE_SEND_DATA;
874 case RTSPSTATE_SEND_REPLY:
875 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
876 av_freep(&c->pb_buffer);
879 /* no need to write if no events */
880 if (!(c->poll_entry->revents & POLLOUT))
882 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
884 if (errno != EAGAIN && errno != EINTR) {
885 /* error : close connection */
886 av_freep(&c->pb_buffer);
890 c->buffer_ptr += len;
891 c->data_count += len;
892 if (c->buffer_ptr >= c->buffer_end) {
893 /* all the buffer was sent : wait for a new request */
894 av_freep(&c->pb_buffer);
895 start_wait_request(c, 1);
899 case HTTPSTATE_READY:
908 static int extract_rates(char *rates, int ratelen, const char *request)
912 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
913 if (strncasecmp(p, "Pragma:", 7) == 0) {
914 const char *q = p + 7;
916 while (*q && *q != '\n' && isspace(*q))
919 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
925 memset(rates, 0xff, ratelen);
928 while (*q && *q != '\n' && *q != ':')
931 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
935 if (stream_no < ratelen && stream_no >= 0) {
936 rates[stream_no] = rate_no;
939 while (*q && *q != '\n' && !isspace(*q))
956 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
959 int best_bitrate = 100000000;
962 for (i = 0; i < feed->nb_streams; i++) {
963 AVCodecContext *feed_codec = &feed->streams[i]->codec;
965 if (feed_codec->codec_id != codec->codec_id ||
966 feed_codec->sample_rate != codec->sample_rate ||
967 feed_codec->width != codec->width ||
968 feed_codec->height != codec->height) {
972 /* Potential stream */
974 /* We want the fastest stream less than bit_rate, or the slowest
975 * faster than bit_rate
978 if (feed_codec->bit_rate <= bit_rate) {
979 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
980 best_bitrate = feed_codec->bit_rate;
984 if (feed_codec->bit_rate < best_bitrate) {
985 best_bitrate = feed_codec->bit_rate;
994 static int modify_current_stream(HTTPContext *c, char *rates)
997 FFStream *req = c->stream;
998 int action_required = 0;
1000 /* Not much we can do for a feed */
1004 for (i = 0; i < req->nb_streams; i++) {
1005 AVCodecContext *codec = &req->streams[i]->codec;
1009 c->switch_feed_streams[i] = req->feed_streams[i];
1012 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1015 /* Wants off or slow */
1016 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1018 /* This doesn't work well when it turns off the only stream! */
1019 c->switch_feed_streams[i] = -2;
1020 c->feed_streams[i] = -2;
1025 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1026 action_required = 1;
1029 return action_required;
1033 static void do_switch_stream(HTTPContext *c, int i)
1035 if (c->switch_feed_streams[i] >= 0) {
1037 c->feed_streams[i] = c->switch_feed_streams[i];
1040 /* Now update the stream */
1042 c->switch_feed_streams[i] = -1;
1045 /* XXX: factorize in utils.c ? */
1046 /* XXX: take care with different space meaning */
1047 static void skip_spaces(const char **pp)
1051 while (*p == ' ' || *p == '\t')
1056 static void get_word(char *buf, int buf_size, const char **pp)
1064 while (!isspace(*p) && *p != '\0') {
1065 if ((q - buf) < buf_size - 1)
1074 static int validate_acl(FFStream *stream, HTTPContext *c)
1076 enum IPAddressAction last_action = IP_DENY;
1078 struct in_addr *src = &c->from_addr.sin_addr;
1080 for (acl = stream->acl; acl; acl = acl->next) {
1081 if (src->s_addr >= acl->first.s_addr && src->s_addr <= acl->last.s_addr) {
1082 return (acl->action == IP_ALLOW) ? 1 : 0;
1084 last_action = acl->action;
1087 /* Nothing matched, so return not the last action */
1088 return (last_action == IP_DENY) ? 1 : 0;
1091 /* compute the real filename of a file by matching it without its
1092 extensions to all the stream filenames */
1093 static void compute_real_filename(char *filename, int max_size)
1100 /* compute filename by matching without the file extensions */
1101 pstrcpy(file1, sizeof(file1), filename);
1102 p = strrchr(file1, '.');
1105 for(stream = first_stream; stream != NULL; stream = stream->next) {
1106 pstrcpy(file2, sizeof(file2), stream->filename);
1107 p = strrchr(file2, '.');
1110 if (!strcmp(file1, file2)) {
1111 pstrcpy(filename, max_size, stream->filename);
1126 /* parse http request and prepare header */
1127 static int http_parse_request(HTTPContext *c)
1131 enum RedirType redir_type;
1133 char info[1024], *filename;
1137 const char *mime_type;
1141 char *useragent = 0;
1144 get_word(cmd, sizeof(cmd), (const char **)&p);
1145 pstrcpy(c->method, sizeof(c->method), cmd);
1147 if (!strcmp(cmd, "GET"))
1149 else if (!strcmp(cmd, "POST"))
1154 get_word(url, sizeof(url), (const char **)&p);
1155 pstrcpy(c->url, sizeof(c->url), url);
1157 get_word(protocol, sizeof(protocol), (const char **)&p);
1158 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1161 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
1163 /* find the filename and the optional info string in the request */
1170 pstrcpy(info, sizeof(info), p);
1176 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1177 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1179 if (*useragent && *useragent != '\n' && isspace(*useragent))
1183 p = strchr(p, '\n');
1190 redir_type = REDIR_NONE;
1191 if (match_ext(filename, "asx")) {
1192 redir_type = REDIR_ASX;
1193 filename[strlen(filename)-1] = 'f';
1194 } else if (match_ext(filename, "asf") &&
1195 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1196 /* if this isn't WMP or lookalike, return the redirector file */
1197 redir_type = REDIR_ASF;
1198 } else if (match_ext(filename, "rpm,ram")) {
1199 redir_type = REDIR_RAM;
1200 strcpy(filename + strlen(filename)-2, "m");
1201 } else if (match_ext(filename, "rtsp")) {
1202 redir_type = REDIR_RTSP;
1203 compute_real_filename(filename, sizeof(url) - 1);
1204 } else if (match_ext(filename, "sdp")) {
1205 redir_type = REDIR_SDP;
1206 compute_real_filename(filename, sizeof(url) - 1);
1209 stream = first_stream;
1210 while (stream != NULL) {
1211 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1213 stream = stream->next;
1215 if (stream == NULL) {
1216 sprintf(msg, "File '%s' not found", url);
1221 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1222 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1224 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1225 c->http_error = 301;
1227 q += sprintf(q, "HTTP/1.0 301 Moved\r\n");
1228 q += sprintf(q, "Location: %s\r\n", stream->feed_filename);
1229 q += sprintf(q, "Content-type: text/html\r\n");
1230 q += sprintf(q, "\r\n");
1231 q += sprintf(q, "<html><head><title>Moved</title></head><body>\r\n");
1232 q += sprintf(q, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1233 q += sprintf(q, "</body></html>\r\n");
1235 /* prepare output buffer */
1236 c->buffer_ptr = c->buffer;
1238 c->state = HTTPSTATE_SEND_HEADER;
1242 /* If this is WMP, get the rate information */
1243 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1244 if (modify_current_stream(c, ratebuf)) {
1245 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1246 if (c->switch_feed_streams[i] >= 0)
1247 do_switch_stream(c, i);
1252 if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1253 current_bandwidth += stream->bandwidth;
1256 if (post == 0 && max_bandwidth < current_bandwidth) {
1257 c->http_error = 200;
1259 q += sprintf(q, "HTTP/1.0 200 Server too busy\r\n");
1260 q += sprintf(q, "Content-type: text/html\r\n");
1261 q += sprintf(q, "\r\n");
1262 q += sprintf(q, "<html><head><title>Too busy</title></head><body>\r\n");
1263 q += sprintf(q, "The server is too busy to serve your request at this time.<p>\r\n");
1264 q += sprintf(q, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
1265 current_bandwidth, max_bandwidth);
1266 q += sprintf(q, "</body></html>\r\n");
1268 /* prepare output buffer */
1269 c->buffer_ptr = c->buffer;
1271 c->state = HTTPSTATE_SEND_HEADER;
1275 if (redir_type != REDIR_NONE) {
1278 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1279 if (strncasecmp(p, "Host:", 5) == 0) {
1283 p = strchr(p, '\n');
1294 while (isspace(*hostinfo))
1297 eoh = strchr(hostinfo, '\n');
1299 if (eoh[-1] == '\r')
1302 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1303 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1304 hostbuf[eoh - hostinfo] = 0;
1306 c->http_error = 200;
1308 switch(redir_type) {
1310 q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n");
1311 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1312 q += sprintf(q, "\r\n");
1313 q += sprintf(q, "<ASX Version=\"3\">\r\n");
1314 q += sprintf(q, "<!-- Autogenerated by ffserver -->\r\n");
1315 q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1316 hostbuf, filename, info);
1317 q += sprintf(q, "</ASX>\r\n");
1320 q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n");
1321 q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n");
1322 q += sprintf(q, "\r\n");
1323 q += sprintf(q, "# Autogenerated by ffserver\r\n");
1324 q += sprintf(q, "http://%s/%s%s\r\n",
1325 hostbuf, filename, info);
1328 q += sprintf(q, "HTTP/1.0 200 ASF Redirect follows\r\n");
1329 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1330 q += sprintf(q, "\r\n");
1331 q += sprintf(q, "[Reference]\r\n");
1332 q += sprintf(q, "Ref1=http://%s/%s%s\r\n",
1333 hostbuf, filename, info);
1337 char hostname[256], *p;
1338 /* extract only hostname */
1339 pstrcpy(hostname, sizeof(hostname), hostbuf);
1340 p = strrchr(hostname, ':');
1343 q += sprintf(q, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1344 /* XXX: incorrect mime type ? */
1345 q += sprintf(q, "Content-type: application/x-rtsp\r\n");
1346 q += sprintf(q, "\r\n");
1347 q += sprintf(q, "rtsp://%s:%d/%s\r\n",
1348 hostname, ntohs(my_rtsp_addr.sin_port),
1355 int sdp_data_size, len;
1356 struct sockaddr_in my_addr;
1358 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1359 q += sprintf(q, "Content-type: application/sdp\r\n");
1360 q += sprintf(q, "\r\n");
1362 len = sizeof(my_addr);
1363 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1365 /* XXX: should use a dynamic buffer */
1366 sdp_data_size = prepare_sdp_description(stream,
1369 if (sdp_data_size > 0) {
1370 memcpy(q, sdp_data, sdp_data_size);
1382 /* prepare output buffer */
1383 c->buffer_ptr = c->buffer;
1385 c->state = HTTPSTATE_SEND_HEADER;
1391 sprintf(msg, "ASX/RAM file not handled");
1395 stream->conns_served++;
1397 /* XXX: add there authenticate and IP match */
1400 /* if post, it means a feed is being sent */
1401 if (!stream->is_feed) {
1402 /* However it might be a status report from WMP! Lets log the data
1403 * as it might come in handy one day
1408 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1409 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1413 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1414 client_id = strtol(p + 18, 0, 10);
1416 p = strchr(p, '\n');
1424 char *eol = strchr(logline, '\n');
1429 if (eol[-1] == '\r')
1431 http_log("%.*s\n", eol - logline, logline);
1432 c->suppress_log = 1;
1437 http_log("\nGot request:\n%s\n", c->buffer);
1440 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1443 /* Now we have to find the client_id */
1444 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1445 if (wmpc->wmp_client_id == client_id)
1450 if (modify_current_stream(wmpc, ratebuf)) {
1451 wmpc->switch_pending = 1;
1456 sprintf(msg, "POST command not handled");
1459 if (http_start_receive_data(c) < 0) {
1460 sprintf(msg, "could not open feed");
1464 c->state = HTTPSTATE_RECEIVE_DATA;
1469 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1470 http_log("\nGot request:\n%s\n", c->buffer);
1474 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1477 /* open input stream */
1478 if (open_input_stream(c, info) < 0) {
1479 sprintf(msg, "Input stream corresponding to '%s' not found", url);
1483 /* prepare http header */
1485 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1486 mime_type = c->stream->fmt->mime_type;
1488 mime_type = "application/x-octet_stream";
1489 q += sprintf(q, "Pragma: no-cache\r\n");
1491 /* for asf, we need extra headers */
1492 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1493 /* Need to allocate a client id */
1495 c->wmp_client_id = random() & 0x7fffffff;
1497 q += sprintf(q, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=%d\r\nPragma: features=\"broadcast\"\r\n", c->wmp_client_id);
1499 q += sprintf(q, "Content-Type: %s\r\n", mime_type);
1500 q += sprintf(q, "\r\n");
1502 /* prepare output buffer */
1504 c->buffer_ptr = c->buffer;
1506 c->state = HTTPSTATE_SEND_HEADER;
1509 c->http_error = 404;
1511 q += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
1512 q += sprintf(q, "Content-type: %s\r\n", "text/html");
1513 q += sprintf(q, "\r\n");
1514 q += sprintf(q, "<HTML>\n");
1515 q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1516 q += sprintf(q, "<BODY>%s</BODY>\n", msg);
1517 q += sprintf(q, "</HTML>\n");
1519 /* prepare output buffer */
1520 c->buffer_ptr = c->buffer;
1522 c->state = HTTPSTATE_SEND_HEADER;
1526 c->http_error = 200; /* horrible : we use this value to avoid
1527 going to the send data state */
1528 c->state = HTTPSTATE_SEND_HEADER;
1532 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1534 static const char *suffix = " kMGTP";
1537 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1540 url_fprintf(pb, "%lld%c", count, *s);
1543 static void compute_stats(HTTPContext *c)
1550 ByteIOContext pb1, *pb = &pb1;
1552 if (url_open_dyn_buf(pb) < 0) {
1553 /* XXX: return an error ? */
1554 c->buffer_ptr = c->buffer;
1555 c->buffer_end = c->buffer;
1559 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1560 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1561 url_fprintf(pb, "Pragma: no-cache\r\n");
1562 url_fprintf(pb, "\r\n");
1564 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1565 if (c->stream->feed_filename) {
1566 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1568 url_fprintf(pb, "</HEAD>\n<BODY>");
1569 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1571 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1572 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1573 url_fprintf(pb, "<TR><Th valign=top>Path<th align=left>Served<br>Conns<Th><br>bytes<Th valign=top>Format<Th>Bit rate<br>kbits/s<Th align=left>Video<br>kbits/s<th><br>Codec<Th align=left>Audio<br>kbits/s<th><br>Codec<Th align=left valign=top>Feed\n");
1574 stream = first_stream;
1575 while (stream != NULL) {
1576 char sfilename[1024];
1579 if (stream->feed != stream) {
1580 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1581 eosf = sfilename + strlen(sfilename);
1582 if (eosf - sfilename >= 4) {
1583 if (strcmp(eosf - 4, ".asf") == 0) {
1584 strcpy(eosf - 4, ".asx");
1585 } else if (strcmp(eosf - 3, ".rm") == 0) {
1586 strcpy(eosf - 3, ".ram");
1587 } else if (stream->fmt == &rtp_mux) {
1588 /* generate a sample RTSP director if
1589 unicast. Generate an SDP redirector if
1591 eosf = strrchr(sfilename, '.');
1593 eosf = sfilename + strlen(sfilename);
1594 if (stream->is_multicast)
1595 strcpy(eosf, ".sdp");
1597 strcpy(eosf, ".rtsp");
1601 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1602 sfilename, stream->filename);
1603 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1604 stream->conns_served);
1605 fmt_bytecount(pb, stream->bytes_served);
1606 switch(stream->stream_type) {
1607 case STREAM_TYPE_LIVE:
1609 int audio_bit_rate = 0;
1610 int video_bit_rate = 0;
1611 const char *audio_codec_name = "";
1612 const char *video_codec_name = "";
1613 const char *audio_codec_name_extra = "";
1614 const char *video_codec_name_extra = "";
1616 for(i=0;i<stream->nb_streams;i++) {
1617 AVStream *st = stream->streams[i];
1618 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1619 switch(st->codec.codec_type) {
1620 case CODEC_TYPE_AUDIO:
1621 audio_bit_rate += st->codec.bit_rate;
1623 if (*audio_codec_name)
1624 audio_codec_name_extra = "...";
1625 audio_codec_name = codec->name;
1628 case CODEC_TYPE_VIDEO:
1629 video_bit_rate += st->codec.bit_rate;
1631 if (*video_codec_name)
1632 video_codec_name_extra = "...";
1633 video_codec_name = codec->name;
1640 url_fprintf(pb, "<TD align=center> %s <TD align=right> %d <TD align=right> %d <TD> %s %s <TD align=right> %d <TD> %s %s",
1643 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1644 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1646 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1648 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1650 url_fprintf(pb, "\n");
1654 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1658 stream = stream->next;
1660 url_fprintf(pb, "</TABLE>\n");
1662 stream = first_stream;
1663 while (stream != NULL) {
1664 if (stream->feed == stream) {
1665 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1667 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1669 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1674 /* This is somewhat linux specific I guess */
1675 snprintf(ps_cmd, sizeof(ps_cmd),
1676 "ps -o \"%%cpu,cputime\" --no-headers %d",
1679 pid_stat = popen(ps_cmd, "r");
1684 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1686 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1694 url_fprintf(pb, "<p>");
1696 url_fprintf(pb, "<table cellspacing=0 cellpadding=4><tr><th>Stream<th>type<th>kbits/s<th align=left>codec<th align=left>Parameters\n");
1698 for (i = 0; i < stream->nb_streams; i++) {
1699 AVStream *st = stream->streams[i];
1700 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1701 const char *type = "unknown";
1702 char parameters[64];
1706 switch(st->codec.codec_type) {
1707 case CODEC_TYPE_AUDIO:
1710 case CODEC_TYPE_VIDEO:
1712 sprintf(parameters, "%dx%d, q=%d-%d, fps=%d", st->codec.width, st->codec.height,
1713 st->codec.qmin, st->codec.qmax, st->codec.frame_rate / FRAME_RATE_BASE);
1718 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1719 i, type, st->codec.bit_rate/1000, codec ? codec->name : "", parameters);
1721 url_fprintf(pb, "</table>\n");
1724 stream = stream->next;
1730 AVCodecContext *enc;
1734 stream = first_feed;
1735 while (stream != NULL) {
1736 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1737 url_fprintf(pb, "<TABLE>\n");
1738 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1739 for(i=0;i<stream->nb_streams;i++) {
1740 AVStream *st = stream->streams[i];
1741 FeedData *fdata = st->priv_data;
1744 avcodec_string(buf, sizeof(buf), enc);
1745 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1746 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1747 avg /= enc->frame_size;
1748 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1749 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1751 url_fprintf(pb, "</TABLE>\n");
1752 stream = stream->next_feed;
1757 /* connection status */
1758 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1760 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1761 nb_connections, nb_max_connections);
1763 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1764 current_bandwidth, max_bandwidth);
1766 url_fprintf(pb, "<TABLE>\n");
1767 url_fprintf(pb, "<TR><th>#<th>File<th>IP<th>Proto<th>State<th>Target bits/sec<th>Actual bits/sec<th>Bytes transferred\n");
1768 c1 = first_http_ctx;
1770 while (c1 != NULL) {
1776 for (j = 0; j < c1->stream->nb_streams; j++) {
1777 if (!c1->stream->feed) {
1778 bitrate += c1->stream->streams[j]->codec.bit_rate;
1780 if (c1->feed_streams[j] >= 0) {
1781 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec.bit_rate;
1788 p = inet_ntoa(c1->from_addr.sin_addr);
1789 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1791 c1->stream ? c1->stream->filename : "",
1792 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1795 http_state[c1->state]);
1796 fmt_bytecount(pb, bitrate);
1797 url_fprintf(pb, "<td align=right>");
1798 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1799 url_fprintf(pb, "<td align=right>");
1800 fmt_bytecount(pb, c1->data_count);
1801 url_fprintf(pb, "\n");
1804 url_fprintf(pb, "</TABLE>\n");
1809 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1810 url_fprintf(pb, "</BODY>\n</HTML>\n");
1812 len = url_close_dyn_buf(pb, &c->pb_buffer);
1813 c->buffer_ptr = c->pb_buffer;
1814 c->buffer_end = c->pb_buffer + len;
1817 /* check if the parser needs to be opened for stream i */
1818 static void open_parser(AVFormatContext *s, int i)
1820 AVStream *st = s->streams[i];
1823 if (!st->codec.codec) {
1824 codec = avcodec_find_decoder(st->codec.codec_id);
1825 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1826 st->codec.parse_only = 1;
1827 if (avcodec_open(&st->codec, codec) < 0) {
1828 st->codec.parse_only = 0;
1834 static int open_input_stream(HTTPContext *c, const char *info)
1837 char input_filename[1024];
1842 /* find file name */
1843 if (c->stream->feed) {
1844 strcpy(input_filename, c->stream->feed->feed_filename);
1845 buf_size = FFM_PACKET_SIZE;
1846 /* compute position (absolute time) */
1847 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1848 stream_pos = parse_date(buf, 0);
1849 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1850 int prebuffer = strtol(buf, 0, 10);
1851 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1853 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1856 strcpy(input_filename, c->stream->feed_filename);
1858 /* compute position (relative time) */
1859 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1860 stream_pos = parse_date(buf, 1);
1865 if (input_filename[0] == '\0')
1869 { time_t when = stream_pos / 1000000;
1870 http_log("Stream pos = %lld, time=%s", stream_pos, ctime(&when));
1875 if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0) {
1876 http_log("%s not found", input_filename);
1881 /* open each parser */
1882 for(i=0;i<s->nb_streams;i++)
1885 /* choose stream as clock source (we favorize video stream if
1886 present) for packet sending */
1887 c->pts_stream_index = 0;
1888 for(i=0;i<c->stream->nb_streams;i++) {
1889 if (c->pts_stream_index == 0 &&
1890 c->stream->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO) {
1891 c->pts_stream_index = i;
1895 if (c->fmt_in->iformat->read_seek) {
1896 c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
1898 /* set the start time (needed for maxtime and RTP packet timing) */
1899 c->start_time = cur_time;
1900 c->first_pts = AV_NOPTS_VALUE;
1904 /* currently desactivated because the new PTS handling is not
1906 //#define AV_READ_FRAME
1907 #ifdef AV_READ_FRAME
1909 /* XXX: generalize that in ffmpeg for picture/audio/data. Currently
1910 the return packet MUST NOT be freed */
1911 int av_read_frame(AVFormatContext *s, AVPacket *pkt)
1914 int len, ret, old_nb_streams, i;
1916 /* see if remaining frames must be parsed */
1918 if (s->cur_len > 0) {
1919 st = s->streams[s->cur_pkt.stream_index];
1920 len = avcodec_parse_frame(&st->codec, &pkt->data, &pkt->size,
1921 s->cur_ptr, s->cur_len);
1923 /* error: get next packet */
1929 /* init pts counter if not done */
1930 if (st->pts.den == 0) {
1931 switch(st->codec.codec_type) {
1932 case CODEC_TYPE_AUDIO:
1933 st->pts_incr = (int64_t)s->pts_den;
1934 av_frac_init(&st->pts, st->pts.val, 0,
1935 (int64_t)s->pts_num * st->codec.sample_rate);
1937 case CODEC_TYPE_VIDEO:
1938 st->pts_incr = (int64_t)s->pts_den * FRAME_RATE_BASE;
1939 av_frac_init(&st->pts, st->pts.val, 0,
1940 (int64_t)s->pts_num * st->codec.frame_rate);
1947 /* a frame was read: return it */
1948 pkt->pts = st->pts.val;
1950 printf("add pts=%Lx num=%Lx den=%Lx incr=%Lx\n",
1951 st->pts.val, st->pts.num, st->pts.den, st->pts_incr);
1953 switch(st->codec.codec_type) {
1954 case CODEC_TYPE_AUDIO:
1955 av_frac_add(&st->pts, st->pts_incr * st->codec.frame_size);
1957 case CODEC_TYPE_VIDEO:
1958 av_frac_add(&st->pts, st->pts_incr);
1963 pkt->stream_index = s->cur_pkt.stream_index;
1964 /* we use the codec indication because it is
1965 more accurate than the demux flags */
1967 if (st->codec.coded_frame->key_frame)
1968 pkt->flags |= PKT_FLAG_KEY;
1973 /* free previous packet */
1974 av_free_packet(&s->cur_pkt);
1976 old_nb_streams = s->nb_streams;
1977 ret = av_read_packet(s, &s->cur_pkt);
1980 /* open parsers for each new streams */
1981 for(i = old_nb_streams; i < s->nb_streams; i++)
1983 st = s->streams[s->cur_pkt.stream_index];
1985 /* update current pts (XXX: dts handling) from packet, or
1986 use current pts if none given */
1987 if (s->cur_pkt.pts != AV_NOPTS_VALUE) {
1988 av_frac_set(&st->pts, s->cur_pkt.pts);
1990 s->cur_pkt.pts = st->pts.val;
1992 if (!st->codec.codec) {
1993 /* no codec opened: just return the raw packet */
1996 /* no codec opened: just update the pts by considering we
1997 have one frame and free the packet */
1998 if (st->pts.den == 0) {
1999 switch(st->codec.codec_type) {
2000 case CODEC_TYPE_AUDIO:
2001 st->pts_incr = (int64_t)s->pts_den * st->codec.frame_size;
2002 av_frac_init(&st->pts, st->pts.val, 0,
2003 (int64_t)s->pts_num * st->codec.sample_rate);
2005 case CODEC_TYPE_VIDEO:
2006 st->pts_incr = (int64_t)s->pts_den * FRAME_RATE_BASE;
2007 av_frac_init(&st->pts, st->pts.val, 0,
2008 (int64_t)s->pts_num * st->codec.frame_rate);
2014 av_frac_add(&st->pts, st->pts_incr);
2017 s->cur_ptr = s->cur_pkt.data;
2018 s->cur_len = s->cur_pkt.size;
2024 static int compute_send_delay(HTTPContext *c)
2026 int64_t cur_pts, delta_pts, next_pts;
2029 /* compute current pts value from system time */
2030 cur_pts = ((int64_t)(cur_time - c->start_time) * c->fmt_in->pts_den) /
2031 (c->fmt_in->pts_num * 1000LL);
2032 /* compute the delta from the stream we choose as
2033 main clock (we do that to avoid using explicit
2034 buffers to do exact packet reordering for each
2036 /* XXX: really need to fix the number of streams */
2037 if (c->pts_stream_index >= c->fmt_in->nb_streams)
2040 next_pts = c->fmt_in->streams[c->pts_stream_index]->pts.val;
2041 delta_pts = next_pts - cur_pts;
2042 if (delta_pts <= 0) {
2045 delay1 = (delta_pts * 1000 * c->fmt_in->pts_num) / c->fmt_in->pts_den;
2051 /* just fall backs */
2052 static int av_read_frame(AVFormatContext *s, AVPacket *pkt)
2054 return av_read_packet(s, pkt);
2057 static int compute_send_delay(HTTPContext *c)
2059 int datarate = 8 * get_longterm_datarate(&c->datarate, c->data_count);
2061 if (datarate > c->stream->bandwidth * 2000) {
2069 static int http_prepare_data(HTTPContext *c)
2072 AVFormatContext *ctx;
2075 case HTTPSTATE_SEND_DATA_HEADER:
2076 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2077 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
2079 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
2080 c->stream->comment);
2081 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
2082 c->stream->copyright);
2083 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
2086 /* open output stream by using specified codecs */
2087 c->fmt_ctx.oformat = c->stream->fmt;
2088 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2089 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2091 st = av_mallocz(sizeof(AVStream));
2092 c->fmt_ctx.streams[i] = st;
2093 /* if file or feed, then just take streams from FFStream struct */
2094 if (!c->stream->feed ||
2095 c->stream->feed == c->stream)
2096 memcpy(st, c->stream->streams[i], sizeof(AVStream));
2098 memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]],
2100 st->codec.frame_number = 0; /* XXX: should be done in
2101 AVStream, not in codec */
2102 /* I'm pretty sure that this is not correct...
2103 * However, without it, we crash
2105 st->codec.coded_frame = &dummy_frame;
2107 c->got_key_frame = 0;
2109 /* prepare header and save header data in a stream */
2110 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2111 /* XXX: potential leak */
2114 c->fmt_ctx.pb.is_streamed = 1;
2116 av_set_parameters(&c->fmt_ctx, NULL);
2117 av_write_header(&c->fmt_ctx);
2119 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2120 c->buffer_ptr = c->pb_buffer;
2121 c->buffer_end = c->pb_buffer + len;
2123 c->state = HTTPSTATE_SEND_DATA;
2124 c->last_packet_sent = 0;
2126 case HTTPSTATE_SEND_DATA:
2127 /* find a new packet */
2131 /* read a packet from the input stream */
2132 if (c->stream->feed) {
2133 ffm_set_write_index(c->fmt_in,
2134 c->stream->feed->feed_write_index,
2135 c->stream->feed->feed_size);
2138 if (c->stream->max_time &&
2139 c->stream->max_time + c->start_time - cur_time < 0) {
2140 /* We have timed out */
2141 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2143 if (1 || c->is_packetized) {
2144 if (compute_send_delay(c) > 0) {
2145 c->state = HTTPSTATE_WAIT;
2146 return 1; /* state changed */
2150 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2151 if (c->stream->feed && c->stream->feed->feed_opened) {
2152 /* if coming from feed, it means we reached the end of the
2153 ffm file, so must wait for more data */
2154 c->state = HTTPSTATE_WAIT_FEED;
2155 return 1; /* state changed */
2157 if (c->stream->loop) {
2158 av_close_input_file(c->fmt_in);
2160 if (open_input_stream(c, "") < 0)
2165 /* must send trailer now because eof or error */
2166 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2170 /* update first pts if needed */
2171 if (c->first_pts == AV_NOPTS_VALUE)
2172 c->first_pts = pkt.pts;
2174 /* send it to the appropriate stream */
2175 if (c->stream->feed) {
2176 /* if coming from a feed, select the right stream */
2177 if (c->switch_pending) {
2178 c->switch_pending = 0;
2179 for(i=0;i<c->stream->nb_streams;i++) {
2180 if (c->switch_feed_streams[i] == pkt.stream_index) {
2181 if (pkt.flags & PKT_FLAG_KEY) {
2182 do_switch_stream(c, i);
2185 if (c->switch_feed_streams[i] >= 0) {
2186 c->switch_pending = 1;
2190 for(i=0;i<c->stream->nb_streams;i++) {
2191 if (c->feed_streams[i] == pkt.stream_index) {
2192 pkt.stream_index = i;
2193 if (pkt.flags & PKT_FLAG_KEY) {
2194 c->got_key_frame |= 1 << i;
2196 /* See if we have all the key frames, then
2197 * we start to send. This logic is not quite
2198 * right, but it works for the case of a
2199 * single video stream with one or more
2200 * audio streams (for which every frame is
2201 * typically a key frame).
2203 if (!c->stream->send_on_key ||
2204 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2210 AVCodecContext *codec;
2213 /* specific handling for RTP: we use several
2214 output stream (one for each RTP
2215 connection). XXX: need more abstract handling */
2216 if (c->is_packetized) {
2217 c->packet_stream_index = pkt.stream_index;
2218 ctx = c->rtp_ctx[c->packet_stream_index];
2219 codec = &ctx->streams[0]->codec;
2220 /* only one stream per RTP connection */
2221 pkt.stream_index = 0;
2225 codec = &ctx->streams[pkt.stream_index]->codec;
2228 codec->coded_frame->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2231 if (codec->codec_type == CODEC_TYPE_AUDIO) {
2232 codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
2233 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
2237 if (c->is_packetized) {
2238 ret = url_open_dyn_packet_buf(&ctx->pb,
2239 url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]));
2240 c->packet_byte_count = 0;
2241 c->packet_start_time_us = av_gettime();
2243 ret = url_open_dyn_buf(&ctx->pb);
2246 /* XXX: potential leak */
2249 if (av_write_frame(ctx, pkt.stream_index, pkt.data, pkt.size)) {
2250 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2253 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2254 c->buffer_ptr = c->pb_buffer;
2255 c->buffer_end = c->pb_buffer + len;
2257 codec->frame_number++;
2259 #ifndef AV_READ_FRAME
2260 av_free_packet(&pkt);
2267 case HTTPSTATE_SEND_DATA_TRAILER:
2268 /* last packet test ? */
2269 if (c->last_packet_sent || c->is_packetized)
2272 /* prepare header */
2273 if (url_open_dyn_buf(&ctx->pb) < 0) {
2274 /* XXX: potential leak */
2277 av_write_trailer(ctx);
2278 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2279 c->buffer_ptr = c->pb_buffer;
2280 c->buffer_end = c->pb_buffer + len;
2282 c->last_packet_sent = 1;
2289 #define SHORT_TERM_BANDWIDTH 8000000
2291 /* should convert the format at the same time */
2292 static int http_send_data(HTTPContext *c)
2296 while (c->buffer_ptr >= c->buffer_end) {
2297 av_freep(&c->pb_buffer);
2298 ret = http_prepare_data(c);
2301 else if (ret == 0) {
2304 /* state change requested */
2309 if (c->buffer_ptr < c->buffer_end) {
2310 if (c->is_packetized) {
2311 /* RTP/UDP data output */
2312 len = c->buffer_end - c->buffer_ptr;
2314 /* fail safe - should never happen */
2316 c->buffer_ptr = c->buffer_end;
2319 len = (c->buffer_ptr[0] << 24) |
2320 (c->buffer_ptr[1] << 16) |
2321 (c->buffer_ptr[2] << 8) |
2323 if (len > (c->buffer_end - c->buffer_ptr))
2326 /* short term bandwidth limitation */
2327 dt = av_gettime() - c->packet_start_time_us;
2331 if ((c->packet_byte_count + len) * (int64_t)1000000 >=
2332 (SHORT_TERM_BANDWIDTH / 8) * (int64_t)dt) {
2333 /* bandwidth overflow : wait at most one tick and retry */
2334 c->state = HTTPSTATE_WAIT_SHORT;
2339 url_write(c->rtp_handles[c->packet_stream_index],
2340 c->buffer_ptr, len);
2341 c->buffer_ptr += len;
2342 c->packet_byte_count += len;
2344 /* TCP data output */
2345 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2347 if (errno != EAGAIN && errno != EINTR) {
2348 /* error : close connection */
2354 c->buffer_ptr += len;
2357 c->data_count += len;
2358 update_datarate(&c->datarate, c->data_count);
2360 c->stream->bytes_served += len;
2365 static int http_start_receive_data(HTTPContext *c)
2369 if (c->stream->feed_opened)
2373 fd = open(c->stream->feed_filename, O_RDWR);
2378 c->stream->feed_write_index = ffm_read_write_index(fd);
2379 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2380 lseek(fd, 0, SEEK_SET);
2382 /* init buffer input */
2383 c->buffer_ptr = c->buffer;
2384 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2385 c->stream->feed_opened = 1;
2389 static int http_receive_data(HTTPContext *c)
2393 if (c->buffer_end > c->buffer_ptr) {
2396 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2398 if (errno != EAGAIN && errno != EINTR) {
2399 /* error : close connection */
2402 } else if (len == 0) {
2403 /* end of connection : close it */
2406 c->buffer_ptr += len;
2407 c->data_count += len;
2408 update_datarate(&c->datarate, c->data_count);
2412 if (c->buffer_ptr >= c->buffer_end) {
2413 FFStream *feed = c->stream;
2414 /* a packet has been received : write it in the store, except
2416 if (c->data_count > FFM_PACKET_SIZE) {
2418 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2419 /* XXX: use llseek or url_seek */
2420 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2421 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2423 feed->feed_write_index += FFM_PACKET_SIZE;
2424 /* update file size */
2425 if (feed->feed_write_index > c->stream->feed_size)
2426 feed->feed_size = feed->feed_write_index;
2428 /* handle wrap around if max file size reached */
2429 if (feed->feed_write_index >= c->stream->feed_max_size)
2430 feed->feed_write_index = FFM_PACKET_SIZE;
2433 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2435 /* wake up any waiting connections */
2436 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2437 if (c1->state == HTTPSTATE_WAIT_FEED &&
2438 c1->stream->feed == c->stream->feed) {
2439 c1->state = HTTPSTATE_SEND_DATA;
2443 /* We have a header in our hands that contains useful data */
2445 AVInputFormat *fmt_in;
2446 ByteIOContext *pb = &s.pb;
2449 memset(&s, 0, sizeof(s));
2451 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2452 pb->buf_end = c->buffer_end; /* ?? */
2453 pb->is_streamed = 1;
2455 /* use feed output format name to find corresponding input format */
2456 fmt_in = av_find_input_format(feed->fmt->name);
2460 if (fmt_in->priv_data_size > 0) {
2461 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2467 if (fmt_in->read_header(&s, 0) < 0) {
2468 av_freep(&s.priv_data);
2472 /* Now we have the actual streams */
2473 if (s.nb_streams != feed->nb_streams) {
2474 av_freep(&s.priv_data);
2477 for (i = 0; i < s.nb_streams; i++) {
2478 memcpy(&feed->streams[i]->codec,
2479 &s.streams[i]->codec, sizeof(AVCodecContext));
2481 av_freep(&s.priv_data);
2483 c->buffer_ptr = c->buffer;
2488 c->stream->feed_opened = 0;
2493 /********************************************************************/
2496 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2503 switch(error_number) {
2504 #define DEF(n, c, s) case c: str = s; break;
2505 #include "rtspcodes.h"
2508 str = "Unknown Error";
2512 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2513 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2515 /* output GMT time */
2519 p = buf2 + strlen(p) - 1;
2522 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2525 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2527 rtsp_reply_header(c, error_number);
2528 url_fprintf(c->pb, "\r\n");
2531 static int rtsp_parse_request(HTTPContext *c)
2533 const char *p, *p1, *p2;
2540 RTSPHeader header1, *header = &header1;
2542 c->buffer_ptr[0] = '\0';
2545 get_word(cmd, sizeof(cmd), &p);
2546 get_word(url, sizeof(url), &p);
2547 get_word(protocol, sizeof(protocol), &p);
2549 pstrcpy(c->method, sizeof(c->method), cmd);
2550 pstrcpy(c->url, sizeof(c->url), url);
2551 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2554 if (url_open_dyn_buf(c->pb) < 0) {
2555 /* XXX: cannot do more */
2556 c->pb = NULL; /* safety */
2560 /* check version name */
2561 if (strcmp(protocol, "RTSP/1.0") != 0) {
2562 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2566 /* parse each header line */
2567 memset(header, 0, sizeof(RTSPHeader));
2568 /* skip to next line */
2569 while (*p != '\n' && *p != '\0')
2573 while (*p != '\0') {
2574 p1 = strchr(p, '\n');
2578 if (p2 > p && p2[-1] == '\r')
2580 /* skip empty line */
2584 if (len > sizeof(line) - 1)
2585 len = sizeof(line) - 1;
2586 memcpy(line, p, len);
2588 rtsp_parse_line(header, line);
2592 /* handle sequence number */
2593 c->seq = header->seq;
2595 if (!strcmp(cmd, "DESCRIBE")) {
2596 rtsp_cmd_describe(c, url);
2597 } else if (!strcmp(cmd, "SETUP")) {
2598 rtsp_cmd_setup(c, url, header);
2599 } else if (!strcmp(cmd, "PLAY")) {
2600 rtsp_cmd_play(c, url, header);
2601 } else if (!strcmp(cmd, "PAUSE")) {
2602 rtsp_cmd_pause(c, url, header);
2603 } else if (!strcmp(cmd, "TEARDOWN")) {
2604 rtsp_cmd_teardown(c, url, header);
2606 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2609 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2610 c->pb = NULL; /* safety */
2612 /* XXX: cannot do more */
2615 c->buffer_ptr = c->pb_buffer;
2616 c->buffer_end = c->pb_buffer + len;
2617 c->state = RTSPSTATE_SEND_REPLY;
2621 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2623 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2624 struct in_addr my_ip)
2626 ByteIOContext pb1, *pb = &pb1;
2627 int i, payload_type, port, private_payload_type, j;
2628 const char *ipstr, *title, *mediatype;
2631 if (url_open_dyn_buf(pb) < 0)
2634 /* general media info */
2636 url_fprintf(pb, "v=0\n");
2637 ipstr = inet_ntoa(my_ip);
2638 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2639 title = stream->title;
2640 if (title[0] == '\0')
2642 url_fprintf(pb, "s=%s\n", title);
2643 if (stream->comment[0] != '\0')
2644 url_fprintf(pb, "i=%s\n", stream->comment);
2645 if (stream->is_multicast) {
2646 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2648 /* for each stream, we output the necessary info */
2649 private_payload_type = 96;
2650 for(i = 0; i < stream->nb_streams; i++) {
2651 st = stream->streams[i];
2652 switch(st->codec.codec_type) {
2653 case CODEC_TYPE_AUDIO:
2654 mediatype = "audio";
2656 case CODEC_TYPE_VIDEO:
2657 mediatype = "video";
2660 mediatype = "application";
2663 /* NOTE: the port indication is not correct in case of
2664 unicast. It is not an issue because RTSP gives it */
2665 payload_type = rtp_get_payload_type(&st->codec);
2666 if (payload_type < 0)
2667 payload_type = private_payload_type++;
2668 if (stream->is_multicast) {
2669 port = stream->multicast_port + 2 * i;
2673 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2674 mediatype, port, payload_type);
2675 if (payload_type >= 96) {
2676 /* for private payload type, we need to give more info */
2677 switch(st->codec.codec_id) {
2678 case CODEC_ID_MPEG4:
2681 url_fprintf(pb, "a=rtpmap:%d MP4V-ES/%d\n",
2682 payload_type, 90000);
2683 /* we must also add the mpeg4 header */
2684 data = st->codec.extradata;
2686 url_fprintf(pb, "a=fmtp:%d config=");
2687 for(j=0;j<st->codec.extradata_size;j++) {
2688 url_fprintf(pb, "%02x", data[j]);
2690 url_fprintf(pb, "\n");
2695 /* XXX: add other codecs ? */
2699 url_fprintf(pb, "a=control:streamid=%d\n", i);
2701 return url_close_dyn_buf(pb, pbuffer);
2703 url_close_dyn_buf(pb, pbuffer);
2708 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2714 int content_length, len;
2715 struct sockaddr_in my_addr;
2717 /* find which url is asked */
2718 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2723 for(stream = first_stream; stream != NULL; stream = stream->next) {
2724 if (!stream->is_feed && stream->fmt == &rtp_mux &&
2725 !strcmp(path, stream->filename)) {
2729 /* no stream found */
2730 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2734 /* prepare the media description in sdp format */
2736 /* get the host IP */
2737 len = sizeof(my_addr);
2738 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2740 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2741 if (content_length < 0) {
2742 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2745 rtsp_reply_header(c, RTSP_STATUS_OK);
2746 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2747 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2748 url_fprintf(c->pb, "\r\n");
2749 put_buffer(c->pb, content, content_length);
2752 static HTTPContext *find_rtp_session(const char *session_id)
2756 if (session_id[0] == '\0')
2759 for(c = first_http_ctx; c != NULL; c = c->next) {
2760 if (!strcmp(c->session_id, session_id))
2766 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2768 RTSPTransportField *th;
2771 for(i=0;i<h->nb_transports;i++) {
2772 th = &h->transports[i];
2773 if (th->protocol == protocol)
2779 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2783 int stream_index, port;
2788 RTSPTransportField *th;
2789 struct sockaddr_in dest_addr;
2790 RTSPActionServerSetup setup;
2792 /* find which url is asked */
2793 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2798 /* now check each stream */
2799 for(stream = first_stream; stream != NULL; stream = stream->next) {
2800 if (!stream->is_feed && stream->fmt == &rtp_mux) {
2801 /* accept aggregate filenames only if single stream */
2802 if (!strcmp(path, stream->filename)) {
2803 if (stream->nb_streams != 1) {
2804 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2811 for(stream_index = 0; stream_index < stream->nb_streams;
2813 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2814 stream->filename, stream_index);
2815 if (!strcmp(path, buf))
2820 /* no stream found */
2821 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2825 /* generate session id if needed */
2826 if (h->session_id[0] == '\0') {
2827 snprintf(h->session_id, sizeof(h->session_id),
2828 "%08x%08x", (int)random(), (int)random());
2831 /* find rtp session, and create it if none found */
2832 rtp_c = find_rtp_session(h->session_id);
2834 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id);
2836 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2840 /* open input stream */
2841 if (open_input_stream(rtp_c, "") < 0) {
2842 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2846 /* always prefer UDP */
2847 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2849 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2851 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2855 rtp_c->rtp_protocol = th->protocol;
2858 /* test if stream is OK (test needed because several SETUP needs
2859 to be done for a given file) */
2860 if (rtp_c->stream != stream) {
2861 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2865 /* test if stream is already set up */
2866 if (rtp_c->rtp_ctx[stream_index]) {
2867 rtsp_reply_error(c, RTSP_STATUS_STATE);
2871 /* check transport */
2872 th = find_transport(h, rtp_c->rtp_protocol);
2873 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2874 th->client_port_min <= 0)) {
2875 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2879 /* setup default options */
2880 setup.transport_option[0] = '\0';
2881 dest_addr = rtp_c->from_addr;
2882 dest_addr.sin_port = htons(th->client_port_min);
2884 /* add transport option if needed */
2885 if (ff_rtsp_callback) {
2886 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2887 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2888 (char *)&setup, sizeof(setup),
2889 stream->rtsp_option) < 0) {
2890 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2893 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2897 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
2898 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2902 /* now everything is OK, so we can send the connection parameters */
2903 rtsp_reply_header(c, RTSP_STATUS_OK);
2905 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2907 switch(rtp_c->rtp_protocol) {
2908 case RTSP_PROTOCOL_RTP_UDP:
2909 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2910 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2911 "client_port=%d-%d;server_port=%d-%d",
2912 th->client_port_min, th->client_port_min + 1,
2915 case RTSP_PROTOCOL_RTP_TCP:
2916 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2917 stream_index * 2, stream_index * 2 + 1);
2922 if (setup.transport_option[0] != '\0') {
2923 url_fprintf(c->pb, ";%s", setup.transport_option);
2925 url_fprintf(c->pb, "\r\n");
2928 url_fprintf(c->pb, "\r\n");
2932 /* find an rtp connection by using the session ID. Check consistency
2934 static HTTPContext *find_rtp_session_with_url(const char *url,
2935 const char *session_id)
2941 rtp_c = find_rtp_session(session_id);
2945 /* find which url is asked */
2946 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2950 if (strcmp(path, rtp_c->stream->filename) != 0)
2955 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2959 rtp_c = find_rtp_session_with_url(url, h->session_id);
2961 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2965 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2966 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2967 rtp_c->state != HTTPSTATE_READY) {
2968 rtsp_reply_error(c, RTSP_STATUS_STATE);
2972 rtp_c->state = HTTPSTATE_SEND_DATA;
2974 /* now everything is OK, so we can send the connection parameters */
2975 rtsp_reply_header(c, RTSP_STATUS_OK);
2977 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2978 url_fprintf(c->pb, "\r\n");
2981 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
2985 rtp_c = find_rtp_session_with_url(url, h->session_id);
2987 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2991 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2992 rtp_c->state != HTTPSTATE_WAIT_FEED) {
2993 rtsp_reply_error(c, RTSP_STATUS_STATE);
2997 rtp_c->state = HTTPSTATE_READY;
2999 /* now everything is OK, so we can send the connection parameters */
3000 rtsp_reply_header(c, RTSP_STATUS_OK);
3002 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3003 url_fprintf(c->pb, "\r\n");
3006 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3010 rtp_c = find_rtp_session_with_url(url, h->session_id);
3012 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3016 /* abort the session */
3017 close_connection(rtp_c);
3019 if (ff_rtsp_callback) {
3020 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
3022 rtp_c->stream->rtsp_option);
3025 /* now everything is OK, so we can send the connection parameters */
3026 rtsp_reply_header(c, RTSP_STATUS_OK);
3028 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3029 url_fprintf(c->pb, "\r\n");
3033 /********************************************************************/
3036 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3037 FFStream *stream, const char *session_id)
3039 HTTPContext *c = NULL;
3041 /* XXX: should output a warning page when coming
3042 close to the connection limit */
3043 if (nb_connections >= nb_max_connections)
3046 /* add a new connection */
3047 c = av_mallocz(sizeof(HTTPContext));
3052 c->poll_entry = NULL;
3053 c->from_addr = *from_addr;
3054 c->buffer_size = IOBUFFER_INIT_SIZE;
3055 c->buffer = av_malloc(c->buffer_size);
3060 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
3061 c->state = HTTPSTATE_READY;
3062 c->is_packetized = 1;
3063 /* protocol is shown in statistics */
3064 pstrcpy(c->protocol, sizeof(c->protocol), "RTP");
3066 current_bandwidth += stream->bandwidth;
3068 c->next = first_http_ctx;
3080 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3081 command). if dest_addr is NULL, then TCP tunneling in RTSP is
3083 static int rtp_new_av_stream(HTTPContext *c,
3084 int stream_index, struct sockaddr_in *dest_addr)
3086 AVFormatContext *ctx;
3093 /* now we can open the relevant output stream */
3094 ctx = av_mallocz(sizeof(AVFormatContext));
3097 ctx->oformat = &rtp_mux;
3099 st = av_mallocz(sizeof(AVStream));
3102 ctx->nb_streams = 1;
3103 ctx->streams[0] = st;
3105 if (!c->stream->feed ||
3106 c->stream->feed == c->stream) {
3107 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3110 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3115 /* build destination RTP address */
3116 ipaddr = inet_ntoa(dest_addr->sin_addr);
3118 /* XXX: also pass as parameter to function ? */
3119 if (c->stream->is_multicast) {
3121 ttl = c->stream->multicast_ttl;
3124 snprintf(ctx->filename, sizeof(ctx->filename),
3125 "rtp://%s:%d?multicast=1&ttl=%d",
3126 ipaddr, ntohs(dest_addr->sin_port), ttl);
3128 snprintf(ctx->filename, sizeof(ctx->filename),
3129 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3132 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3134 c->rtp_handles[stream_index] = h;
3139 http_log("%s:%d - - [%s] \"RTPSTART %s/streamid=%d\"\n",
3140 ipaddr, ntohs(dest_addr->sin_port),
3142 c->stream->filename, stream_index);
3144 /* normally, no packets should be output here, but the packet size may be checked */
3145 if (url_open_dyn_packet_buf(&ctx->pb,
3146 url_get_max_packet_size(h)) < 0) {
3147 /* XXX: close stream */
3150 av_set_parameters(ctx, NULL);
3151 if (av_write_header(ctx) < 0) {
3158 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3161 c->rtp_ctx[stream_index] = ctx;
3165 /********************************************************************/
3166 /* ffserver initialization */
3168 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3172 fst = av_mallocz(sizeof(AVStream));
3175 fst->priv_data = av_mallocz(sizeof(FeedData));
3176 memcpy(&fst->codec, codec, sizeof(AVCodecContext));
3177 fst->codec.coded_frame = &dummy_frame;
3178 stream->streams[stream->nb_streams++] = fst;
3182 /* return the stream number in the feed */
3183 static int add_av_stream(FFStream *feed, AVStream *st)
3186 AVCodecContext *av, *av1;
3190 for(i=0;i<feed->nb_streams;i++) {
3191 st = feed->streams[i];
3193 if (av1->codec_id == av->codec_id &&
3194 av1->codec_type == av->codec_type &&
3195 av1->bit_rate == av->bit_rate) {
3197 switch(av->codec_type) {
3198 case CODEC_TYPE_AUDIO:
3199 if (av1->channels == av->channels &&
3200 av1->sample_rate == av->sample_rate)
3203 case CODEC_TYPE_VIDEO:
3204 if (av1->width == av->width &&
3205 av1->height == av->height &&
3206 av1->frame_rate == av->frame_rate &&
3207 av1->gop_size == av->gop_size)
3216 fst = add_av_stream1(feed, av);
3219 return feed->nb_streams - 1;
3224 static void remove_stream(FFStream *stream)
3228 while (*ps != NULL) {
3229 if (*ps == stream) {
3237 /* specific mpeg4 handling : we extract the raw parameters */
3238 static void extract_mpeg4_header(AVFormatContext *infile)
3240 int mpeg4_count, i, size;
3246 for(i=0;i<infile->nb_streams;i++) {
3247 st = infile->streams[i];
3248 if (st->codec.codec_id == CODEC_ID_MPEG4 &&
3249 st->codec.extradata == NULL) {
3256 printf("MPEG4 without extra data: trying to find header\n");
3257 while (mpeg4_count > 0) {
3258 if (av_read_packet(infile, &pkt) < 0)
3260 st = infile->streams[pkt.stream_index];
3261 if (st->codec.codec_id == CODEC_ID_MPEG4 &&
3262 st->codec.extradata == NULL) {
3263 /* fill extradata with the header */
3264 /* XXX: we make hard suppositions here ! */
3266 while (p < pkt.data + pkt.size - 4) {
3267 /* stop when vop header is found */
3268 if (p[0] == 0x00 && p[1] == 0x00 &&
3269 p[2] == 0x01 && p[3] == 0xb6) {
3270 size = p - pkt.data;
3271 // av_hex_dump(pkt.data, size);
3272 st->codec.extradata = av_malloc(size);
3273 st->codec.extradata_size = size;
3274 memcpy(st->codec.extradata, pkt.data, size);
3281 av_free_packet(&pkt);
3285 /* compute the needed AVStream for each file */
3286 static void build_file_streams(void)
3288 FFStream *stream, *stream_next;
3289 AVFormatContext *infile;
3292 /* gather all streams */
3293 for(stream = first_stream; stream != NULL; stream = stream_next) {
3294 stream_next = stream->next;
3295 if (stream->stream_type == STREAM_TYPE_LIVE &&
3297 /* the stream comes from a file */
3298 /* try to open the file */
3300 if (av_open_input_file(&infile, stream->feed_filename,
3301 NULL, 0, NULL) < 0) {
3302 http_log("%s not found", stream->feed_filename);
3303 /* remove stream (no need to spend more time on it) */
3305 remove_stream(stream);
3307 /* find all the AVStreams inside and reference them in
3309 if (av_find_stream_info(infile) < 0) {
3310 http_log("Could not find codec parameters from '%s'",
3311 stream->feed_filename);
3312 av_close_input_file(infile);
3315 extract_mpeg4_header(infile);
3317 for(i=0;i<infile->nb_streams;i++) {
3318 add_av_stream1(stream, &infile->streams[i]->codec);
3320 av_close_input_file(infile);
3326 /* compute the needed AVStream for each feed */
3327 static void build_feed_streams(void)
3329 FFStream *stream, *feed;
3332 /* gather all streams */
3333 for(stream = first_stream; stream != NULL; stream = stream->next) {
3334 feed = stream->feed;
3336 if (!stream->is_feed) {
3337 /* we handle a stream coming from a feed */
3338 for(i=0;i<stream->nb_streams;i++) {
3339 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3345 /* gather all streams */
3346 for(stream = first_stream; stream != NULL; stream = stream->next) {
3347 feed = stream->feed;
3349 if (stream->is_feed) {
3350 for(i=0;i<stream->nb_streams;i++) {
3351 stream->feed_streams[i] = i;
3357 /* create feed files if needed */
3358 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3361 if (url_exist(feed->feed_filename)) {
3362 /* See if it matches */
3366 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3367 /* Now see if it matches */
3368 if (s->nb_streams == feed->nb_streams) {
3370 for(i=0;i<s->nb_streams;i++) {
3372 sf = feed->streams[i];
3375 if (sf->index != ss->index ||
3377 printf("Index & Id do not match for stream %d\n", i);
3380 AVCodecContext *ccf, *ccs;
3384 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3386 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3387 printf("Codecs do not match for stream %d\n", i);
3389 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3390 printf("Codec bitrates do not match for stream %d\n", i);
3392 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3393 if (CHECK_CODEC(frame_rate) ||
3394 CHECK_CODEC(width) ||
3395 CHECK_CODEC(height)) {
3396 printf("Codec width, height and framerate do not match for stream %d\n", i);
3399 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3400 if (CHECK_CODEC(sample_rate) ||
3401 CHECK_CODEC(channels) ||
3402 CHECK_CODEC(frame_size)) {
3403 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3407 printf("Unknown codec type\n");
3416 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3417 feed->feed_filename, s->nb_streams, feed->nb_streams);
3420 av_close_input_file(s);
3422 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3423 feed->feed_filename);
3426 unlink(feed->feed_filename);
3428 if (!url_exist(feed->feed_filename)) {
3429 AVFormatContext s1, *s = &s1;
3431 /* only write the header of the ffm file */
3432 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3433 fprintf(stderr, "Could not open output feed file '%s'\n",
3434 feed->feed_filename);
3437 s->oformat = feed->fmt;
3438 s->nb_streams = feed->nb_streams;
3439 for(i=0;i<s->nb_streams;i++) {
3441 st = feed->streams[i];
3444 av_set_parameters(s, NULL);
3446 /* XXX: need better api */
3447 av_freep(&s->priv_data);
3450 /* get feed size and write index */
3451 fd = open(feed->feed_filename, O_RDONLY);
3453 fprintf(stderr, "Could not open output feed file '%s'\n",
3454 feed->feed_filename);
3458 feed->feed_write_index = ffm_read_write_index(fd);
3459 feed->feed_size = lseek(fd, 0, SEEK_END);
3460 /* ensure that we do not wrap before the end of file */
3461 if (feed->feed_max_size < feed->feed_size)
3462 feed->feed_max_size = feed->feed_size;
3468 /* compute the bandwidth used by each stream */
3469 static void compute_bandwidth(void)
3474 for(stream = first_stream; stream != NULL; stream = stream->next) {
3476 for(i=0;i<stream->nb_streams;i++) {
3477 AVStream *st = stream->streams[i];
3478 switch(st->codec.codec_type) {
3479 case CODEC_TYPE_AUDIO:
3480 case CODEC_TYPE_VIDEO:
3481 bandwidth += st->codec.bit_rate;
3487 stream->bandwidth = (bandwidth + 999) / 1000;
3491 static void get_arg(char *buf, int buf_size, const char **pp)
3498 while (isspace(*p)) p++;
3501 if (*p == '\"' || *p == '\'')
3513 if ((q - buf) < buf_size - 1)
3518 if (quote && *p == quote)
3523 /* add a codec and set the default parameters */
3524 static void add_codec(FFStream *stream, AVCodecContext *av)
3528 /* compute default parameters */
3529 switch(av->codec_type) {
3530 case CODEC_TYPE_AUDIO:
3531 if (av->bit_rate == 0)
3532 av->bit_rate = 64000;
3533 if (av->sample_rate == 0)
3534 av->sample_rate = 22050;
3535 if (av->channels == 0)
3538 case CODEC_TYPE_VIDEO:
3539 if (av->bit_rate == 0)
3540 av->bit_rate = 64000;
3541 if (av->frame_rate == 0)
3542 av->frame_rate = 5 * FRAME_RATE_BASE;
3543 if (av->width == 0 || av->height == 0) {
3547 /* Bitrate tolerance is less for streaming */
3548 if (av->bit_rate_tolerance == 0)
3549 av->bit_rate_tolerance = av->bit_rate / 4;
3554 if (av->max_qdiff == 0)
3556 av->qcompress = 0.5;
3560 av->rc_eq = "tex^qComp";
3561 if (!av->i_quant_factor)
3562 av->i_quant_factor = -0.8;
3563 if (!av->b_quant_factor)
3564 av->b_quant_factor = 1.25;
3565 if (!av->b_quant_offset)
3566 av->b_quant_offset = 1.25;
3567 if (!av->rc_min_rate)
3568 av->rc_min_rate = av->bit_rate / 2;
3569 if (!av->rc_max_rate)
3570 av->rc_max_rate = av->bit_rate * 2;
3577 st = av_mallocz(sizeof(AVStream));
3580 stream->streams[stream->nb_streams++] = st;
3581 memcpy(&st->codec, av, sizeof(AVCodecContext));
3584 static int opt_audio_codec(const char *arg)
3590 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3595 return CODEC_ID_NONE;
3601 static int opt_video_codec(const char *arg)
3607 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3612 return CODEC_ID_NONE;
3618 /* simplistic plugin support */
3620 #ifdef CONFIG_HAVE_DLOPEN
3621 void load_module(const char *filename)
3624 void (*init_func)(void);
3625 dll = dlopen(filename, RTLD_NOW);
3627 fprintf(stderr, "Could not load module '%s' - %s\n",
3628 filename, dlerror());
3632 init_func = dlsym(dll, "ffserver_module_init");
3635 "%s: init function 'ffserver_module_init()' not found\n",
3644 static int parse_ffconfig(const char *filename)
3651 int val, errors, line_num;
3652 FFStream **last_stream, *stream, *redirect;
3653 FFStream **last_feed, *feed;
3654 AVCodecContext audio_enc, video_enc;
3655 int audio_id, video_id;
3657 f = fopen(filename, "r");
3665 first_stream = NULL;
3666 last_stream = &first_stream;
3668 last_feed = &first_feed;
3672 audio_id = CODEC_ID_NONE;
3673 video_id = CODEC_ID_NONE;
3675 if (fgets(line, sizeof(line), f) == NULL)
3681 if (*p == '\0' || *p == '#')
3684 get_arg(cmd, sizeof(cmd), &p);
3686 if (!strcasecmp(cmd, "Port")) {
3687 get_arg(arg, sizeof(arg), &p);
3688 my_http_addr.sin_port = htons (atoi(arg));
3689 } else if (!strcasecmp(cmd, "BindAddress")) {
3690 get_arg(arg, sizeof(arg), &p);
3691 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3692 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3693 filename, line_num, arg);
3696 } else if (!strcasecmp(cmd, "NoDaemon")) {
3697 ffserver_daemon = 0;
3698 } else if (!strcasecmp(cmd, "RTSPPort")) {
3699 get_arg(arg, sizeof(arg), &p);
3700 my_rtsp_addr.sin_port = htons (atoi(arg));
3701 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3702 get_arg(arg, sizeof(arg), &p);
3703 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3704 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3705 filename, line_num, arg);
3708 } else if (!strcasecmp(cmd, "MaxClients")) {
3709 get_arg(arg, sizeof(arg), &p);
3711 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3712 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3713 filename, line_num, arg);
3716 nb_max_connections = val;
3718 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3719 get_arg(arg, sizeof(arg), &p);
3721 if (val < 10 || val > 100000) {
3722 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3723 filename, line_num, arg);
3726 max_bandwidth = val;
3728 } else if (!strcasecmp(cmd, "CustomLog")) {
3729 get_arg(logfilename, sizeof(logfilename), &p);
3730 } else if (!strcasecmp(cmd, "<Feed")) {
3731 /*********************************************/
3732 /* Feed related options */
3734 if (stream || feed) {
3735 fprintf(stderr, "%s:%d: Already in a tag\n",
3736 filename, line_num);
3738 feed = av_mallocz(sizeof(FFStream));
3739 /* add in stream list */
3740 *last_stream = feed;
3741 last_stream = &feed->next;
3742 /* add in feed list */
3744 last_feed = &feed->next_feed;
3746 get_arg(feed->filename, sizeof(feed->filename), &p);
3747 q = strrchr(feed->filename, '>');
3750 feed->fmt = guess_format("ffm", NULL, NULL);
3751 /* defaut feed file */
3752 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3753 "/tmp/%s.ffm", feed->filename);
3754 feed->feed_max_size = 5 * 1024 * 1024;
3756 feed->feed = feed; /* self feeding :-) */
3758 } else if (!strcasecmp(cmd, "Launch")) {
3762 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3764 feed->child_argv[0] = av_malloc(7);
3765 strcpy(feed->child_argv[0], "ffmpeg");
3767 for (i = 1; i < 62; i++) {
3770 get_arg(argbuf, sizeof(argbuf), &p);
3774 feed->child_argv[i] = av_malloc(strlen(argbuf + 1));
3775 strcpy(feed->child_argv[i], argbuf);
3778 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3780 snprintf(feed->child_argv[i], 256, "http://127.0.0.1:%d/%s",
3781 ntohs(my_http_addr.sin_port), feed->filename);
3783 } else if (!strcasecmp(cmd, "File")) {
3785 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3786 } else if (stream) {
3787 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3789 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3794 get_arg(arg, sizeof(arg), &p);
3796 fsize = strtod(p1, (char **)&p1);
3797 switch(toupper(*p1)) {
3802 fsize *= 1024 * 1024;
3805 fsize *= 1024 * 1024 * 1024;
3808 feed->feed_max_size = (int64_t)fsize;
3810 } else if (!strcasecmp(cmd, "</Feed>")) {
3812 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3813 filename, line_num);
3817 /* Make sure that we start out clean */
3818 if (unlink(feed->feed_filename) < 0
3819 && errno != ENOENT) {
3820 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3821 filename, line_num, feed->feed_filename, strerror(errno));
3827 } else if (!strcasecmp(cmd, "<Stream")) {
3828 /*********************************************/
3829 /* Stream related options */
3831 if (stream || feed) {
3832 fprintf(stderr, "%s:%d: Already in a tag\n",
3833 filename, line_num);
3835 stream = av_mallocz(sizeof(FFStream));
3836 *last_stream = stream;
3837 last_stream = &stream->next;
3839 get_arg(stream->filename, sizeof(stream->filename), &p);
3840 q = strrchr(stream->filename, '>');
3843 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3844 memset(&audio_enc, 0, sizeof(AVCodecContext));
3845 memset(&video_enc, 0, sizeof(AVCodecContext));
3846 audio_id = CODEC_ID_NONE;
3847 video_id = CODEC_ID_NONE;
3849 audio_id = stream->fmt->audio_codec;
3850 video_id = stream->fmt->video_codec;
3853 } else if (!strcasecmp(cmd, "Feed")) {
3854 get_arg(arg, sizeof(arg), &p);
3859 while (sfeed != NULL) {
3860 if (!strcmp(sfeed->filename, arg))
3862 sfeed = sfeed->next_feed;
3865 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3866 filename, line_num, arg);
3868 stream->feed = sfeed;
3871 } else if (!strcasecmp(cmd, "Format")) {
3872 get_arg(arg, sizeof(arg), &p);
3873 if (!strcmp(arg, "status")) {
3874 stream->stream_type = STREAM_TYPE_STATUS;
3877 stream->stream_type = STREAM_TYPE_LIVE;
3878 /* jpeg cannot be used here, so use single frame jpeg */
3879 if (!strcmp(arg, "jpeg"))
3880 strcpy(arg, "singlejpeg");
3881 stream->fmt = guess_stream_format(arg, NULL, NULL);
3883 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3884 filename, line_num, arg);
3889 audio_id = stream->fmt->audio_codec;
3890 video_id = stream->fmt->video_codec;
3892 } else if (!strcasecmp(cmd, "FaviconURL")) {
3893 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3894 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3896 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3897 filename, line_num);
3900 } else if (!strcasecmp(cmd, "Author")) {
3902 get_arg(stream->author, sizeof(stream->author), &p);
3904 } else if (!strcasecmp(cmd, "Comment")) {
3906 get_arg(stream->comment, sizeof(stream->comment), &p);
3908 } else if (!strcasecmp(cmd, "Copyright")) {
3910 get_arg(stream->copyright, sizeof(stream->copyright), &p);
3912 } else if (!strcasecmp(cmd, "Title")) {
3914 get_arg(stream->title, sizeof(stream->title), &p);
3916 } else if (!strcasecmp(cmd, "Preroll")) {
3917 get_arg(arg, sizeof(arg), &p);
3919 stream->prebuffer = atof(arg) * 1000;
3921 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
3923 stream->send_on_key = 1;
3925 } else if (!strcasecmp(cmd, "AudioCodec")) {
3926 get_arg(arg, sizeof(arg), &p);
3927 audio_id = opt_audio_codec(arg);
3928 if (audio_id == CODEC_ID_NONE) {
3929 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
3930 filename, line_num, arg);
3933 } else if (!strcasecmp(cmd, "VideoCodec")) {
3934 get_arg(arg, sizeof(arg), &p);
3935 video_id = opt_video_codec(arg);
3936 if (video_id == CODEC_ID_NONE) {
3937 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
3938 filename, line_num, arg);
3941 } else if (!strcasecmp(cmd, "MaxTime")) {
3942 get_arg(arg, sizeof(arg), &p);
3944 stream->max_time = atof(arg) * 1000;
3946 } else if (!strcasecmp(cmd, "AudioBitRate")) {
3947 get_arg(arg, sizeof(arg), &p);
3949 audio_enc.bit_rate = atoi(arg) * 1000;
3951 } else if (!strcasecmp(cmd, "AudioChannels")) {
3952 get_arg(arg, sizeof(arg), &p);
3954 audio_enc.channels = atoi(arg);
3956 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
3957 get_arg(arg, sizeof(arg), &p);
3959 audio_enc.sample_rate = atoi(arg);
3961 } else if (!strcasecmp(cmd, "AudioQuality")) {
3962 get_arg(arg, sizeof(arg), &p);
3964 // audio_enc.quality = atof(arg) * 1000;
3966 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
3968 int minrate, maxrate;
3970 get_arg(arg, sizeof(arg), &p);
3972 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
3973 video_enc.rc_min_rate = minrate * 1000;
3974 video_enc.rc_max_rate = maxrate * 1000;
3976 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
3977 filename, line_num, arg);
3981 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
3983 get_arg(arg, sizeof(arg), &p);
3984 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
3986 } else if (!strcasecmp(cmd, "VideoBitRate")) {
3987 get_arg(arg, sizeof(arg), &p);
3989 video_enc.bit_rate = atoi(arg) * 1000;
3991 } else if (!strcasecmp(cmd, "VideoSize")) {
3992 get_arg(arg, sizeof(arg), &p);
3994 parse_image_size(&video_enc.width, &video_enc.height, arg);
3995 if ((video_enc.width % 16) != 0 ||
3996 (video_enc.height % 16) != 0) {
3997 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
3998 filename, line_num);
4002 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4003 get_arg(arg, sizeof(arg), &p);
4005 video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
4007 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4008 get_arg(arg, sizeof(arg), &p);
4010 video_enc.gop_size = atoi(arg);
4012 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4014 video_enc.gop_size = 1;
4016 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4018 video_enc.flags |= CODEC_FLAG_HQ;
4020 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4021 get_arg(arg, sizeof(arg), &p);
4023 video_enc.max_qdiff = atoi(arg);
4024 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4025 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4026 filename, line_num);
4030 } else if (!strcasecmp(cmd, "VideoQMax")) {
4031 get_arg(arg, sizeof(arg), &p);
4033 video_enc.qmax = atoi(arg);
4034 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4035 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4036 filename, line_num);
4040 } else if (!strcasecmp(cmd, "VideoQMin")) {
4041 get_arg(arg, sizeof(arg), &p);
4043 video_enc.qmin = atoi(arg);
4044 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4045 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4046 filename, line_num);
4050 } else if (!strcasecmp(cmd, "LumaElim")) {
4051 get_arg(arg, sizeof(arg), &p);
4053 video_enc.luma_elim_threshold = atoi(arg);
4055 } else if (!strcasecmp(cmd, "ChromaElim")) {
4056 get_arg(arg, sizeof(arg), &p);
4058 video_enc.chroma_elim_threshold = atoi(arg);
4060 } else if (!strcasecmp(cmd, "LumiMask")) {
4061 get_arg(arg, sizeof(arg), &p);
4063 video_enc.lumi_masking = atof(arg);
4065 } else if (!strcasecmp(cmd, "DarkMask")) {
4066 get_arg(arg, sizeof(arg), &p);
4068 video_enc.dark_masking = atof(arg);
4070 } else if (!strcasecmp(cmd, "NoVideo")) {
4071 video_id = CODEC_ID_NONE;
4072 } else if (!strcasecmp(cmd, "NoAudio")) {
4073 audio_id = CODEC_ID_NONE;
4074 } else if (!strcasecmp(cmd, "ACL")) {
4078 get_arg(arg, sizeof(arg), &p);
4079 if (strcasecmp(arg, "allow") == 0) {
4080 acl.action = IP_ALLOW;
4081 } else if (strcasecmp(arg, "deny") == 0) {
4082 acl.action = IP_DENY;
4084 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4085 filename, line_num, arg);
4089 get_arg(arg, sizeof(arg), &p);
4091 he = gethostbyname(arg);
4093 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4094 filename, line_num, arg);
4097 /* Only take the first */
4098 acl.first = *(struct in_addr *) he->h_addr_list[0];
4099 acl.last = acl.first;
4102 get_arg(arg, sizeof(arg), &p);
4105 he = gethostbyname(arg);
4107 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4108 filename, line_num, arg);
4111 /* Only take the first */
4112 acl.last = *(struct in_addr *) he->h_addr_list[0];
4117 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
4118 IPAddressACL **naclp = 0;
4124 naclp = &stream->acl;
4128 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4129 filename, line_num);
4135 naclp = &(*naclp)->next;
4140 } else if (!strcasecmp(cmd, "RTSPOption")) {
4141 get_arg(arg, sizeof(arg), &p);
4143 av_freep(&stream->rtsp_option);
4144 /* XXX: av_strdup ? */
4145 stream->rtsp_option = av_malloc(strlen(arg) + 1);
4146 if (stream->rtsp_option) {
4147 strcpy(stream->rtsp_option, arg);
4150 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4151 get_arg(arg, sizeof(arg), &p);
4153 if (!inet_aton(arg, &stream->multicast_ip)) {
4154 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
4155 filename, line_num, arg);
4158 stream->is_multicast = 1;
4159 stream->loop = 1; /* default is looping */
4161 } else if (!strcasecmp(cmd, "MulticastPort")) {
4162 get_arg(arg, sizeof(arg), &p);
4164 stream->multicast_port = atoi(arg);
4166 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4167 get_arg(arg, sizeof(arg), &p);
4169 stream->multicast_ttl = atoi(arg);
4171 } else if (!strcasecmp(cmd, "NoLoop")) {
4175 } else if (!strcasecmp(cmd, "</Stream>")) {
4177 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4178 filename, line_num);
4181 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4182 if (audio_id != CODEC_ID_NONE) {
4183 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4184 audio_enc.codec_id = audio_id;
4185 add_codec(stream, &audio_enc);
4187 if (video_id != CODEC_ID_NONE) {
4188 video_enc.codec_type = CODEC_TYPE_VIDEO;
4189 video_enc.codec_id = video_id;
4190 add_codec(stream, &video_enc);
4194 } else if (!strcasecmp(cmd, "<Redirect")) {
4195 /*********************************************/
4197 if (stream || feed || redirect) {
4198 fprintf(stderr, "%s:%d: Already in a tag\n",
4199 filename, line_num);
4202 redirect = av_mallocz(sizeof(FFStream));
4203 *last_stream = redirect;
4204 last_stream = &redirect->next;
4206 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4207 q = strrchr(redirect->filename, '>');
4210 redirect->stream_type = STREAM_TYPE_REDIRECT;
4212 } else if (!strcasecmp(cmd, "URL")) {
4214 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4216 } else if (!strcasecmp(cmd, "</Redirect>")) {
4218 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4219 filename, line_num);
4222 if (!redirect->feed_filename[0]) {
4223 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4224 filename, line_num);
4228 } else if (!strcasecmp(cmd, "LoadModule")) {
4229 get_arg(arg, sizeof(arg), &p);
4230 #ifdef CONFIG_HAVE_DLOPEN
4233 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4234 filename, line_num, arg);
4238 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4239 filename, line_num, cmd);
4253 static void write_packet(FFCodec *ffenc,
4254 uint8_t *buf, int size)
4257 AVCodecContext *enc = &ffenc->enc;
4259 mk_header(&hdr, enc, size);
4260 wptr = http_fifo.wptr;
4261 fifo_write(&http_fifo, (uint8_t *)&hdr, sizeof(hdr), &wptr);
4262 fifo_write(&http_fifo, buf, size, &wptr);
4263 /* atomic modification of wptr */
4264 http_fifo.wptr = wptr;
4265 ffenc->data_count += size;
4266 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4270 static void help(void)
4272 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4273 "usage: ffserver [-L] [-h] [-f configfile]\n"
4274 "Hyper fast multi format Audio/Video streaming server\n"
4276 "-L : print the LICENCE\n"
4278 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4282 static void licence(void)
4285 "ffserver version " FFMPEG_VERSION "\n"
4286 "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4287 "This library is free software; you can redistribute it and/or\n"
4288 "modify it under the terms of the GNU Lesser General Public\n"
4289 "License as published by the Free Software Foundation; either\n"
4290 "version 2 of the License, or (at your option) any later version.\n"
4292 "This library is distributed in the hope that it will be useful,\n"
4293 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4294 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4295 "Lesser General Public License for more details.\n"
4297 "You should have received a copy of the GNU Lesser General Public\n"
4298 "License along with this library; if not, write to the Free Software\n"
4299 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
4303 static void handle_child_exit(int sig)
4308 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4311 for (feed = first_feed; feed; feed = feed->next) {
4312 if (feed->pid == pid) {
4313 int uptime = time(0) - feed->pid_start;
4316 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4319 /* Turn off any more restarts */
4320 feed->child_argv = 0;
4326 need_to_start_children = 1;
4329 int main(int argc, char **argv)
4331 const char *config_filename;
4333 struct sigaction sigact;
4337 config_filename = "/etc/ffserver.conf";
4339 my_program_name = argv[0];
4340 my_program_dir = getcwd(0, 0);
4341 ffserver_daemon = 1;
4344 c = getopt(argc, argv, "ndLh?f:");
4360 ffserver_daemon = 0;
4363 config_filename = optarg;
4370 putenv("http_proxy"); /* Kill the http_proxy */
4372 srandom(gettime_ms() + (getpid() << 16));
4374 /* address on which the server will handle HTTP connections */
4375 my_http_addr.sin_family = AF_INET;
4376 my_http_addr.sin_port = htons (8080);
4377 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4379 /* address on which the server will handle RTSP connections */
4380 my_rtsp_addr.sin_family = AF_INET;
4381 my_rtsp_addr.sin_port = htons (5454);
4382 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4384 nb_max_connections = 5;
4385 max_bandwidth = 1000;
4386 first_stream = NULL;
4387 logfilename[0] = '\0';
4389 memset(&sigact, 0, sizeof(sigact));
4390 sigact.sa_handler = handle_child_exit;
4391 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4392 sigaction(SIGCHLD, &sigact, 0);
4394 if (parse_ffconfig(config_filename) < 0) {
4395 fprintf(stderr, "Incorrect config file - exiting.\n");
4399 build_file_streams();
4401 build_feed_streams();
4403 compute_bandwidth();
4405 /* put the process in background and detach it from its TTY */
4406 if (ffserver_daemon) {
4413 } else if (pid > 0) {
4421 open("/dev/null", O_RDWR);
4422 if (strcmp(logfilename, "-") != 0) {
4432 signal(SIGPIPE, SIG_IGN);
4434 /* open log file if needed */
4435 if (logfilename[0] != '\0') {
4436 if (!strcmp(logfilename, "-"))
4439 logfile = fopen(logfilename, "w");
4442 if (http_server() < 0) {
4443 fprintf(stderr, "Could not start server\n");