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
23 #include <netinet/in.h>
26 #include <sys/ioctl.h>
31 #include <sys/types.h>
32 #include <sys/socket.h>
34 #include <arpa/inet.h>
42 /* maximum number of simultaneous HTTP connections */
43 #define HTTP_MAX_CONNECTIONS 2000
46 HTTPSTATE_WAIT_REQUEST,
47 HTTPSTATE_SEND_HEADER,
48 HTTPSTATE_SEND_DATA_HEADER,
49 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
50 HTTPSTATE_SEND_DATA_TRAILER,
51 HTTPSTATE_RECEIVE_DATA,
52 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
53 HTTPSTATE_WAIT, /* wait before sending next packets */
54 HTTPSTATE_WAIT_SHORT, /* short wait for short term
55 bandwidth limitation */
58 RTSPSTATE_WAIT_REQUEST,
62 const char *http_state[] = {
79 #define IOBUFFER_INIT_SIZE 8192
81 /* coef for exponential mean for bitrate estimation in statistics */
84 /* timeouts are in ms */
85 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
86 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
88 #define SYNC_TIMEOUT (10 * 1000)
95 /* context associated with one connection */
96 typedef struct HTTPContext {
98 int fd; /* socket file descriptor */
99 struct sockaddr_in from_addr; /* origin */
100 struct pollfd *poll_entry; /* used when polling */
102 UINT8 *buffer_ptr, *buffer_end;
104 struct HTTPContext *next;
105 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
109 /* input format handling */
110 AVFormatContext *fmt_in;
111 long start_time; /* In milliseconds - this wraps fairly often */
112 INT64 first_pts; /* initial pts value */
113 int pts_stream_index; /* stream we choose as clock reference */
114 /* output format handling */
115 struct FFStream *stream;
116 /* -1 is invalid stream */
117 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
118 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
120 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
121 int last_packet_sent; /* true if last data packet was sent */
124 DataRateData datarate;
131 int is_packetized; /* if true, the stream is packetized */
132 int packet_stream_index; /* current stream for output in state machine */
134 /* RTSP state specific */
135 UINT8 *pb_buffer; /* XXX: use that in all the code */
137 int seq; /* RTSP sequence number */
139 /* RTP state specific */
140 enum RTSPProtocol rtp_protocol;
141 char session_id[32]; /* session id */
142 AVFormatContext *rtp_ctx[MAX_STREAMS];
143 URLContext *rtp_handles[MAX_STREAMS];
144 /* RTP short term bandwidth limitation */
145 int packet_byte_count;
146 int packet_start_time_us; /* used for short durations (a few
150 /* each generated stream is described here */
154 STREAM_TYPE_REDIRECT,
157 enum IPAddressAction {
162 typedef struct IPAddressACL {
163 struct IPAddressACL *next;
164 enum IPAddressAction action;
165 struct in_addr first;
169 /* description of each stream of the ffserver.conf file */
170 typedef struct FFStream {
171 enum StreamType stream_type;
172 char filename[1024]; /* stream filename */
173 struct FFStream *feed; /* feed we are using (can be null if
178 int prebuffer; /* Number of millseconds early to start */
179 long max_time; /* Number of milliseconds to run */
181 AVStream *streams[MAX_STREAMS];
182 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
183 char feed_filename[1024]; /* file name of the feed storage, or
184 input file name for a stream */
189 pid_t pid; /* Of ffmpeg process */
190 time_t pid_start; /* Of ffmpeg process */
192 struct FFStream *next;
195 /* multicast specific */
197 struct in_addr multicast_ip;
198 int multicast_port; /* first port used for multicast */
201 int feed_opened; /* true if someone is writing to the feed */
202 int is_feed; /* true if it is a feed */
205 INT64 feed_max_size; /* maximum storage size */
206 INT64 feed_write_index; /* current write position in feed (it wraps round) */
207 INT64 feed_size; /* current size of feed */
208 struct FFStream *next_feed;
211 typedef struct FeedData {
212 long long data_count;
213 float avg_frame_size; /* frame size averraged over last frames with exponential mean */
216 struct sockaddr_in my_http_addr;
217 struct sockaddr_in my_rtsp_addr;
219 char logfilename[1024];
220 HTTPContext *first_http_ctx;
221 FFStream *first_feed; /* contains only feeds */
222 FFStream *first_stream; /* contains all streams, including feeds */
224 static void new_connection(int server_fd, int is_rtsp);
225 static void close_connection(HTTPContext *c);
228 static int handle_connection(HTTPContext *c);
229 static int http_parse_request(HTTPContext *c);
230 static int http_send_data(HTTPContext *c);
231 static void compute_stats(HTTPContext *c);
232 static int open_input_stream(HTTPContext *c, const char *info);
233 static int http_start_receive_data(HTTPContext *c);
234 static int http_receive_data(HTTPContext *c);
235 static int compute_send_delay(HTTPContext *c);
238 static int rtsp_parse_request(HTTPContext *c);
239 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
240 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
241 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
242 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
243 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
246 static int prepare_sdp_description(FFStream *stream, UINT8 **pbuffer,
247 struct in_addr my_ip);
250 static HTTPContext *rtp_new_connection(HTTPContext *rtsp_c,
251 FFStream *stream, const char *session_id);
252 static int rtp_new_av_stream(HTTPContext *c,
253 int stream_index, struct sockaddr_in *dest_addr);
255 static const char *my_program_name;
256 static const char *my_program_dir;
258 static int ffserver_debug;
259 static int ffserver_daemon;
260 static int no_launch;
261 static int need_to_start_children;
263 int nb_max_connections;
266 int nb_max_bandwidth;
269 static long cur_time; // Making this global saves on passing it around everywhere
271 static long gettime_ms(void)
275 gettimeofday(&tv,NULL);
276 return (long long)tv.tv_sec * 1000 + (tv.tv_usec / 1000);
279 static FILE *logfile = NULL;
281 static void http_log(char *fmt, ...)
287 vfprintf(logfile, fmt, ap);
293 static void log_connection(HTTPContext *c)
295 char buf1[32], buf2[32], *p;
301 /* XXX: reentrant function ? */
302 p = inet_ntoa(c->from_addr.sin_addr);
307 p = buf2 + strlen(p) - 1;
310 http_log("%s - - [%s] \"%s %s %s\" %d %lld\n",
311 buf1, buf2, c->method, c->url, c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
314 static void update_datarate(DataRateData *drd, INT64 count)
316 if (!drd->time1 && !drd->count1) {
317 drd->time1 = drd->time2 = cur_time;
318 drd->count1 = drd->count2 = count;
320 if (cur_time - drd->time2 > 5000) {
321 drd->time1 = drd->time2;
322 drd->count1 = drd->count2;
323 drd->time2 = cur_time;
329 /* In bytes per second */
330 static int compute_datarate(DataRateData *drd, INT64 count)
332 if (cur_time == drd->time1)
335 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
338 static int get_longterm_datarate(DataRateData *drd, INT64 count)
340 /* You get the first 3 seconds flat out */
341 if (cur_time - drd->time1 < 3000)
344 return compute_datarate(drd, count);
348 static void start_children(FFStream *feed)
353 for (; feed; feed = feed->next) {
354 if (feed->child_argv && !feed->pid) {
355 feed->pid_start = time(0);
360 fprintf(stderr, "Unable to create children\n");
369 for (i = 3; i < 256; i++) {
373 if (!ffserver_debug) {
374 i = open("/dev/null", O_RDWR);
383 pstrcpy(pathname, sizeof(pathname), my_program_name);
385 slash = strrchr(pathname, '/');
391 strcpy(slash, "ffmpeg");
393 /* This is needed to make relative pathnames work */
394 chdir(my_program_dir);
396 execvp(pathname, feed->child_argv);
404 /* open a listening socket */
405 static int socket_open_listen(struct sockaddr_in *my_addr)
409 server_fd = socket(AF_INET,SOCK_STREAM,0);
416 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
418 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
424 if (listen (server_fd, 5) < 0) {
429 fcntl(server_fd, F_SETFL, O_NONBLOCK);
435 /* main loop of the http server */
436 static int http_server(void)
438 int server_fd, ret, rtsp_server_fd, delay, delay1;
439 struct pollfd poll_table[HTTP_MAX_CONNECTIONS + 2], *poll_entry;
440 HTTPContext *c, *c_next;
442 server_fd = socket_open_listen(&my_http_addr);
446 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
447 if (rtsp_server_fd < 0)
450 http_log("ffserver started.\n");
452 start_children(first_feed);
454 first_http_ctx = NULL;
456 first_http_ctx = NULL;
458 poll_entry = poll_table;
459 poll_entry->fd = server_fd;
460 poll_entry->events = POLLIN;
463 poll_entry->fd = rtsp_server_fd;
464 poll_entry->events = POLLIN;
467 /* wait for events on each HTTP handle */
474 case HTTPSTATE_SEND_HEADER:
475 case RTSPSTATE_SEND_REPLY:
476 c->poll_entry = poll_entry;
478 poll_entry->events = POLLOUT;
481 case HTTPSTATE_SEND_DATA_HEADER:
482 case HTTPSTATE_SEND_DATA:
483 case HTTPSTATE_SEND_DATA_TRAILER:
484 if (!c->is_packetized) {
485 /* for TCP, we output as much as we can (may need to put a limit) */
486 c->poll_entry = poll_entry;
488 poll_entry->events = POLLOUT;
491 /* not strictly correct, but currently cannot add
492 more than one fd in poll entry */
496 case HTTPSTATE_WAIT_REQUEST:
497 case HTTPSTATE_RECEIVE_DATA:
498 case HTTPSTATE_WAIT_FEED:
499 case RTSPSTATE_WAIT_REQUEST:
500 /* need to catch errors */
501 c->poll_entry = poll_entry;
503 poll_entry->events = POLLIN;/* Maybe this will work */
507 c->poll_entry = NULL;
508 delay1 = compute_send_delay(c);
512 case HTTPSTATE_WAIT_SHORT:
513 c->poll_entry = NULL;
514 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
519 c->poll_entry = NULL;
525 /* wait for an event on one connection. We poll at least every
526 second to handle timeouts */
528 ret = poll(poll_table, poll_entry - poll_table, delay);
531 cur_time = gettime_ms();
533 if (need_to_start_children) {
534 need_to_start_children = 0;
535 start_children(first_feed);
538 /* now handle the events */
539 for(c = first_http_ctx; c != NULL; c = c_next) {
541 if (handle_connection(c) < 0) {
542 /* close and free the connection */
548 poll_entry = poll_table;
549 /* new HTTP connection request ? */
550 if (poll_entry->revents & POLLIN) {
551 new_connection(server_fd, 0);
554 /* new RTSP connection request ? */
555 if (poll_entry->revents & POLLIN) {
556 new_connection(rtsp_server_fd, 1);
561 /* start waiting for a new HTTP/RTSP request */
562 static void start_wait_request(HTTPContext *c, int is_rtsp)
564 c->buffer_ptr = c->buffer;
565 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
568 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
569 c->state = RTSPSTATE_WAIT_REQUEST;
571 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
572 c->state = HTTPSTATE_WAIT_REQUEST;
576 static void new_connection(int server_fd, int is_rtsp)
578 struct sockaddr_in from_addr;
580 HTTPContext *c = NULL;
582 len = sizeof(from_addr);
583 fd = accept(server_fd, (struct sockaddr *)&from_addr,
587 fcntl(fd, F_SETFL, O_NONBLOCK);
589 /* XXX: should output a warning page when coming
590 close to the connection limit */
591 if (nb_connections >= nb_max_connections)
594 /* add a new connection */
595 c = av_mallocz(sizeof(HTTPContext));
599 c->next = first_http_ctx;
602 c->poll_entry = NULL;
603 c->from_addr = from_addr;
604 c->buffer_size = IOBUFFER_INIT_SIZE;
605 c->buffer = av_malloc(c->buffer_size);
610 start_wait_request(c, is_rtsp);
622 static void close_connection(HTTPContext *c)
624 HTTPContext **cp, *c1;
626 AVFormatContext *ctx;
630 /* remove connection from list */
631 cp = &first_http_ctx;
632 while ((*cp) != NULL) {
641 /* remove connection associated resources */
645 /* close each frame parser */
646 for(i=0;i<c->fmt_in->nb_streams;i++) {
647 st = c->fmt_in->streams[i];
648 if (st->codec.codec) {
649 avcodec_close(&st->codec);
652 av_close_input_file(c->fmt_in);
655 /* free RTP output streams if any */
658 nb_streams = c->stream->nb_streams;
660 for(i=0;i<nb_streams;i++) {
663 av_write_trailer(ctx);
666 h = c->rtp_handles[i];
672 nb_bandwidth -= c->bandwidth;
673 av_freep(&c->pb_buffer);
679 static int handle_connection(HTTPContext *c)
684 case HTTPSTATE_WAIT_REQUEST:
685 case RTSPSTATE_WAIT_REQUEST:
687 if ((c->timeout - cur_time) < 0)
689 if (c->poll_entry->revents & (POLLERR | POLLHUP))
692 /* no need to read if no events */
693 if (!(c->poll_entry->revents & POLLIN))
696 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
698 if (errno != EAGAIN && errno != EINTR)
700 } else if (len == 0) {
703 /* search for end of request. XXX: not fully correct since garbage could come after the end */
705 c->buffer_ptr += len;
707 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
708 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
709 /* request found : parse it and reply */
710 if (c->state == HTTPSTATE_WAIT_REQUEST) {
711 ret = http_parse_request(c);
713 ret = rtsp_parse_request(c);
717 } else if (ptr >= c->buffer_end) {
718 /* request too long: cannot do anything */
724 case HTTPSTATE_SEND_HEADER:
725 if (c->poll_entry->revents & (POLLERR | POLLHUP))
728 /* no need to write if no events */
729 if (!(c->poll_entry->revents & POLLOUT))
731 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
733 if (errno != EAGAIN && errno != EINTR) {
734 /* error : close connection */
735 av_freep(&c->pb_buffer);
739 c->buffer_ptr += len;
741 c->stream->bytes_served += len;
742 c->data_count += len;
743 if (c->buffer_ptr >= c->buffer_end) {
744 av_freep(&c->pb_buffer);
749 /* all the buffer was sent : synchronize to the incoming stream */
750 c->state = HTTPSTATE_SEND_DATA_HEADER;
751 c->buffer_ptr = c->buffer_end = c->buffer;
756 case HTTPSTATE_SEND_DATA:
757 case HTTPSTATE_SEND_DATA_HEADER:
758 case HTTPSTATE_SEND_DATA_TRAILER:
759 /* for packetized output, we consider we can always write (the
760 input streams sets the speed). It may be better to verify
761 that we do not rely too much on the kernel queues */
762 if (!c->is_packetized) {
763 if (c->poll_entry->revents & (POLLERR | POLLHUP))
766 /* no need to read if no events */
767 if (!(c->poll_entry->revents & POLLOUT))
770 if (http_send_data(c) < 0)
773 case HTTPSTATE_RECEIVE_DATA:
774 /* no need to read if no events */
775 if (c->poll_entry->revents & (POLLERR | POLLHUP))
777 if (!(c->poll_entry->revents & POLLIN))
779 if (http_receive_data(c) < 0)
782 case HTTPSTATE_WAIT_FEED:
783 /* no need to read if no events */
784 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
787 /* nothing to do, we'll be waken up by incoming feed packets */
791 /* if the delay expired, we can send new packets */
792 if (compute_send_delay(c) <= 0)
793 c->state = HTTPSTATE_SEND_DATA;
795 case HTTPSTATE_WAIT_SHORT:
796 /* just return back to send data */
797 c->state = HTTPSTATE_SEND_DATA;
800 case RTSPSTATE_SEND_REPLY:
801 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
802 av_freep(&c->pb_buffer);
805 /* no need to write if no events */
806 if (!(c->poll_entry->revents & POLLOUT))
808 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
810 if (errno != EAGAIN && errno != EINTR) {
811 /* error : close connection */
812 av_freep(&c->pb_buffer);
816 c->buffer_ptr += len;
817 c->data_count += len;
818 if (c->buffer_ptr >= c->buffer_end) {
819 /* all the buffer was sent : wait for a new request */
820 av_freep(&c->pb_buffer);
821 start_wait_request(c, 1);
825 case HTTPSTATE_READY:
834 static int extract_rates(char *rates, int ratelen, const char *request)
838 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
839 if (strncasecmp(p, "Pragma:", 7) == 0) {
840 const char *q = p + 7;
842 while (*q && *q != '\n' && isspace(*q))
845 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
851 memset(rates, 0xff, ratelen);
854 while (*q && *q != '\n' && *q != ':')
857 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2) {
861 if (stream_no < ratelen && stream_no >= 0) {
862 rates[stream_no] = rate_no;
865 while (*q && *q != '\n' && !isspace(*q))
882 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
885 int best_bitrate = 100000000;
888 for (i = 0; i < feed->nb_streams; i++) {
889 AVCodecContext *feed_codec = &feed->streams[i]->codec;
891 if (feed_codec->codec_id != codec->codec_id ||
892 feed_codec->sample_rate != codec->sample_rate ||
893 feed_codec->width != codec->width ||
894 feed_codec->height != codec->height) {
898 /* Potential stream */
900 /* We want the fastest stream less than bit_rate, or the slowest
901 * faster than bit_rate
904 if (feed_codec->bit_rate <= bit_rate) {
905 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
906 best_bitrate = feed_codec->bit_rate;
910 if (feed_codec->bit_rate < best_bitrate) {
911 best_bitrate = feed_codec->bit_rate;
920 static int modify_current_stream(HTTPContext *c, char *rates)
923 FFStream *req = c->stream;
924 int action_required = 0;
926 for (i = 0; i < req->nb_streams; i++) {
927 AVCodecContext *codec = &req->streams[i]->codec;
931 c->switch_feed_streams[i] = req->feed_streams[i];
934 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
937 /* Wants off or slow */
938 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
940 /* This doesn't work well when it turns off the only stream! */
941 c->switch_feed_streams[i] = -2;
942 c->feed_streams[i] = -2;
947 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
951 return action_required;
955 static void do_switch_stream(HTTPContext *c, int i)
957 if (c->switch_feed_streams[i] >= 0) {
959 c->feed_streams[i] = c->switch_feed_streams[i];
962 /* Now update the stream */
964 c->switch_feed_streams[i] = -1;
967 /* XXX: factorize in utils.c ? */
968 /* XXX: take care with different space meaning */
969 static void skip_spaces(const char **pp)
973 while (*p == ' ' || *p == '\t')
978 static void get_word(char *buf, int buf_size, const char **pp)
986 while (!isspace(*p) && *p != '\0') {
987 if ((q - buf) < buf_size - 1)
996 static int validate_acl(FFStream *stream, HTTPContext *c)
998 enum IPAddressAction last_action = IP_DENY;
1000 struct in_addr *src = &c->from_addr.sin_addr;
1002 for (acl = stream->acl; acl; acl = acl->next) {
1003 if (src->s_addr >= acl->first.s_addr && src->s_addr <= acl->last.s_addr) {
1004 return (acl->action == IP_ALLOW) ? 1 : 0;
1006 last_action = acl->action;
1009 /* Nothing matched, so return not the last action */
1010 return (last_action == IP_DENY) ? 1 : 0;
1013 /* compute the real filename of a file by matching it without its
1014 extensions to all the stream filenames */
1015 static void compute_real_filename(char *filename, int max_size)
1022 /* compute filename by matching without the file extensions */
1023 pstrcpy(file1, sizeof(file1), filename);
1024 p = strrchr(file1, '.');
1027 for(stream = first_stream; stream != NULL; stream = stream->next) {
1028 pstrcpy(file2, sizeof(file2), stream->filename);
1029 p = strrchr(file2, '.');
1032 if (!strcmp(file1, file2)) {
1033 pstrcpy(filename, max_size, stream->filename);
1048 /* parse http request and prepare header */
1049 static int http_parse_request(HTTPContext *c)
1053 enum RedirType redir_type;
1055 char info[1024], *filename;
1059 const char *mime_type;
1063 char *useragent = 0;
1066 get_word(cmd, sizeof(cmd), (const char **)&p);
1067 pstrcpy(c->method, sizeof(c->method), cmd);
1069 if (!strcmp(cmd, "GET"))
1071 else if (!strcmp(cmd, "POST"))
1076 get_word(url, sizeof(url), (const char **)&p);
1077 pstrcpy(c->url, sizeof(c->url), url);
1079 get_word(protocol, sizeof(protocol), (const char **)&p);
1080 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1083 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
1085 /* find the filename and the optional info string in the request */
1092 pstrcpy(info, sizeof(info), p);
1098 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1099 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1101 if (*useragent && *useragent != '\n' && isspace(*useragent))
1105 p = strchr(p, '\n');
1112 redir_type = REDIR_NONE;
1113 if (match_ext(filename, "asx")) {
1114 redir_type = REDIR_ASX;
1115 filename[strlen(filename)-1] = 'f';
1116 } else if (match_ext(filename, ".asf") &&
1117 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1118 /* if this isn't WMP or lookalike, return the redirector file */
1119 redir_type = REDIR_ASF;
1120 } else if (match_ext(filename, "rpm,ram")) {
1121 redir_type = REDIR_RAM;
1122 strcpy(filename + strlen(filename)-2, "m");
1123 } else if (match_ext(filename, "rtsp")) {
1124 redir_type = REDIR_RTSP;
1125 compute_real_filename(filename, sizeof(url) - 1);
1126 } else if (match_ext(filename, "sdp")) {
1127 redir_type = REDIR_SDP;
1128 printf("before %s\n", filename);
1129 compute_real_filename(filename, sizeof(url) - 1);
1130 printf("after %s\n", filename);
1133 stream = first_stream;
1134 while (stream != NULL) {
1135 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1137 stream = stream->next;
1139 if (stream == NULL) {
1140 sprintf(msg, "File '%s' not found", url);
1145 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1146 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1148 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1149 c->http_error = 301;
1151 q += sprintf(q, "HTTP/1.0 301 Moved\r\n");
1152 q += sprintf(q, "Location: %s\r\n", stream->feed_filename);
1153 q += sprintf(q, "Content-type: text/html\r\n");
1154 q += sprintf(q, "\r\n");
1155 q += sprintf(q, "<html><head><title>Moved</title></head><body>\r\n");
1156 q += sprintf(q, "You should be <a href=\"%s\">redirected</a>.\r\n", stream->feed_filename);
1157 q += sprintf(q, "</body></html>\r\n");
1159 /* prepare output buffer */
1160 c->buffer_ptr = c->buffer;
1162 c->state = HTTPSTATE_SEND_HEADER;
1166 /* If this is WMP, get the rate information */
1167 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1168 if (modify_current_stream(c, ratebuf)) {
1169 for (i = 0; i < sizeof(c->feed_streams) / sizeof(c->feed_streams[0]); i++) {
1170 if (c->switch_feed_streams[i] >= 0)
1171 do_switch_stream(c, i);
1176 if (post == 0 && stream->stream_type == STREAM_TYPE_LIVE) {
1177 /* See if we meet the bandwidth requirements */
1178 for(i=0;i<stream->nb_streams;i++) {
1179 AVStream *st = stream->streams[i];
1180 switch(st->codec.codec_type) {
1181 case CODEC_TYPE_AUDIO:
1182 c->bandwidth += st->codec.bit_rate;
1184 case CODEC_TYPE_VIDEO:
1185 c->bandwidth += st->codec.bit_rate;
1193 c->bandwidth /= 1000;
1194 nb_bandwidth += c->bandwidth;
1196 if (post == 0 && nb_max_bandwidth < nb_bandwidth) {
1197 c->http_error = 200;
1199 q += sprintf(q, "HTTP/1.0 200 Server too busy\r\n");
1200 q += sprintf(q, "Content-type: text/html\r\n");
1201 q += sprintf(q, "\r\n");
1202 q += sprintf(q, "<html><head><title>Too busy</title></head><body>\r\n");
1203 q += sprintf(q, "The server is too busy to serve your request at this time.<p>\r\n");
1204 q += sprintf(q, "The bandwidth being served (including your stream) is %dkbit/sec, and this exceeds the limit of %dkbit/sec\r\n",
1205 nb_bandwidth, nb_max_bandwidth);
1206 q += sprintf(q, "</body></html>\r\n");
1208 /* prepare output buffer */
1209 c->buffer_ptr = c->buffer;
1211 c->state = HTTPSTATE_SEND_HEADER;
1215 if (redir_type != REDIR_NONE) {
1218 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1219 if (strncasecmp(p, "Host:", 5) == 0) {
1223 p = strchr(p, '\n');
1234 while (isspace(*hostinfo))
1237 eoh = strchr(hostinfo, '\n');
1239 if (eoh[-1] == '\r')
1242 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1243 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1244 hostbuf[eoh - hostinfo] = 0;
1246 c->http_error = 200;
1248 switch(redir_type) {
1250 q += sprintf(q, "HTTP/1.0 200 ASX Follows\r\n");
1251 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1252 q += sprintf(q, "\r\n");
1253 q += sprintf(q, "<ASX Version=\"3\">\r\n");
1254 q += sprintf(q, "<!-- Autogenerated by ffserver -->\r\n");
1255 q += sprintf(q, "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n",
1256 hostbuf, filename, info);
1257 q += sprintf(q, "</ASX>\r\n");
1260 q += sprintf(q, "HTTP/1.0 200 RAM Follows\r\n");
1261 q += sprintf(q, "Content-type: audio/x-pn-realaudio\r\n");
1262 q += sprintf(q, "\r\n");
1263 q += sprintf(q, "# Autogenerated by ffserver\r\n");
1264 q += sprintf(q, "http://%s/%s%s\r\n",
1265 hostbuf, filename, info);
1268 q += sprintf(q, "HTTP/1.0 200 ASF Redirect follows\r\n");
1269 q += sprintf(q, "Content-type: video/x-ms-asf\r\n");
1270 q += sprintf(q, "\r\n");
1271 q += sprintf(q, "[Reference]\r\n");
1272 q += sprintf(q, "Ref1=http://%s/%s%s\r\n",
1273 hostbuf, filename, info);
1277 char hostname[256], *p;
1278 /* extract only hostname */
1279 pstrcpy(hostname, sizeof(hostname), hostbuf);
1280 p = strrchr(hostname, ':');
1283 q += sprintf(q, "HTTP/1.0 200 RTSP Redirect follows\r\n");
1284 /* XXX: incorrect mime type ? */
1285 q += sprintf(q, "Content-type: application/x-rtsp\r\n");
1286 q += sprintf(q, "\r\n");
1287 q += sprintf(q, "rtsp://%s:%d/%s\r\n",
1288 hostname, ntohs(my_rtsp_addr.sin_port),
1295 int sdp_data_size, len;
1296 struct sockaddr_in my_addr;
1298 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1299 q += sprintf(q, "Content-type: application/sdp\r\n");
1300 q += sprintf(q, "\r\n");
1302 len = sizeof(my_addr);
1303 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1305 /* XXX: should use a dynamic buffer */
1306 sdp_data_size = prepare_sdp_description(stream,
1309 if (sdp_data_size > 0) {
1310 memcpy(q, sdp_data, sdp_data_size);
1322 /* prepare output buffer */
1323 c->buffer_ptr = c->buffer;
1325 c->state = HTTPSTATE_SEND_HEADER;
1331 sprintf(msg, "ASX/RAM file not handled");
1335 stream->conns_served++;
1337 /* XXX: add there authenticate and IP match */
1340 /* if post, it means a feed is being sent */
1341 if (!stream->is_feed) {
1342 /* However it might be a status report from WMP! Lets log the data
1343 * as it might come in handy one day
1348 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1349 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1353 if (strncasecmp(p, "Pragma: client-id=", 18) == 0) {
1354 client_id = strtol(p + 18, 0, 10);
1356 p = strchr(p, '\n');
1364 char *eol = strchr(logline, '\n');
1369 if (eol[-1] == '\r')
1371 http_log("%.*s\n", eol - logline, logline);
1372 c->suppress_log = 1;
1377 http_log("\nGot request:\n%s\n", c->buffer);
1380 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1383 /* Now we have to find the client_id */
1384 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1385 if (wmpc->wmp_client_id == client_id)
1390 if (modify_current_stream(wmpc, ratebuf)) {
1391 wmpc->switch_pending = 1;
1396 sprintf(msg, "POST command not handled");
1399 if (http_start_receive_data(c) < 0) {
1400 sprintf(msg, "could not open feed");
1404 c->state = HTTPSTATE_RECEIVE_DATA;
1409 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0) {
1410 http_log("\nGot request:\n%s\n", c->buffer);
1414 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1417 /* open input stream */
1418 if (open_input_stream(c, info) < 0) {
1419 sprintf(msg, "Input stream corresponding to '%s' not found", url);
1423 /* prepare http header */
1425 q += sprintf(q, "HTTP/1.0 200 OK\r\n");
1426 mime_type = c->stream->fmt->mime_type;
1428 mime_type = "application/x-octet_stream";
1429 q += sprintf(q, "Pragma: no-cache\r\n");
1431 /* for asf, we need extra headers */
1432 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1433 /* Need to allocate a client id */
1435 c->wmp_client_id = random() & 0x7fffffff;
1437 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);
1438 mime_type = "application/octet-stream";
1440 q += sprintf(q, "Content-Type: %s\r\n", mime_type);
1441 q += sprintf(q, "\r\n");
1443 /* prepare output buffer */
1445 c->buffer_ptr = c->buffer;
1447 c->state = HTTPSTATE_SEND_HEADER;
1450 c->http_error = 404;
1452 q += sprintf(q, "HTTP/1.0 404 Not Found\r\n");
1453 q += sprintf(q, "Content-type: %s\r\n", "text/html");
1454 q += sprintf(q, "\r\n");
1455 q += sprintf(q, "<HTML>\n");
1456 q += sprintf(q, "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n");
1457 q += sprintf(q, "<BODY>%s</BODY>\n", msg);
1458 q += sprintf(q, "</HTML>\n");
1460 /* prepare output buffer */
1461 c->buffer_ptr = c->buffer;
1463 c->state = HTTPSTATE_SEND_HEADER;
1467 c->http_error = 200; /* horrible : we use this value to avoid
1468 going to the send data state */
1469 c->state = HTTPSTATE_SEND_HEADER;
1473 static void fmt_bytecount(ByteIOContext *pb, INT64 count)
1475 static const char *suffix = " kMGTP";
1478 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++) {
1481 url_fprintf(pb, "%lld%c", count, *s);
1484 static void compute_stats(HTTPContext *c)
1491 ByteIOContext pb1, *pb = &pb1;
1493 if (url_open_dyn_buf(pb) < 0) {
1494 /* XXX: return an error ? */
1495 c->buffer_ptr = c->buffer;
1496 c->buffer_end = c->buffer;
1500 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1501 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1502 url_fprintf(pb, "Pragma: no-cache\r\n");
1503 url_fprintf(pb, "\r\n");
1505 url_fprintf(pb, "<HEAD><TITLE>FFServer Status</TITLE>\n");
1506 if (c->stream->feed_filename) {
1507 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1509 url_fprintf(pb, "</HEAD>\n<BODY>");
1510 url_fprintf(pb, "<H1>FFServer Status</H1>\n");
1512 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1513 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1514 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");
1515 stream = first_stream;
1516 while (stream != NULL) {
1517 char sfilename[1024];
1520 if (stream->feed != stream) {
1521 pstrcpy(sfilename, sizeof(sfilename) - 10, stream->filename);
1522 eosf = sfilename + strlen(sfilename);
1523 if (eosf - sfilename >= 4) {
1524 if (strcmp(eosf - 4, ".asf") == 0) {
1525 strcpy(eosf - 4, ".asx");
1526 } else if (strcmp(eosf - 3, ".rm") == 0) {
1527 strcpy(eosf - 3, ".ram");
1528 } else if (stream->fmt == &rtp_mux) {
1529 /* generate a sample RTSP director if
1530 unicast. Generate an SDP redirector if
1532 eosf = strrchr(sfilename, '.');
1534 eosf = sfilename + strlen(sfilename);
1535 if (stream->is_multicast)
1536 strcpy(eosf, ".sdp");
1538 strcpy(eosf, ".rtsp");
1542 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1543 sfilename, stream->filename);
1544 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1545 stream->conns_served);
1546 fmt_bytecount(pb, stream->bytes_served);
1547 switch(stream->stream_type) {
1548 case STREAM_TYPE_LIVE:
1550 int audio_bit_rate = 0;
1551 int video_bit_rate = 0;
1552 const char *audio_codec_name = "";
1553 const char *video_codec_name = "";
1554 const char *audio_codec_name_extra = "";
1555 const char *video_codec_name_extra = "";
1557 for(i=0;i<stream->nb_streams;i++) {
1558 AVStream *st = stream->streams[i];
1559 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1560 switch(st->codec.codec_type) {
1561 case CODEC_TYPE_AUDIO:
1562 audio_bit_rate += st->codec.bit_rate;
1564 if (*audio_codec_name)
1565 audio_codec_name_extra = "...";
1566 audio_codec_name = codec->name;
1569 case CODEC_TYPE_VIDEO:
1570 video_bit_rate += st->codec.bit_rate;
1572 if (*video_codec_name)
1573 video_codec_name_extra = "...";
1574 video_codec_name = codec->name;
1581 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",
1583 (audio_bit_rate + video_bit_rate) / 1000,
1584 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1585 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1587 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1589 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1591 url_fprintf(pb, "\n");
1595 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1599 stream = stream->next;
1601 url_fprintf(pb, "</TABLE>\n");
1603 stream = first_stream;
1604 while (stream != NULL) {
1605 if (stream->feed == stream) {
1606 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1608 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1610 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1615 /* This is somewhat linux specific I guess */
1616 snprintf(ps_cmd, sizeof(ps_cmd),
1617 "ps -o \"%%cpu,cputime\" --no-headers %d",
1620 pid_stat = popen(ps_cmd, "r");
1625 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1627 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1635 url_fprintf(pb, "<p>");
1637 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");
1639 for (i = 0; i < stream->nb_streams; i++) {
1640 AVStream *st = stream->streams[i];
1641 AVCodec *codec = avcodec_find_encoder(st->codec.codec_id);
1642 char *type = "unknown";
1643 char parameters[64];
1647 switch(st->codec.codec_type) {
1648 case CODEC_TYPE_AUDIO:
1651 case CODEC_TYPE_VIDEO:
1653 sprintf(parameters, "%dx%d, q=%d-%d, fps=%d", st->codec.width, st->codec.height,
1654 st->codec.qmin, st->codec.qmax, st->codec.frame_rate / FRAME_RATE_BASE);
1659 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1660 i, type, st->codec.bit_rate/1000, codec ? codec->name : "", parameters);
1662 url_fprintf(pb, "</table>\n");
1665 stream = stream->next;
1671 AVCodecContext *enc;
1675 stream = first_feed;
1676 while (stream != NULL) {
1677 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1678 url_fprintf(pb, "<TABLE>\n");
1679 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1680 for(i=0;i<stream->nb_streams;i++) {
1681 AVStream *st = stream->streams[i];
1682 FeedData *fdata = st->priv_data;
1685 avcodec_string(buf, sizeof(buf), enc);
1686 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1687 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1688 avg /= enc->frame_size;
1689 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %Ld <TD> %0.1f\n",
1690 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1692 url_fprintf(pb, "</TABLE>\n");
1693 stream = stream->next_feed;
1698 /* connection status */
1699 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1701 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1702 nb_connections, nb_max_connections);
1704 url_fprintf(pb, "Bandwidth in use: %dk / %dk<BR>\n",
1705 nb_bandwidth, nb_max_bandwidth);
1707 url_fprintf(pb, "<TABLE>\n");
1708 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");
1709 c1 = first_http_ctx;
1711 while (c1 != NULL) {
1717 for (j = 0; j < c1->stream->nb_streams; j++) {
1718 if (!c1->stream->feed) {
1719 bitrate += c1->stream->streams[j]->codec.bit_rate;
1721 if (c1->feed_streams[j] >= 0) {
1722 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec.bit_rate;
1729 p = inet_ntoa(c1->from_addr.sin_addr);
1730 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1732 c1->stream ? c1->stream->filename : "",
1733 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1736 http_state[c1->state]);
1737 fmt_bytecount(pb, bitrate);
1738 url_fprintf(pb, "<td align=right>");
1739 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1740 url_fprintf(pb, "<td align=right>");
1741 fmt_bytecount(pb, c1->data_count);
1742 url_fprintf(pb, "\n");
1745 url_fprintf(pb, "</TABLE>\n");
1750 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1751 url_fprintf(pb, "</BODY>\n</HTML>\n");
1753 len = url_close_dyn_buf(pb, &c->pb_buffer);
1754 c->buffer_ptr = c->pb_buffer;
1755 c->buffer_end = c->pb_buffer + len;
1758 /* check if the parser needs to be opened for stream i */
1759 static void open_parser(AVFormatContext *s, int i)
1761 AVStream *st = s->streams[i];
1764 if (!st->codec.codec) {
1765 codec = avcodec_find_decoder(st->codec.codec_id);
1766 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1767 st->codec.parse_only = 1;
1768 if (avcodec_open(&st->codec, codec) < 0) {
1769 st->codec.parse_only = 0;
1775 static int open_input_stream(HTTPContext *c, const char *info)
1778 char input_filename[1024];
1783 /* find file name */
1784 if (c->stream->feed) {
1785 strcpy(input_filename, c->stream->feed->feed_filename);
1786 buf_size = FFM_PACKET_SIZE;
1787 /* compute position (absolute time) */
1788 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1789 stream_pos = parse_date(buf, 0);
1790 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1791 int prebuffer = strtol(buf, 0, 10);
1792 stream_pos = av_gettime() - prebuffer * (INT64)1000000;
1794 stream_pos = av_gettime() - c->stream->prebuffer * (INT64)1000;
1797 strcpy(input_filename, c->stream->feed_filename);
1799 /* compute position (relative time) */
1800 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1801 stream_pos = parse_date(buf, 1);
1806 if (input_filename[0] == '\0')
1810 { time_t when = stream_pos / 1000000;
1811 http_log("Stream pos = %lld, time=%s", stream_pos, ctime(&when));
1816 if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0) {
1817 http_log("%s not found", input_filename);
1822 /* open each parser */
1823 for(i=0;i<s->nb_streams;i++)
1826 /* choose stream as clock source (we favorize video stream if
1827 present) for packet sending */
1828 c->pts_stream_index = 0;
1829 for(i=0;i<c->stream->nb_streams;i++) {
1830 if (c->pts_stream_index == 0 &&
1831 c->stream->streams[i]->codec.codec_type == CODEC_TYPE_VIDEO) {
1832 c->pts_stream_index = i;
1836 if (c->fmt_in->iformat->read_seek) {
1837 c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
1839 /* set the start time (needed for maxtime and RTP packet timing) */
1840 c->start_time = cur_time;
1841 c->first_pts = AV_NOPTS_VALUE;
1845 /* currently desactivated because the new PTS handling is not
1847 //#define AV_READ_FRAME
1848 #ifdef AV_READ_FRAME
1850 /* XXX: generalize that in ffmpeg for picture/audio/data. Currently
1851 the return packet MUST NOT be freed */
1852 int av_read_frame(AVFormatContext *s, AVPacket *pkt)
1855 int len, ret, old_nb_streams, i;
1857 /* see if remaining frames must be parsed */
1859 if (s->cur_len > 0) {
1860 st = s->streams[s->cur_pkt.stream_index];
1861 len = avcodec_parse_frame(&st->codec, &pkt->data, &pkt->size,
1862 s->cur_ptr, s->cur_len);
1864 /* error: get next packet */
1870 /* init pts counter if not done */
1871 if (st->pts.den == 0) {
1872 switch(st->codec.codec_type) {
1873 case CODEC_TYPE_AUDIO:
1874 st->pts_incr = (INT64)s->pts_den;
1875 av_frac_init(&st->pts, st->pts.val, 0,
1876 (INT64)s->pts_num * st->codec.sample_rate);
1878 case CODEC_TYPE_VIDEO:
1879 st->pts_incr = (INT64)s->pts_den * FRAME_RATE_BASE;
1880 av_frac_init(&st->pts, st->pts.val, 0,
1881 (INT64)s->pts_num * st->codec.frame_rate);
1888 /* a frame was read: return it */
1889 pkt->pts = st->pts.val;
1891 printf("add pts=%Lx num=%Lx den=%Lx incr=%Lx\n",
1892 st->pts.val, st->pts.num, st->pts.den, st->pts_incr);
1894 switch(st->codec.codec_type) {
1895 case CODEC_TYPE_AUDIO:
1896 av_frac_add(&st->pts, st->pts_incr * st->codec.frame_size);
1898 case CODEC_TYPE_VIDEO:
1899 av_frac_add(&st->pts, st->pts_incr);
1904 pkt->stream_index = s->cur_pkt.stream_index;
1905 /* we use the codec indication because it is
1906 more accurate than the demux flags */
1908 if (st->codec.key_frame)
1909 pkt->flags |= PKT_FLAG_KEY;
1914 /* free previous packet */
1915 av_free_packet(&s->cur_pkt);
1917 old_nb_streams = s->nb_streams;
1918 ret = av_read_packet(s, &s->cur_pkt);
1921 /* open parsers for each new streams */
1922 for(i = old_nb_streams; i < s->nb_streams; i++)
1924 st = s->streams[s->cur_pkt.stream_index];
1926 /* update current pts (XXX: dts handling) from packet, or
1927 use current pts if none given */
1928 if (s->cur_pkt.pts != AV_NOPTS_VALUE) {
1929 av_frac_set(&st->pts, s->cur_pkt.pts);
1931 s->cur_pkt.pts = st->pts.val;
1933 if (!st->codec.codec) {
1934 /* no codec opened: just return the raw packet */
1937 /* no codec opened: just update the pts by considering we
1938 have one frame and free the packet */
1939 if (st->pts.den == 0) {
1940 switch(st->codec.codec_type) {
1941 case CODEC_TYPE_AUDIO:
1942 st->pts_incr = (INT64)s->pts_den * st->codec.frame_size;
1943 av_frac_init(&st->pts, st->pts.val, 0,
1944 (INT64)s->pts_num * st->codec.sample_rate);
1946 case CODEC_TYPE_VIDEO:
1947 st->pts_incr = (INT64)s->pts_den * FRAME_RATE_BASE;
1948 av_frac_init(&st->pts, st->pts.val, 0,
1949 (INT64)s->pts_num * st->codec.frame_rate);
1955 av_frac_add(&st->pts, st->pts_incr);
1958 s->cur_ptr = s->cur_pkt.data;
1959 s->cur_len = s->cur_pkt.size;
1965 static int compute_send_delay(HTTPContext *c)
1967 INT64 cur_pts, delta_pts, next_pts;
1970 /* compute current pts value from system time */
1971 cur_pts = ((INT64)(cur_time - c->start_time) * c->fmt_in->pts_den) /
1972 (c->fmt_in->pts_num * 1000LL);
1973 /* compute the delta from the stream we choose as
1974 main clock (we do that to avoid using explicit
1975 buffers to do exact packet reordering for each
1977 /* XXX: really need to fix the number of streams */
1978 if (c->pts_stream_index >= c->fmt_in->nb_streams)
1981 next_pts = c->fmt_in->streams[c->pts_stream_index]->pts.val;
1982 delta_pts = next_pts - cur_pts;
1983 if (delta_pts <= 0) {
1986 delay1 = (delta_pts * 1000 * c->fmt_in->pts_num) / c->fmt_in->pts_den;
1992 /* just fall backs */
1993 int av_read_frame(AVFormatContext *s, AVPacket *pkt)
1995 return av_read_packet(s, pkt);
1998 static int compute_send_delay(HTTPContext *c)
2000 int datarate = 8 * get_longterm_datarate(&c->datarate, c->data_count);
2002 if (datarate > c->bandwidth * 2000) {
2010 static int http_prepare_data(HTTPContext *c)
2013 AVFormatContext *ctx;
2016 case HTTPSTATE_SEND_DATA_HEADER:
2017 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2018 pstrcpy(c->fmt_ctx.author, sizeof(c->fmt_ctx.author),
2020 pstrcpy(c->fmt_ctx.comment, sizeof(c->fmt_ctx.comment),
2021 c->stream->comment);
2022 pstrcpy(c->fmt_ctx.copyright, sizeof(c->fmt_ctx.copyright),
2023 c->stream->copyright);
2024 pstrcpy(c->fmt_ctx.title, sizeof(c->fmt_ctx.title),
2027 /* open output stream by using specified codecs */
2028 c->fmt_ctx.oformat = c->stream->fmt;
2029 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2030 for(i=0;i<c->fmt_ctx.nb_streams;i++) {
2032 st = av_mallocz(sizeof(AVStream));
2033 c->fmt_ctx.streams[i] = st;
2034 /* if file or feed, then just take streams from FFStream struct */
2035 if (!c->stream->feed ||
2036 c->stream->feed == c->stream)
2037 memcpy(st, c->stream->streams[i], sizeof(AVStream));
2039 memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]],
2041 st->codec.frame_number = 0; /* XXX: should be done in
2042 AVStream, not in codec */
2044 c->got_key_frame = 0;
2046 /* prepare header and save header data in a stream */
2047 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2048 /* XXX: potential leak */
2051 c->fmt_ctx.pb.is_streamed = 1;
2053 av_write_header(&c->fmt_ctx);
2055 len = url_close_dyn_buf(&c->fmt_ctx.pb, &c->pb_buffer);
2056 c->buffer_ptr = c->pb_buffer;
2057 c->buffer_end = c->pb_buffer + len;
2059 c->state = HTTPSTATE_SEND_DATA;
2060 c->last_packet_sent = 0;
2062 case HTTPSTATE_SEND_DATA:
2063 /* find a new packet */
2067 /* read a packet from the input stream */
2068 if (c->stream->feed) {
2069 ffm_set_write_index(c->fmt_in,
2070 c->stream->feed->feed_write_index,
2071 c->stream->feed->feed_size);
2074 if (c->stream->max_time &&
2075 c->stream->max_time + c->start_time - cur_time < 0) {
2076 /* We have timed out */
2077 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2079 if (1 || c->is_packetized) {
2080 if (compute_send_delay(c) > 0) {
2081 c->state = HTTPSTATE_WAIT;
2082 return 1; /* state changed */
2085 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2086 if (c->stream->feed && c->stream->feed->feed_opened) {
2087 /* if coming from feed, it means we reached the end of the
2088 ffm file, so must wait for more data */
2089 c->state = HTTPSTATE_WAIT_FEED;
2090 return 1; /* state changed */
2092 /* must send trailer now because eof or error */
2093 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2096 /* update first pts if needed */
2097 if (c->first_pts == AV_NOPTS_VALUE)
2098 c->first_pts = pkt.pts;
2100 /* send it to the appropriate stream */
2101 if (c->stream->feed) {
2102 /* if coming from a feed, select the right stream */
2103 if (c->switch_pending) {
2104 c->switch_pending = 0;
2105 for(i=0;i<c->stream->nb_streams;i++) {
2106 if (c->switch_feed_streams[i] == pkt.stream_index) {
2107 if (pkt.flags & PKT_FLAG_KEY) {
2108 do_switch_stream(c, i);
2111 if (c->switch_feed_streams[i] >= 0) {
2112 c->switch_pending = 1;
2116 for(i=0;i<c->stream->nb_streams;i++) {
2117 if (c->feed_streams[i] == pkt.stream_index) {
2118 pkt.stream_index = i;
2119 if (pkt.flags & PKT_FLAG_KEY) {
2120 c->got_key_frame |= 1 << i;
2122 /* See if we have all the key frames, then
2123 * we start to send. This logic is not quite
2124 * right, but it works for the case of a
2125 * single video stream with one or more
2126 * audio streams (for which every frame is
2127 * typically a key frame).
2129 if (!c->stream->send_on_key ||
2130 ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
2136 AVCodecContext *codec;
2139 /* specific handling for RTP: we use several
2140 output stream (one for each RTP
2141 connection). XXX: need more abstract handling */
2142 if (c->is_packetized) {
2143 c->packet_stream_index = pkt.stream_index;
2144 ctx = c->rtp_ctx[c->packet_stream_index];
2145 codec = &ctx->streams[0]->codec;
2149 codec = &ctx->streams[pkt.stream_index]->codec;
2152 codec->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
2155 if (codec->codec_type == CODEC_TYPE_AUDIO) {
2156 codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
2157 /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
2161 if (c->is_packetized) {
2162 ret = url_open_dyn_packet_buf(&ctx->pb,
2163 url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]));
2164 c->packet_byte_count = 0;
2165 c->packet_start_time_us = av_gettime();
2167 ret = url_open_dyn_buf(&ctx->pb);
2170 /* XXX: potential leak */
2173 if (av_write_frame(ctx, pkt.stream_index, pkt.data, pkt.size)) {
2174 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2177 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2178 c->buffer_ptr = c->pb_buffer;
2179 c->buffer_end = c->pb_buffer + len;
2181 codec->frame_number++;
2183 #ifndef AV_READ_FRAME
2184 av_free_packet(&pkt);
2191 case HTTPSTATE_SEND_DATA_TRAILER:
2192 /* last packet test ? */
2193 if (c->last_packet_sent || c->is_packetized)
2196 /* prepare header */
2197 if (url_open_dyn_buf(&ctx->pb) < 0) {
2198 /* XXX: potential leak */
2201 av_write_trailer(ctx);
2202 len = url_close_dyn_buf(&ctx->pb, &c->pb_buffer);
2203 c->buffer_ptr = c->pb_buffer;
2204 c->buffer_end = c->pb_buffer + len;
2206 c->last_packet_sent = 1;
2213 #define SHORT_TERM_BANDWIDTH 8000000
2215 /* should convert the format at the same time */
2216 static int http_send_data(HTTPContext *c)
2220 while (c->buffer_ptr >= c->buffer_end) {
2221 av_freep(&c->pb_buffer);
2222 ret = http_prepare_data(c);
2225 else if (ret == 0) {
2228 /* state change requested */
2233 if (c->buffer_ptr < c->buffer_end) {
2234 if (c->is_packetized) {
2235 /* RTP/UDP data output */
2236 len = c->buffer_end - c->buffer_ptr;
2238 /* fail safe - should never happen */
2240 c->buffer_ptr = c->buffer_end;
2243 len = (c->buffer_ptr[0] << 24) |
2244 (c->buffer_ptr[1] << 16) |
2245 (c->buffer_ptr[2] << 8) |
2247 if (len > (c->buffer_end - c->buffer_ptr))
2250 /* short term bandwidth limitation */
2251 dt = av_gettime() - c->packet_start_time_us;
2255 if ((c->packet_byte_count + len) * (INT64)1000000 >=
2256 (SHORT_TERM_BANDWIDTH / 8) * (INT64)dt) {
2257 /* bandwidth overflow : wait at most one tick and retry */
2258 c->state = HTTPSTATE_WAIT_SHORT;
2263 url_write(c->rtp_handles[c->packet_stream_index],
2264 c->buffer_ptr, len);
2265 c->buffer_ptr += len;
2266 c->packet_byte_count += len;
2268 /* TCP data output */
2269 len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2271 if (errno != EAGAIN && errno != EINTR) {
2272 /* error : close connection */
2278 c->buffer_ptr += len;
2281 c->data_count += len;
2282 update_datarate(&c->datarate, c->data_count);
2284 c->stream->bytes_served += len;
2289 static int http_start_receive_data(HTTPContext *c)
2293 if (c->stream->feed_opened)
2297 fd = open(c->stream->feed_filename, O_RDWR);
2302 c->stream->feed_write_index = ffm_read_write_index(fd);
2303 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2304 lseek(fd, 0, SEEK_SET);
2306 /* init buffer input */
2307 c->buffer_ptr = c->buffer;
2308 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2309 c->stream->feed_opened = 1;
2313 static int http_receive_data(HTTPContext *c)
2317 if (c->buffer_end > c->buffer_ptr) {
2320 len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
2322 if (errno != EAGAIN && errno != EINTR) {
2323 /* error : close connection */
2326 } else if (len == 0) {
2327 /* end of connection : close it */
2330 c->buffer_ptr += len;
2331 c->data_count += len;
2332 update_datarate(&c->datarate, c->data_count);
2336 if (c->buffer_ptr >= c->buffer_end) {
2337 FFStream *feed = c->stream;
2338 /* a packet has been received : write it in the store, except
2340 if (c->data_count > FFM_PACKET_SIZE) {
2342 // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
2343 /* XXX: use llseek or url_seek */
2344 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2345 write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
2347 feed->feed_write_index += FFM_PACKET_SIZE;
2348 /* update file size */
2349 if (feed->feed_write_index > c->stream->feed_size)
2350 feed->feed_size = feed->feed_write_index;
2352 /* handle wrap around if max file size reached */
2353 if (feed->feed_write_index >= c->stream->feed_max_size)
2354 feed->feed_write_index = FFM_PACKET_SIZE;
2357 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2359 /* wake up any waiting connections */
2360 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2361 if (c1->state == HTTPSTATE_WAIT_FEED &&
2362 c1->stream->feed == c->stream->feed) {
2363 c1->state = HTTPSTATE_SEND_DATA;
2367 /* We have a header in our hands that contains useful data */
2369 AVInputFormat *fmt_in;
2370 ByteIOContext *pb = &s.pb;
2373 memset(&s, 0, sizeof(s));
2375 url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2376 pb->buf_end = c->buffer_end; /* ?? */
2377 pb->is_streamed = 1;
2379 /* use feed output format name to find corresponding input format */
2380 fmt_in = av_find_input_format(feed->fmt->name);
2384 s.priv_data = av_mallocz(fmt_in->priv_data_size);
2388 if (fmt_in->read_header(&s, 0) < 0) {
2389 av_freep(&s.priv_data);
2393 /* Now we have the actual streams */
2394 if (s.nb_streams != feed->nb_streams) {
2395 av_freep(&s.priv_data);
2398 for (i = 0; i < s.nb_streams; i++) {
2399 memcpy(&feed->streams[i]->codec,
2400 &s.streams[i]->codec, sizeof(AVCodecContext));
2402 av_freep(&s.priv_data);
2404 c->buffer_ptr = c->buffer;
2409 c->stream->feed_opened = 0;
2414 /********************************************************************/
2417 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2424 switch(error_number) {
2425 #define DEF(n, c, s) case c: str = s; break;
2426 #include "rtspcodes.h"
2429 str = "Unknown Error";
2433 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2434 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2436 /* output GMT time */
2440 p = buf2 + strlen(p) - 1;
2443 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2446 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2448 rtsp_reply_header(c, error_number);
2449 url_fprintf(c->pb, "\r\n");
2452 static int rtsp_parse_request(HTTPContext *c)
2454 const char *p, *p1, *p2;
2461 RTSPHeader header1, *header = &header1;
2463 c->buffer_ptr[0] = '\0';
2466 get_word(cmd, sizeof(cmd), &p);
2467 get_word(url, sizeof(url), &p);
2468 get_word(protocol, sizeof(protocol), &p);
2470 pstrcpy(c->method, sizeof(c->method), cmd);
2471 pstrcpy(c->url, sizeof(c->url), url);
2472 pstrcpy(c->protocol, sizeof(c->protocol), protocol);
2475 if (url_open_dyn_buf(c->pb) < 0) {
2476 /* XXX: cannot do more */
2477 c->pb = NULL; /* safety */
2481 /* check version name */
2482 if (strcmp(protocol, "RTSP/1.0") != 0) {
2483 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2487 /* parse each header line */
2488 memset(header, 0, sizeof(RTSPHeader));
2489 /* skip to next line */
2490 while (*p != '\n' && *p != '\0')
2494 while (*p != '\0') {
2495 p1 = strchr(p, '\n');
2499 if (p2 > p && p2[-1] == '\r')
2501 /* skip empty line */
2505 if (len > sizeof(line) - 1)
2506 len = sizeof(line) - 1;
2507 memcpy(line, p, len);
2509 rtsp_parse_line(header, line);
2513 /* handle sequence number */
2514 c->seq = header->seq;
2516 if (!strcmp(cmd, "DESCRIBE")) {
2517 rtsp_cmd_describe(c, url);
2518 } else if (!strcmp(cmd, "SETUP")) {
2519 rtsp_cmd_setup(c, url, header);
2520 } else if (!strcmp(cmd, "PLAY")) {
2521 rtsp_cmd_play(c, url, header);
2522 } else if (!strcmp(cmd, "PAUSE")) {
2523 rtsp_cmd_pause(c, url, header);
2524 } else if (!strcmp(cmd, "TEARDOWN")) {
2525 rtsp_cmd_teardown(c, url, header);
2527 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2530 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2531 c->pb = NULL; /* safety */
2533 /* XXX: cannot do more */
2536 c->buffer_ptr = c->pb_buffer;
2537 c->buffer_end = c->pb_buffer + len;
2538 c->state = RTSPSTATE_SEND_REPLY;
2542 /* XXX: move that to rtsp.c, but would need to replace FFStream by
2544 static int prepare_sdp_description(FFStream *stream, UINT8 **pbuffer,
2545 struct in_addr my_ip)
2547 ByteIOContext pb1, *pb = &pb1;
2548 int i, payload_type, port;
2549 const char *ipstr, *title, *mediatype;
2552 if (url_open_dyn_buf(pb) < 0)
2555 /* general media info */
2557 url_fprintf(pb, "v=0\n");
2558 ipstr = inet_ntoa(my_ip);
2559 url_fprintf(pb, "o=- 0 0 IN IP4 %s\n", ipstr);
2560 title = stream->title;
2561 if (title[0] == '\0')
2563 url_fprintf(pb, "s=%s\n", title);
2564 if (stream->comment[0] != '\0')
2565 url_fprintf(pb, "i=%s\n", stream->comment);
2566 if (stream->is_multicast) {
2567 url_fprintf(pb, "c=IN IP4 %s\n", inet_ntoa(stream->multicast_ip));
2569 /* for each stream, we output the necessary info */
2570 for(i = 0; i < stream->nb_streams; i++) {
2571 st = stream->streams[i];
2572 switch(st->codec.codec_type) {
2573 case CODEC_TYPE_AUDIO:
2574 mediatype = "audio";
2576 case CODEC_TYPE_VIDEO:
2577 mediatype = "video";
2580 mediatype = "application";
2583 /* NOTE: the port indication is not correct in case of
2584 unicast. It is not an issue because RTSP gives it */
2585 payload_type = rtp_get_payload_type(&st->codec);
2586 if (stream->is_multicast) {
2587 port = stream->multicast_port + 2 * i;
2591 url_fprintf(pb, "m=%s %d RTP/AVP %d\n",
2592 mediatype, port, payload_type);
2593 url_fprintf(pb, "a=control:streamid=%d\n", i);
2595 return url_close_dyn_buf(pb, pbuffer);
2598 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2604 int content_length, len;
2605 struct sockaddr_in my_addr;
2607 /* find which url is asked */
2608 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2613 for(stream = first_stream; stream != NULL; stream = stream->next) {
2614 if (!stream->is_feed && stream->fmt == &rtp_mux &&
2615 !strcmp(path, stream->filename)) {
2619 /* no stream found */
2620 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2624 /* prepare the media description in sdp format */
2626 /* get the host IP */
2627 len = sizeof(my_addr);
2628 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2630 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2631 if (content_length < 0) {
2632 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2635 rtsp_reply_header(c, RTSP_STATUS_OK);
2636 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2637 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2638 url_fprintf(c->pb, "\r\n");
2639 put_buffer(c->pb, content, content_length);
2642 static HTTPContext *find_rtp_session(const char *session_id)
2646 if (session_id[0] == '\0')
2649 for(c = first_http_ctx; c != NULL; c = c->next) {
2650 if (!strcmp(c->session_id, session_id))
2656 RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPProtocol protocol)
2658 RTSPTransportField *th;
2661 for(i=0;i<h->nb_transports;i++) {
2662 th = &h->transports[i];
2663 if (th->protocol == protocol)
2669 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2673 int stream_index, port;
2678 RTSPTransportField *th;
2679 struct sockaddr_in dest_addr;
2680 RTSPActionServerSetup setup;
2682 /* find which url is asked */
2683 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2688 /* now check each stream */
2689 for(stream = first_stream; stream != NULL; stream = stream->next) {
2690 if (!stream->is_feed && stream->fmt == &rtp_mux) {
2691 /* accept aggregate filenames only if single stream */
2692 if (!strcmp(path, stream->filename)) {
2693 if (stream->nb_streams != 1) {
2694 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2701 for(stream_index = 0; stream_index < stream->nb_streams;
2703 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2704 stream->filename, stream_index);
2705 if (!strcmp(path, buf))
2710 /* no stream found */
2711 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2715 /* generate session id if needed */
2716 if (h->session_id[0] == '\0') {
2717 snprintf(h->session_id, sizeof(h->session_id),
2718 "%08x%08x", (int)random(), (int)random());
2721 /* find rtp session, and create it if none found */
2722 rtp_c = find_rtp_session(h->session_id);
2724 rtp_c = rtp_new_connection(c, stream, h->session_id);
2726 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2730 /* open input stream */
2731 if (open_input_stream(rtp_c, "") < 0) {
2732 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2736 /* always prefer UDP */
2737 th = find_transport(h, RTSP_PROTOCOL_RTP_UDP);
2739 th = find_transport(h, RTSP_PROTOCOL_RTP_TCP);
2741 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2745 rtp_c->rtp_protocol = th->protocol;
2748 /* test if stream is OK (test needed because several SETUP needs
2749 to be done for a given file) */
2750 if (rtp_c->stream != stream) {
2751 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2755 /* test if stream is already set up */
2756 if (rtp_c->rtp_ctx[stream_index]) {
2757 rtsp_reply_error(c, RTSP_STATUS_STATE);
2761 /* check transport */
2762 th = find_transport(h, rtp_c->rtp_protocol);
2763 if (!th || (th->protocol == RTSP_PROTOCOL_RTP_UDP &&
2764 th->client_port_min <= 0)) {
2765 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2769 /* setup default options */
2770 setup.transport_option[0] = '\0';
2771 dest_addr = rtp_c->from_addr;
2772 dest_addr.sin_port = htons(th->client_port_min);
2774 /* add transport option if needed */
2775 if (ff_rtsp_callback) {
2776 setup.ipaddr = ntohl(dest_addr.sin_addr.s_addr);
2777 if (ff_rtsp_callback(RTSP_ACTION_SERVER_SETUP, rtp_c->session_id,
2778 (char *)&setup, sizeof(setup),
2779 stream->rtsp_option) < 0) {
2780 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2783 dest_addr.sin_addr.s_addr = htonl(setup.ipaddr);
2787 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr) < 0) {
2788 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2792 /* now everything is OK, so we can send the connection parameters */
2793 rtsp_reply_header(c, RTSP_STATUS_OK);
2795 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2797 switch(rtp_c->rtp_protocol) {
2798 case RTSP_PROTOCOL_RTP_UDP:
2799 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2800 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2801 "client_port=%d-%d;server_port=%d-%d",
2802 th->client_port_min, th->client_port_min + 1,
2805 case RTSP_PROTOCOL_RTP_TCP:
2806 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2807 stream_index * 2, stream_index * 2 + 1);
2812 if (setup.transport_option[0] != '\0') {
2813 url_fprintf(c->pb, ";%s", setup.transport_option);
2815 url_fprintf(c->pb, "\r\n");
2818 url_fprintf(c->pb, "\r\n");
2822 /* find an rtp connection by using the session ID. Check consistency
2824 static HTTPContext *find_rtp_session_with_url(const char *url,
2825 const char *session_id)
2831 rtp_c = find_rtp_session(session_id);
2835 /* find which url is asked */
2836 url_split(NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2840 if (strcmp(path, rtp_c->stream->filename) != 0)
2845 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2849 rtp_c = find_rtp_session_with_url(url, h->session_id);
2851 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2855 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2856 rtp_c->state != HTTPSTATE_WAIT_FEED &&
2857 rtp_c->state != HTTPSTATE_READY) {
2858 rtsp_reply_error(c, RTSP_STATUS_STATE);
2862 rtp_c->state = HTTPSTATE_SEND_DATA;
2864 /* now everything is OK, so we can send the connection parameters */
2865 rtsp_reply_header(c, RTSP_STATUS_OK);
2867 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2868 url_fprintf(c->pb, "\r\n");
2871 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
2875 rtp_c = find_rtp_session_with_url(url, h->session_id);
2877 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2881 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
2882 rtp_c->state != HTTPSTATE_WAIT_FEED) {
2883 rtsp_reply_error(c, RTSP_STATUS_STATE);
2887 rtp_c->state = HTTPSTATE_READY;
2889 /* now everything is OK, so we can send the connection parameters */
2890 rtsp_reply_header(c, RTSP_STATUS_OK);
2892 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2893 url_fprintf(c->pb, "\r\n");
2896 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
2900 rtp_c = find_rtp_session_with_url(url, h->session_id);
2902 rtsp_reply_error(c, RTSP_STATUS_SESSION);
2906 /* abort the session */
2907 close_connection(rtp_c);
2909 if (ff_rtsp_callback) {
2910 ff_rtsp_callback(RTSP_ACTION_SERVER_TEARDOWN, rtp_c->session_id,
2912 rtp_c->stream->rtsp_option);
2915 /* now everything is OK, so we can send the connection parameters */
2916 rtsp_reply_header(c, RTSP_STATUS_OK);
2918 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2919 url_fprintf(c->pb, "\r\n");
2923 /********************************************************************/
2926 static HTTPContext *rtp_new_connection(HTTPContext *rtsp_c,
2927 FFStream *stream, const char *session_id)
2929 HTTPContext *c = NULL;
2931 /* XXX: should output a warning page when coming
2932 close to the connection limit */
2933 if (nb_connections >= nb_max_connections)
2936 /* add a new connection */
2937 c = av_mallocz(sizeof(HTTPContext));
2942 c->poll_entry = NULL;
2943 c->from_addr = rtsp_c->from_addr;
2944 c->buffer_size = IOBUFFER_INIT_SIZE;
2945 c->buffer = av_malloc(c->buffer_size);
2950 pstrcpy(c->session_id, sizeof(c->session_id), session_id);
2951 c->state = HTTPSTATE_READY;
2952 c->is_packetized = 1;
2953 /* protocol is shown in statistics */
2954 pstrcpy(c->protocol, sizeof(c->protocol), "RTP");
2956 c->next = first_http_ctx;
2968 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
2969 command). if dest_addr is NULL, then TCP tunneling in RTSP is
2971 static int rtp_new_av_stream(HTTPContext *c,
2972 int stream_index, struct sockaddr_in *dest_addr)
2974 AVFormatContext *ctx;
2980 /* now we can open the relevant output stream */
2981 ctx = av_mallocz(sizeof(AVFormatContext));
2984 ctx->oformat = &rtp_mux;
2986 st = av_mallocz(sizeof(AVStream));
2989 ctx->nb_streams = 1;
2990 ctx->streams[0] = st;
2992 if (!c->stream->feed ||
2993 c->stream->feed == c->stream) {
2994 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
2997 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3002 /* build destination RTP address */
3003 ipaddr = inet_ntoa(dest_addr->sin_addr);
3005 snprintf(ctx->filename, sizeof(ctx->filename),
3006 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3008 printf("open %s\n", ctx->filename);
3010 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3012 c->rtp_handles[stream_index] = h;
3017 /* normally, no packets should be output here, but the packet size may be checked */
3018 if (url_open_dyn_packet_buf(&ctx->pb,
3019 url_get_max_packet_size(h)) < 0) {
3020 /* XXX: close stream */
3023 if (av_write_header(ctx) < 0) {
3030 url_close_dyn_buf(&ctx->pb, &dummy_buf);
3033 c->rtp_ctx[stream_index] = ctx;
3037 /********************************************************************/
3038 /* ffserver initialization */
3040 AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3044 fst = av_mallocz(sizeof(AVStream));
3047 fst->priv_data = av_mallocz(sizeof(FeedData));
3048 memcpy(&fst->codec, codec, sizeof(AVCodecContext));
3049 stream->streams[stream->nb_streams++] = fst;
3053 /* return the stream number in the feed */
3054 int add_av_stream(FFStream *feed,
3058 AVCodecContext *av, *av1;
3062 for(i=0;i<feed->nb_streams;i++) {
3063 st = feed->streams[i];
3065 if (av1->codec_id == av->codec_id &&
3066 av1->codec_type == av->codec_type &&
3067 av1->bit_rate == av->bit_rate) {
3069 switch(av->codec_type) {
3070 case CODEC_TYPE_AUDIO:
3071 if (av1->channels == av->channels &&
3072 av1->sample_rate == av->sample_rate)
3075 case CODEC_TYPE_VIDEO:
3076 if (av1->width == av->width &&
3077 av1->height == av->height &&
3078 av1->frame_rate == av->frame_rate &&
3079 av1->gop_size == av->gop_size)
3088 fst = add_av_stream1(feed, av);
3091 return feed->nb_streams - 1;
3096 void remove_stream(FFStream *stream)
3100 while (*ps != NULL) {
3101 if (*ps == stream) {
3109 /* compute the needed AVStream for each file */
3110 void build_file_streams(void)
3112 FFStream *stream, *stream_next;
3113 AVFormatContext *infile;
3116 /* gather all streams */
3117 for(stream = first_stream; stream != NULL; stream = stream_next) {
3118 stream_next = stream->next;
3119 if (stream->stream_type == STREAM_TYPE_LIVE &&
3121 /* the stream comes from a file */
3122 /* try to open the file */
3124 if (av_open_input_file(&infile, stream->feed_filename,
3125 NULL, 0, NULL) < 0) {
3126 http_log("%s not found", stream->feed_filename);
3127 /* remove stream (no need to spend more time on it) */
3129 remove_stream(stream);
3131 /* find all the AVStreams inside and reference them in
3133 if (av_find_stream_info(infile) < 0) {
3134 http_log("Could not find codec parameters from '%s'",
3135 stream->feed_filename);
3136 av_close_input_file(infile);
3139 for(i=0;i<infile->nb_streams;i++) {
3140 add_av_stream1(stream, &infile->streams[i]->codec);
3142 av_close_input_file(infile);
3148 /* compute the needed AVStream for each feed */
3149 void build_feed_streams(void)
3151 FFStream *stream, *feed;
3154 /* gather all streams */
3155 for(stream = first_stream; stream != NULL; stream = stream->next) {
3156 feed = stream->feed;
3158 if (!stream->is_feed) {
3159 /* we handle a stream coming from a feed */
3160 for(i=0;i<stream->nb_streams;i++) {
3161 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3167 /* gather all streams */
3168 for(stream = first_stream; stream != NULL; stream = stream->next) {
3169 feed = stream->feed;
3171 if (stream->is_feed) {
3172 for(i=0;i<stream->nb_streams;i++) {
3173 stream->feed_streams[i] = i;
3179 /* create feed files if needed */
3180 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3183 if (url_exist(feed->feed_filename)) {
3184 /* See if it matches */
3188 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3189 /* Now see if it matches */
3190 if (s->nb_streams == feed->nb_streams) {
3192 for(i=0;i<s->nb_streams;i++) {
3194 sf = feed->streams[i];
3197 if (sf->index != ss->index ||
3199 printf("Index & Id do not match for stream %d\n", i);
3202 AVCodecContext *ccf, *ccs;
3206 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3208 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3209 printf("Codecs do not match for stream %d\n", i);
3211 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3212 printf("Codec bitrates do not match for stream %d\n", i);
3214 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3215 if (CHECK_CODEC(frame_rate) ||
3216 CHECK_CODEC(width) ||
3217 CHECK_CODEC(height)) {
3218 printf("Codec width, height and framerate do not match for stream %d\n", i);
3221 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3222 if (CHECK_CODEC(sample_rate) ||
3223 CHECK_CODEC(channels) ||
3224 CHECK_CODEC(frame_size)) {
3225 printf("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3229 printf("Unknown codec type\n");
3238 printf("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3239 feed->feed_filename, s->nb_streams, feed->nb_streams);
3242 av_close_input_file(s);
3244 printf("Deleting feed file '%s' as it appears to be corrupt\n",
3245 feed->feed_filename);
3248 unlink(feed->feed_filename);
3250 if (!url_exist(feed->feed_filename)) {
3251 AVFormatContext s1, *s = &s1;
3253 /* only write the header of the ffm file */
3254 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3255 fprintf(stderr, "Could not open output feed file '%s'\n",
3256 feed->feed_filename);
3259 s->oformat = feed->fmt;
3260 s->nb_streams = feed->nb_streams;
3261 for(i=0;i<s->nb_streams;i++) {
3263 st = feed->streams[i];
3267 /* XXX: need better api */
3268 av_freep(&s->priv_data);
3271 /* get feed size and write index */
3272 fd = open(feed->feed_filename, O_RDONLY);
3274 fprintf(stderr, "Could not open output feed file '%s'\n",
3275 feed->feed_filename);
3279 feed->feed_write_index = ffm_read_write_index(fd);
3280 feed->feed_size = lseek(fd, 0, SEEK_END);
3281 /* ensure that we do not wrap before the end of file */
3282 if (feed->feed_max_size < feed->feed_size)
3283 feed->feed_max_size = feed->feed_size;
3289 static void get_arg(char *buf, int buf_size, const char **pp)
3296 while (isspace(*p)) p++;
3299 if (*p == '\"' || *p == '\'')
3311 if ((q - buf) < buf_size - 1)
3316 if (quote && *p == quote)
3321 /* add a codec and set the default parameters */
3322 void add_codec(FFStream *stream, AVCodecContext *av)
3326 /* compute default parameters */
3327 switch(av->codec_type) {
3328 case CODEC_TYPE_AUDIO:
3329 if (av->bit_rate == 0)
3330 av->bit_rate = 64000;
3331 if (av->sample_rate == 0)
3332 av->sample_rate = 22050;
3333 if (av->channels == 0)
3336 case CODEC_TYPE_VIDEO:
3337 if (av->bit_rate == 0)
3338 av->bit_rate = 64000;
3339 if (av->frame_rate == 0)
3340 av->frame_rate = 5 * FRAME_RATE_BASE;
3341 if (av->width == 0 || av->height == 0) {
3345 /* Bitrate tolerance is less for streaming */
3346 if (av->bit_rate_tolerance == 0)
3347 av->bit_rate_tolerance = av->bit_rate / 4;
3352 if (av->max_qdiff == 0)
3354 av->qcompress = 0.5;
3358 av->rc_eq = "tex^qComp";
3359 if (!av->i_quant_factor)
3360 av->i_quant_factor = -0.8;
3361 if (!av->b_quant_factor)
3362 av->b_quant_factor = 1.25;
3363 if (!av->b_quant_offset)
3364 av->b_quant_offset = 1.25;
3365 if (!av->rc_min_rate)
3366 av->rc_min_rate = av->bit_rate / 2;
3367 if (!av->rc_max_rate)
3368 av->rc_max_rate = av->bit_rate * 2;
3375 st = av_mallocz(sizeof(AVStream));
3378 stream->streams[stream->nb_streams++] = st;
3379 memcpy(&st->codec, av, sizeof(AVCodecContext));
3382 int opt_audio_codec(const char *arg)
3388 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
3393 return CODEC_ID_NONE;
3399 int opt_video_codec(const char *arg)
3405 if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
3410 return CODEC_ID_NONE;
3416 /* simplistic plugin support */
3418 void load_module(const char *filename)
3421 void (*init_func)(void);
3422 dll = dlopen(filename, RTLD_NOW);
3424 fprintf(stderr, "Could not load module '%s' - %s\n",
3425 filename, dlerror());
3429 init_func = dlsym(dll, "ffserver_module_init");
3432 "%s: init function 'ffserver_module_init()' not found\n",
3440 int parse_ffconfig(const char *filename)
3447 int val, errors, line_num;
3448 FFStream **last_stream, *stream, *redirect;
3449 FFStream **last_feed, *feed;
3450 AVCodecContext audio_enc, video_enc;
3451 int audio_id, video_id;
3453 f = fopen(filename, "r");
3461 first_stream = NULL;
3462 last_stream = &first_stream;
3464 last_feed = &first_feed;
3468 audio_id = CODEC_ID_NONE;
3469 video_id = CODEC_ID_NONE;
3471 if (fgets(line, sizeof(line), f) == NULL)
3477 if (*p == '\0' || *p == '#')
3480 get_arg(cmd, sizeof(cmd), &p);
3482 if (!strcasecmp(cmd, "Port")) {
3483 get_arg(arg, sizeof(arg), &p);
3484 my_http_addr.sin_port = htons (atoi(arg));
3485 } else if (!strcasecmp(cmd, "BindAddress")) {
3486 get_arg(arg, sizeof(arg), &p);
3487 if (!inet_aton(arg, &my_http_addr.sin_addr)) {
3488 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3489 filename, line_num, arg);
3492 } else if (!strcasecmp(cmd, "NoDaemon")) {
3493 ffserver_daemon = 0;
3494 } else if (!strcasecmp(cmd, "RTSPPort")) {
3495 get_arg(arg, sizeof(arg), &p);
3496 my_rtsp_addr.sin_port = htons (atoi(arg));
3497 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3498 get_arg(arg, sizeof(arg), &p);
3499 if (!inet_aton(arg, &my_rtsp_addr.sin_addr)) {
3500 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3501 filename, line_num, arg);
3504 } else if (!strcasecmp(cmd, "MaxClients")) {
3505 get_arg(arg, sizeof(arg), &p);
3507 if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
3508 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3509 filename, line_num, arg);
3512 nb_max_connections = val;
3514 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3515 get_arg(arg, sizeof(arg), &p);
3517 if (val < 10 || val > 100000) {
3518 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3519 filename, line_num, arg);
3522 nb_max_bandwidth = val;
3524 } else if (!strcasecmp(cmd, "CustomLog")) {
3525 get_arg(logfilename, sizeof(logfilename), &p);
3526 } else if (!strcasecmp(cmd, "<Feed")) {
3527 /*********************************************/
3528 /* Feed related options */
3530 if (stream || feed) {
3531 fprintf(stderr, "%s:%d: Already in a tag\n",
3532 filename, line_num);
3534 feed = av_mallocz(sizeof(FFStream));
3535 /* add in stream list */
3536 *last_stream = feed;
3537 last_stream = &feed->next;
3538 /* add in feed list */
3540 last_feed = &feed->next_feed;
3542 get_arg(feed->filename, sizeof(feed->filename), &p);
3543 q = strrchr(feed->filename, '>');
3546 feed->fmt = guess_format("ffm", NULL, NULL);
3547 /* defaut feed file */
3548 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3549 "/tmp/%s.ffm", feed->filename);
3550 feed->feed_max_size = 5 * 1024 * 1024;
3552 feed->feed = feed; /* self feeding :-) */
3554 } else if (!strcasecmp(cmd, "Launch")) {
3558 feed->child_argv = (char **) av_mallocz(64 * sizeof(char *));
3560 feed->child_argv[0] = av_malloc(7);
3561 strcpy(feed->child_argv[0], "ffmpeg");
3563 for (i = 1; i < 62; i++) {
3566 get_arg(argbuf, sizeof(argbuf), &p);
3570 feed->child_argv[i] = av_malloc(strlen(argbuf + 1));
3571 strcpy(feed->child_argv[i], argbuf);
3574 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3576 snprintf(feed->child_argv[i], 256, "http://127.0.0.1:%d/%s",
3577 ntohs(my_http_addr.sin_port), feed->filename);
3579 } else if (!strcasecmp(cmd, "File")) {
3581 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3582 } else if (stream) {
3583 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3585 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3590 get_arg(arg, sizeof(arg), &p);
3592 fsize = strtod(p1, (char **)&p1);
3593 switch(toupper(*p1)) {
3598 fsize *= 1024 * 1024;
3601 fsize *= 1024 * 1024 * 1024;
3604 feed->feed_max_size = (INT64)fsize;
3606 } else if (!strcasecmp(cmd, "</Feed>")) {
3608 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3609 filename, line_num);
3613 /* Make sure that we start out clean */
3614 if (unlink(feed->feed_filename) < 0
3615 && errno != ENOENT) {
3616 fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
3617 filename, line_num, feed->feed_filename, strerror(errno));
3623 } else if (!strcasecmp(cmd, "<Stream")) {
3624 /*********************************************/
3625 /* Stream related options */
3627 if (stream || feed) {
3628 fprintf(stderr, "%s:%d: Already in a tag\n",
3629 filename, line_num);
3631 stream = av_mallocz(sizeof(FFStream));
3632 *last_stream = stream;
3633 last_stream = &stream->next;
3635 get_arg(stream->filename, sizeof(stream->filename), &p);
3636 q = strrchr(stream->filename, '>');
3639 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3640 memset(&audio_enc, 0, sizeof(AVCodecContext));
3641 memset(&video_enc, 0, sizeof(AVCodecContext));
3642 audio_id = CODEC_ID_NONE;
3643 video_id = CODEC_ID_NONE;
3645 audio_id = stream->fmt->audio_codec;
3646 video_id = stream->fmt->video_codec;
3649 } else if (!strcasecmp(cmd, "Feed")) {
3650 get_arg(arg, sizeof(arg), &p);
3655 while (sfeed != NULL) {
3656 if (!strcmp(sfeed->filename, arg))
3658 sfeed = sfeed->next_feed;
3661 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3662 filename, line_num, arg);
3664 stream->feed = sfeed;
3667 } else if (!strcasecmp(cmd, "Format")) {
3668 get_arg(arg, sizeof(arg), &p);
3669 if (!strcmp(arg, "status")) {
3670 stream->stream_type = STREAM_TYPE_STATUS;
3673 stream->stream_type = STREAM_TYPE_LIVE;
3674 /* jpeg cannot be used here, so use single frame jpeg */
3675 if (!strcmp(arg, "jpeg"))
3676 strcpy(arg, "singlejpeg");
3677 stream->fmt = guess_stream_format(arg, NULL, NULL);
3679 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
3680 filename, line_num, arg);
3685 audio_id = stream->fmt->audio_codec;
3686 video_id = stream->fmt->video_codec;
3688 } else if (!strcasecmp(cmd, "FaviconURL")) {
3689 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
3690 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3692 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
3693 filename, line_num);
3696 } else if (!strcasecmp(cmd, "Author")) {
3698 get_arg(stream->author, sizeof(stream->author), &p);
3700 } else if (!strcasecmp(cmd, "Comment")) {
3702 get_arg(stream->comment, sizeof(stream->comment), &p);
3704 } else if (!strcasecmp(cmd, "Copyright")) {
3706 get_arg(stream->copyright, sizeof(stream->copyright), &p);
3708 } else if (!strcasecmp(cmd, "Title")) {
3710 get_arg(stream->title, sizeof(stream->title), &p);
3712 } else if (!strcasecmp(cmd, "Preroll")) {
3713 get_arg(arg, sizeof(arg), &p);
3715 stream->prebuffer = atof(arg) * 1000;
3717 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
3719 stream->send_on_key = 1;
3721 } else if (!strcasecmp(cmd, "AudioCodec")) {
3722 get_arg(arg, sizeof(arg), &p);
3723 audio_id = opt_audio_codec(arg);
3724 if (audio_id == CODEC_ID_NONE) {
3725 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
3726 filename, line_num, arg);
3729 } else if (!strcasecmp(cmd, "VideoCodec")) {
3730 get_arg(arg, sizeof(arg), &p);
3731 video_id = opt_video_codec(arg);
3732 if (video_id == CODEC_ID_NONE) {
3733 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
3734 filename, line_num, arg);
3737 } else if (!strcasecmp(cmd, "MaxTime")) {
3738 get_arg(arg, sizeof(arg), &p);
3740 stream->max_time = atof(arg) * 1000;
3742 } else if (!strcasecmp(cmd, "AudioBitRate")) {
3743 get_arg(arg, sizeof(arg), &p);
3745 audio_enc.bit_rate = atoi(arg) * 1000;
3747 } else if (!strcasecmp(cmd, "AudioChannels")) {
3748 get_arg(arg, sizeof(arg), &p);
3750 audio_enc.channels = atoi(arg);
3752 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
3753 get_arg(arg, sizeof(arg), &p);
3755 audio_enc.sample_rate = atoi(arg);
3757 } else if (!strcasecmp(cmd, "AudioQuality")) {
3758 get_arg(arg, sizeof(arg), &p);
3760 audio_enc.quality = atof(arg) * 1000;
3762 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
3764 int minrate, maxrate;
3766 get_arg(arg, sizeof(arg), &p);
3768 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
3769 video_enc.rc_min_rate = minrate * 1000;
3770 video_enc.rc_max_rate = maxrate * 1000;
3772 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
3773 filename, line_num, arg);
3777 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
3779 get_arg(arg, sizeof(arg), &p);
3780 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
3782 } else if (!strcasecmp(cmd, "VideoBitRate")) {
3783 get_arg(arg, sizeof(arg), &p);
3785 video_enc.bit_rate = atoi(arg) * 1000;
3787 } else if (!strcasecmp(cmd, "VideoSize")) {
3788 get_arg(arg, sizeof(arg), &p);
3790 parse_image_size(&video_enc.width, &video_enc.height, arg);
3791 if ((video_enc.width % 16) != 0 ||
3792 (video_enc.height % 16) != 0) {
3793 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
3794 filename, line_num);
3798 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
3799 get_arg(arg, sizeof(arg), &p);
3801 video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
3803 } else if (!strcasecmp(cmd, "VideoGopSize")) {
3804 get_arg(arg, sizeof(arg), &p);
3806 video_enc.gop_size = atoi(arg);
3808 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
3810 video_enc.gop_size = 1;
3812 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
3814 video_enc.flags |= CODEC_FLAG_HQ;
3816 } else if (!strcasecmp(cmd, "VideoQDiff")) {
3817 get_arg(arg, sizeof(arg), &p);
3819 video_enc.max_qdiff = atoi(arg);
3820 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
3821 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
3822 filename, line_num);
3826 } else if (!strcasecmp(cmd, "VideoQMax")) {
3827 get_arg(arg, sizeof(arg), &p);
3829 video_enc.qmax = atoi(arg);
3830 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
3831 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
3832 filename, line_num);
3836 } else if (!strcasecmp(cmd, "VideoQMin")) {
3837 get_arg(arg, sizeof(arg), &p);
3839 video_enc.qmin = atoi(arg);
3840 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
3841 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
3842 filename, line_num);
3846 } else if (!strcasecmp(cmd, "LumaElim")) {
3847 get_arg(arg, sizeof(arg), &p);
3849 video_enc.luma_elim_threshold = atoi(arg);
3851 } else if (!strcasecmp(cmd, "ChromaElim")) {
3852 get_arg(arg, sizeof(arg), &p);
3854 video_enc.chroma_elim_threshold = atoi(arg);
3856 } else if (!strcasecmp(cmd, "LumiMask")) {
3857 get_arg(arg, sizeof(arg), &p);
3859 video_enc.lumi_masking = atof(arg);
3861 } else if (!strcasecmp(cmd, "DarkMask")) {
3862 get_arg(arg, sizeof(arg), &p);
3864 video_enc.dark_masking = atof(arg);
3866 } else if (!strcasecmp(cmd, "NoVideo")) {
3867 video_id = CODEC_ID_NONE;
3868 } else if (!strcasecmp(cmd, "NoAudio")) {
3869 audio_id = CODEC_ID_NONE;
3870 } else if (!strcasecmp(cmd, "ACL")) {
3874 get_arg(arg, sizeof(arg), &p);
3875 if (strcasecmp(arg, "allow") == 0) {
3876 acl.action = IP_ALLOW;
3877 } else if (strcasecmp(arg, "deny") == 0) {
3878 acl.action = IP_DENY;
3880 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
3881 filename, line_num, arg);
3885 get_arg(arg, sizeof(arg), &p);
3887 he = gethostbyname(arg);
3889 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
3890 filename, line_num, arg);
3893 /* Only take the first */
3894 acl.first = *(struct in_addr *) he->h_addr_list[0];
3895 acl.last = acl.first;
3898 get_arg(arg, sizeof(arg), &p);
3901 he = gethostbyname(arg);
3903 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
3904 filename, line_num, arg);
3907 /* Only take the first */
3908 acl.last = *(struct in_addr *) he->h_addr_list[0];
3913 IPAddressACL *nacl = (IPAddressACL *) av_mallocz(sizeof(*nacl));
3914 IPAddressACL **naclp = 0;
3920 naclp = &stream->acl;
3924 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
3925 filename, line_num);
3931 naclp = &(*naclp)->next;
3936 } else if (!strcasecmp(cmd, "RTSPOption")) {
3937 get_arg(arg, sizeof(arg), &p);
3939 av_freep(&stream->rtsp_option);
3940 /* XXX: av_strdup ? */
3941 stream->rtsp_option = av_malloc(strlen(arg) + 1);
3942 if (stream->rtsp_option) {
3943 strcpy(stream->rtsp_option, arg);
3946 } else if (!strcasecmp(cmd, "MulticastAddress")) {
3947 get_arg(arg, sizeof(arg), &p);
3949 if (!inet_aton(arg, &stream->multicast_ip)) {
3950 fprintf(stderr, "%s:%d: Invalid IP address: %s\n",
3951 filename, line_num, arg);
3954 stream->is_multicast = 1;
3956 } else if (!strcasecmp(cmd, "MulticastPort")) {
3957 get_arg(arg, sizeof(arg), &p);
3959 stream->multicast_port = atoi(arg);
3961 } else if (!strcasecmp(cmd, "</Stream>")) {
3963 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
3964 filename, line_num);
3967 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
3968 if (audio_id != CODEC_ID_NONE) {
3969 audio_enc.codec_type = CODEC_TYPE_AUDIO;
3970 audio_enc.codec_id = audio_id;
3971 add_codec(stream, &audio_enc);
3973 if (video_id != CODEC_ID_NONE) {
3974 video_enc.codec_type = CODEC_TYPE_VIDEO;
3975 video_enc.codec_id = video_id;
3976 add_codec(stream, &video_enc);
3980 } else if (!strcasecmp(cmd, "<Redirect")) {
3981 /*********************************************/
3983 if (stream || feed || redirect) {
3984 fprintf(stderr, "%s:%d: Already in a tag\n",
3985 filename, line_num);
3988 redirect = av_mallocz(sizeof(FFStream));
3989 *last_stream = redirect;
3990 last_stream = &redirect->next;
3992 get_arg(redirect->filename, sizeof(redirect->filename), &p);
3993 q = strrchr(redirect->filename, '>');
3996 redirect->stream_type = STREAM_TYPE_REDIRECT;
3998 } else if (!strcasecmp(cmd, "URL")) {
4000 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4002 } else if (!strcasecmp(cmd, "</Redirect>")) {
4004 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4005 filename, line_num);
4008 if (!redirect->feed_filename[0]) {
4009 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4010 filename, line_num);
4014 } else if (!strcasecmp(cmd, "LoadModule")) {
4015 get_arg(arg, sizeof(arg), &p);
4018 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4019 filename, line_num, cmd);
4033 static void write_packet(FFCodec *ffenc,
4034 UINT8 *buf, int size)
4037 AVCodecContext *enc = &ffenc->enc;
4039 mk_header(&hdr, enc, size);
4040 wptr = http_fifo.wptr;
4041 fifo_write(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &wptr);
4042 fifo_write(&http_fifo, buf, size, &wptr);
4043 /* atomic modification of wptr */
4044 http_fifo.wptr = wptr;
4045 ffenc->data_count += size;
4046 ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
4052 printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4053 "usage: ffserver [-L] [-h] [-f configfile]\n"
4054 "Hyper fast multi format Audio/Video streaming server\n"
4056 "-L : print the LICENCE\n"
4058 "-f configfile : use configfile instead of /etc/ffserver.conf\n"
4065 "ffserver version " FFMPEG_VERSION "\n"
4066 "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
4067 "This library is free software; you can redistribute it and/or\n"
4068 "modify it under the terms of the GNU Lesser General Public\n"
4069 "License as published by the Free Software Foundation; either\n"
4070 "version 2 of the License, or (at your option) any later version.\n"
4072 "This library is distributed in the hope that it will be useful,\n"
4073 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
4074 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
4075 "Lesser General Public License for more details.\n"
4077 "You should have received a copy of the GNU Lesser General Public\n"
4078 "License along with this library; if not, write to the Free Software\n"
4079 "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n"
4083 static void handle_child_exit(int sig)
4088 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4091 for (feed = first_feed; feed; feed = feed->next) {
4092 if (feed->pid == pid) {
4093 int uptime = time(0) - feed->pid_start;
4096 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4099 /* Turn off any more restarts */
4100 feed->child_argv = 0;
4106 need_to_start_children = 1;
4109 int main(int argc, char **argv)
4111 const char *config_filename;
4113 struct sigaction sigact;
4117 config_filename = "/etc/ffserver.conf";
4119 my_program_name = argv[0];
4120 my_program_dir = getcwd(0, 0);
4121 ffserver_daemon = 1;
4124 c = getopt(argc, argv, "ndLh?f:");
4140 ffserver_daemon = 0;
4143 config_filename = optarg;
4150 putenv("http_proxy"); /* Kill the http_proxy */
4152 srandom(gettime_ms() + (getpid() << 16));
4154 /* address on which the server will handle HTTP connections */
4155 my_http_addr.sin_family = AF_INET;
4156 my_http_addr.sin_port = htons (8080);
4157 my_http_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4159 /* address on which the server will handle RTSP connections */
4160 my_rtsp_addr.sin_family = AF_INET;
4161 my_rtsp_addr.sin_port = htons (5454);
4162 my_rtsp_addr.sin_addr.s_addr = htonl (INADDR_ANY);
4164 nb_max_connections = 5;
4165 nb_max_bandwidth = 1000;
4166 first_stream = NULL;
4167 logfilename[0] = '\0';
4169 memset(&sigact, 0, sizeof(sigact));
4170 sigact.sa_handler = handle_child_exit;
4171 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4172 sigaction(SIGCHLD, &sigact, 0);
4174 if (parse_ffconfig(config_filename) < 0) {
4175 fprintf(stderr, "Incorrect config file - exiting.\n");
4179 build_file_streams();
4181 build_feed_streams();
4183 /* put the process in background and detach it from its TTY */
4184 if (ffserver_daemon) {
4191 } else if (pid > 0) {
4199 open("/dev/null", O_RDWR);
4200 if (strcmp(logfilename, "-") != 0) {
4210 signal(SIGPIPE, SIG_IGN);
4212 /* open log file if needed */
4213 if (logfilename[0] != '\0') {
4214 if (!strcmp(logfilename, "-"))
4217 logfile = fopen(logfilename, "w");
4220 if (http_server() < 0) {
4221 fprintf(stderr, "Could not start server\n");