Rename "tx_ctx" and "cur_tx" variables to "transport_priv" and
[ffmpeg-lucabe.git] / ffserver.c
blob8bfea66c2a48de168b2f28f7ece972bac29ec6ed
1 /*
2 * Multiple format streaming server
3 * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #define _XOPEN_SOURCE 600
24 #include "config.h"
25 #if !HAVE_CLOSESOCKET
26 #define closesocket close
27 #endif
28 #include <string.h>
29 #include <strings.h>
30 #include <stdlib.h>
31 /* avformat.h defines LIBAVFORMAT_BUILD, include it before all the other libav* headers which use it */
32 #include "libavformat/avformat.h"
33 #include "libavformat/network.h"
34 #include "libavformat/os_support.h"
35 #include "libavformat/rtp.h"
36 #include "libavformat/rtsp.h"
37 #include "libavutil/avstring.h"
38 #include "libavutil/random.h"
39 #include "libavutil/intreadwrite.h"
40 #include "libavcodec/opt.h"
41 #include <stdarg.h>
42 #include <unistd.h>
43 #include <fcntl.h>
44 #include <sys/ioctl.h>
45 #if HAVE_POLL_H
46 #include <poll.h>
47 #endif
48 #include <errno.h>
49 #include <sys/time.h>
50 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
51 #include <time.h>
52 #include <sys/wait.h>
53 #include <signal.h>
54 #if HAVE_DLFCN_H
55 #include <dlfcn.h>
56 #endif
58 #include "cmdutils.h"
60 #undef exit
62 const char program_name[] = "FFserver";
63 const int program_birth_year = 2000;
65 static const OptionDef options[];
67 enum HTTPState {
68 HTTPSTATE_WAIT_REQUEST,
69 HTTPSTATE_SEND_HEADER,
70 HTTPSTATE_SEND_DATA_HEADER,
71 HTTPSTATE_SEND_DATA, /* sending TCP or UDP data */
72 HTTPSTATE_SEND_DATA_TRAILER,
73 HTTPSTATE_RECEIVE_DATA,
74 HTTPSTATE_WAIT_FEED, /* wait for data from the feed */
75 HTTPSTATE_READY,
77 RTSPSTATE_WAIT_REQUEST,
78 RTSPSTATE_SEND_REPLY,
79 RTSPSTATE_SEND_PACKET,
82 static const char *http_state[] = {
83 "HTTP_WAIT_REQUEST",
84 "HTTP_SEND_HEADER",
86 "SEND_DATA_HEADER",
87 "SEND_DATA",
88 "SEND_DATA_TRAILER",
89 "RECEIVE_DATA",
90 "WAIT_FEED",
91 "READY",
93 "RTSP_WAIT_REQUEST",
94 "RTSP_SEND_REPLY",
95 "RTSP_SEND_PACKET",
98 #define IOBUFFER_INIT_SIZE 8192
100 /* timeouts are in ms */
101 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
102 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
104 #define SYNC_TIMEOUT (10 * 1000)
106 typedef struct RTSPActionServerSetup {
107 uint32_t ipaddr;
108 char transport_option[512];
109 } RTSPActionServerSetup;
111 typedef struct {
112 int64_t count1, count2;
113 int64_t time1, time2;
114 } DataRateData;
116 /* context associated with one connection */
117 typedef struct HTTPContext {
118 enum HTTPState state;
119 int fd; /* socket file descriptor */
120 struct sockaddr_in from_addr; /* origin */
121 struct pollfd *poll_entry; /* used when polling */
122 int64_t timeout;
123 uint8_t *buffer_ptr, *buffer_end;
124 int http_error;
125 int post;
126 struct HTTPContext *next;
127 int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
128 int64_t data_count;
129 /* feed input */
130 int feed_fd;
131 /* input format handling */
132 AVFormatContext *fmt_in;
133 int64_t start_time; /* In milliseconds - this wraps fairly often */
134 int64_t first_pts; /* initial pts value */
135 int64_t cur_pts; /* current pts value from the stream in us */
136 int64_t cur_frame_duration; /* duration of the current frame in us */
137 int cur_frame_bytes; /* output frame size, needed to compute
138 the time at which we send each
139 packet */
140 int pts_stream_index; /* stream we choose as clock reference */
141 int64_t cur_clock; /* current clock reference value in us */
142 /* output format handling */
143 struct FFStream *stream;
144 /* -1 is invalid stream */
145 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
146 int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
147 int switch_pending;
148 AVFormatContext fmt_ctx; /* instance of FFStream for one user */
149 int last_packet_sent; /* true if last data packet was sent */
150 int suppress_log;
151 DataRateData datarate;
152 int wmp_client_id;
153 char protocol[16];
154 char method[16];
155 char url[128];
156 int buffer_size;
157 uint8_t *buffer;
158 int is_packetized; /* if true, the stream is packetized */
159 int packet_stream_index; /* current stream for output in state machine */
161 /* RTSP state specific */
162 uint8_t *pb_buffer; /* XXX: use that in all the code */
163 ByteIOContext *pb;
164 int seq; /* RTSP sequence number */
166 /* RTP state specific */
167 enum RTSPLowerTransport rtp_protocol;
168 char session_id[32]; /* session id */
169 AVFormatContext *rtp_ctx[MAX_STREAMS];
171 /* RTP/UDP specific */
172 URLContext *rtp_handles[MAX_STREAMS];
174 /* RTP/TCP specific */
175 struct HTTPContext *rtsp_c;
176 uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
177 } HTTPContext;
179 /* each generated stream is described here */
180 enum StreamType {
181 STREAM_TYPE_LIVE,
182 STREAM_TYPE_STATUS,
183 STREAM_TYPE_REDIRECT,
186 enum IPAddressAction {
187 IP_ALLOW = 1,
188 IP_DENY,
191 typedef struct IPAddressACL {
192 struct IPAddressACL *next;
193 enum IPAddressAction action;
194 /* These are in host order */
195 struct in_addr first;
196 struct in_addr last;
197 } IPAddressACL;
199 /* description of each stream of the ffserver.conf file */
200 typedef struct FFStream {
201 enum StreamType stream_type;
202 char filename[1024]; /* stream filename */
203 struct FFStream *feed; /* feed we are using (can be null if
204 coming from file) */
205 AVFormatParameters *ap_in; /* input parameters */
206 AVInputFormat *ifmt; /* if non NULL, force input format */
207 AVOutputFormat *fmt;
208 IPAddressACL *acl;
209 int nb_streams;
210 int prebuffer; /* Number of millseconds early to start */
211 int64_t max_time; /* Number of milliseconds to run */
212 int send_on_key;
213 AVStream *streams[MAX_STREAMS];
214 int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
215 char feed_filename[1024]; /* file name of the feed storage, or
216 input file name for a stream */
217 char author[512];
218 char title[512];
219 char copyright[512];
220 char comment[512];
221 pid_t pid; /* Of ffmpeg process */
222 time_t pid_start; /* Of ffmpeg process */
223 char **child_argv;
224 struct FFStream *next;
225 unsigned bandwidth; /* bandwidth, in kbits/s */
226 /* RTSP options */
227 char *rtsp_option;
228 /* multicast specific */
229 int is_multicast;
230 struct in_addr multicast_ip;
231 int multicast_port; /* first port used for multicast */
232 int multicast_ttl;
233 int loop; /* if true, send the stream in loops (only meaningful if file) */
235 /* feed specific */
236 int feed_opened; /* true if someone is writing to the feed */
237 int is_feed; /* true if it is a feed */
238 int readonly; /* True if writing is prohibited to the file */
239 int conns_served;
240 int64_t bytes_served;
241 int64_t feed_max_size; /* maximum storage size, zero means unlimited */
242 int64_t feed_write_index; /* current write position in feed (it wraps around) */
243 int64_t feed_size; /* current size of feed */
244 struct FFStream *next_feed;
245 } FFStream;
247 typedef struct FeedData {
248 long long data_count;
249 float avg_frame_size; /* frame size averaged over last frames with exponential mean */
250 } FeedData;
252 static struct sockaddr_in my_http_addr;
253 static struct sockaddr_in my_rtsp_addr;
255 static char logfilename[1024];
256 static HTTPContext *first_http_ctx;
257 static FFStream *first_feed; /* contains only feeds */
258 static FFStream *first_stream; /* contains all streams, including feeds */
260 static void new_connection(int server_fd, int is_rtsp);
261 static void close_connection(HTTPContext *c);
263 /* HTTP handling */
264 static int handle_connection(HTTPContext *c);
265 static int http_parse_request(HTTPContext *c);
266 static int http_send_data(HTTPContext *c);
267 static void compute_status(HTTPContext *c);
268 static int open_input_stream(HTTPContext *c, const char *info);
269 static int http_start_receive_data(HTTPContext *c);
270 static int http_receive_data(HTTPContext *c);
272 /* RTSP handling */
273 static int rtsp_parse_request(HTTPContext *c);
274 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
275 static void rtsp_cmd_options(HTTPContext *c, const char *url);
276 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPHeader *h);
277 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h);
278 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h);
279 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h);
281 /* SDP handling */
282 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
283 struct in_addr my_ip);
285 /* RTP handling */
286 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
287 FFStream *stream, const char *session_id,
288 enum RTSPLowerTransport rtp_protocol);
289 static int rtp_new_av_stream(HTTPContext *c,
290 int stream_index, struct sockaddr_in *dest_addr,
291 HTTPContext *rtsp_c);
293 static const char *my_program_name;
294 static const char *my_program_dir;
296 static const char *config_filename;
297 static int ffserver_debug;
298 static int ffserver_daemon;
299 static int no_launch;
300 static int need_to_start_children;
302 /* maximum number of simultaneous HTTP connections */
303 static unsigned int nb_max_http_connections = 2000;
304 static unsigned int nb_max_connections = 5;
305 static unsigned int nb_connections;
307 static uint64_t max_bandwidth = 1000;
308 static uint64_t current_bandwidth;
310 static int64_t cur_time; // Making this global saves on passing it around everywhere
312 static AVRandomState random_state;
314 static FILE *logfile = NULL;
316 static char *ctime1(char *buf2)
318 time_t ti;
319 char *p;
321 ti = time(NULL);
322 p = ctime(&ti);
323 strcpy(buf2, p);
324 p = buf2 + strlen(p) - 1;
325 if (*p == '\n')
326 *p = '\0';
327 return buf2;
330 static void http_vlog(const char *fmt, va_list vargs)
332 static int print_prefix = 1;
333 if (logfile) {
334 if (print_prefix) {
335 char buf[32];
336 ctime1(buf);
337 fprintf(logfile, "%s ", buf);
339 print_prefix = strstr(fmt, "\n") != NULL;
340 vfprintf(logfile, fmt, vargs);
341 fflush(logfile);
345 void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
347 va_list vargs;
348 va_start(vargs, fmt);
349 http_vlog(fmt, vargs);
350 va_end(vargs);
353 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
355 static int print_prefix = 1;
356 AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
357 if (level > av_log_level)
358 return;
359 if (print_prefix && avc)
360 http_log("[%s @ %p]", avc->item_name(ptr), ptr);
361 print_prefix = strstr(fmt, "\n") != NULL;
362 http_vlog(fmt, vargs);
365 static void log_connection(HTTPContext *c)
367 if (c->suppress_log)
368 return;
370 http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
371 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
372 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
375 static void update_datarate(DataRateData *drd, int64_t count)
377 if (!drd->time1 && !drd->count1) {
378 drd->time1 = drd->time2 = cur_time;
379 drd->count1 = drd->count2 = count;
380 } else if (cur_time - drd->time2 > 5000) {
381 drd->time1 = drd->time2;
382 drd->count1 = drd->count2;
383 drd->time2 = cur_time;
384 drd->count2 = count;
388 /* In bytes per second */
389 static int compute_datarate(DataRateData *drd, int64_t count)
391 if (cur_time == drd->time1)
392 return 0;
394 return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
398 static void start_children(FFStream *feed)
400 if (no_launch)
401 return;
403 for (; feed; feed = feed->next) {
404 if (feed->child_argv && !feed->pid) {
405 feed->pid_start = time(0);
407 feed->pid = fork();
409 if (feed->pid < 0) {
410 http_log("Unable to create children\n");
411 exit(1);
413 if (!feed->pid) {
414 /* In child */
415 char pathname[1024];
416 char *slash;
417 int i;
419 av_strlcpy(pathname, my_program_name, sizeof(pathname));
421 slash = strrchr(pathname, '/');
422 if (!slash)
423 slash = pathname;
424 else
425 slash++;
426 strcpy(slash, "ffmpeg");
428 http_log("Launch commandline: ");
429 http_log("%s ", pathname);
430 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
431 http_log("%s ", feed->child_argv[i]);
432 http_log("\n");
434 for (i = 3; i < 256; i++)
435 close(i);
437 if (!ffserver_debug) {
438 i = open("/dev/null", O_RDWR);
439 if (i != -1) {
440 dup2(i, 0);
441 dup2(i, 1);
442 dup2(i, 2);
443 close(i);
447 /* This is needed to make relative pathnames work */
448 chdir(my_program_dir);
450 signal(SIGPIPE, SIG_DFL);
452 execvp(pathname, feed->child_argv);
454 _exit(1);
460 /* open a listening socket */
461 static int socket_open_listen(struct sockaddr_in *my_addr)
463 int server_fd, tmp;
465 server_fd = socket(AF_INET,SOCK_STREAM,0);
466 if (server_fd < 0) {
467 perror ("socket");
468 return -1;
471 tmp = 1;
472 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
474 if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
475 char bindmsg[32];
476 snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
477 perror (bindmsg);
478 closesocket(server_fd);
479 return -1;
482 if (listen (server_fd, 5) < 0) {
483 perror ("listen");
484 closesocket(server_fd);
485 return -1;
487 ff_socket_nonblock(server_fd, 1);
489 return server_fd;
492 /* start all multicast streams */
493 static void start_multicast(void)
495 FFStream *stream;
496 char session_id[32];
497 HTTPContext *rtp_c;
498 struct sockaddr_in dest_addr;
499 int default_port, stream_index;
501 default_port = 6000;
502 for(stream = first_stream; stream != NULL; stream = stream->next) {
503 if (stream->is_multicast) {
504 /* open the RTP connection */
505 snprintf(session_id, sizeof(session_id), "%08x%08x",
506 av_random(&random_state), av_random(&random_state));
508 /* choose a port if none given */
509 if (stream->multicast_port == 0) {
510 stream->multicast_port = default_port;
511 default_port += 100;
514 dest_addr.sin_family = AF_INET;
515 dest_addr.sin_addr = stream->multicast_ip;
516 dest_addr.sin_port = htons(stream->multicast_port);
518 rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
519 RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
520 if (!rtp_c)
521 continue;
523 if (open_input_stream(rtp_c, "") < 0) {
524 http_log("Could not open input stream for stream '%s'\n",
525 stream->filename);
526 continue;
529 /* open each RTP stream */
530 for(stream_index = 0; stream_index < stream->nb_streams;
531 stream_index++) {
532 dest_addr.sin_port = htons(stream->multicast_port +
533 2 * stream_index);
534 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
535 http_log("Could not open output stream '%s/streamid=%d'\n",
536 stream->filename, stream_index);
537 exit(1);
541 /* change state to send data */
542 rtp_c->state = HTTPSTATE_SEND_DATA;
547 /* main loop of the http server */
548 static int http_server(void)
550 int server_fd = 0, rtsp_server_fd = 0;
551 int ret, delay, delay1;
552 struct pollfd *poll_table, *poll_entry;
553 HTTPContext *c, *c_next;
555 if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
556 http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
557 return -1;
560 if (my_http_addr.sin_port) {
561 server_fd = socket_open_listen(&my_http_addr);
562 if (server_fd < 0)
563 return -1;
566 if (my_rtsp_addr.sin_port) {
567 rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
568 if (rtsp_server_fd < 0)
569 return -1;
572 if (!rtsp_server_fd && !server_fd) {
573 http_log("HTTP and RTSP disabled.\n");
574 return -1;
577 http_log("FFserver started.\n");
579 start_children(first_feed);
581 start_multicast();
583 for(;;) {
584 poll_entry = poll_table;
585 if (server_fd) {
586 poll_entry->fd = server_fd;
587 poll_entry->events = POLLIN;
588 poll_entry++;
590 if (rtsp_server_fd) {
591 poll_entry->fd = rtsp_server_fd;
592 poll_entry->events = POLLIN;
593 poll_entry++;
596 /* wait for events on each HTTP handle */
597 c = first_http_ctx;
598 delay = 1000;
599 while (c != NULL) {
600 int fd;
601 fd = c->fd;
602 switch(c->state) {
603 case HTTPSTATE_SEND_HEADER:
604 case RTSPSTATE_SEND_REPLY:
605 case RTSPSTATE_SEND_PACKET:
606 c->poll_entry = poll_entry;
607 poll_entry->fd = fd;
608 poll_entry->events = POLLOUT;
609 poll_entry++;
610 break;
611 case HTTPSTATE_SEND_DATA_HEADER:
612 case HTTPSTATE_SEND_DATA:
613 case HTTPSTATE_SEND_DATA_TRAILER:
614 if (!c->is_packetized) {
615 /* for TCP, we output as much as we can (may need to put a limit) */
616 c->poll_entry = poll_entry;
617 poll_entry->fd = fd;
618 poll_entry->events = POLLOUT;
619 poll_entry++;
620 } else {
621 /* when ffserver is doing the timing, we work by
622 looking at which packet need to be sent every
623 10 ms */
624 delay1 = 10; /* one tick wait XXX: 10 ms assumed */
625 if (delay1 < delay)
626 delay = delay1;
628 break;
629 case HTTPSTATE_WAIT_REQUEST:
630 case HTTPSTATE_RECEIVE_DATA:
631 case HTTPSTATE_WAIT_FEED:
632 case RTSPSTATE_WAIT_REQUEST:
633 /* need to catch errors */
634 c->poll_entry = poll_entry;
635 poll_entry->fd = fd;
636 poll_entry->events = POLLIN;/* Maybe this will work */
637 poll_entry++;
638 break;
639 default:
640 c->poll_entry = NULL;
641 break;
643 c = c->next;
646 /* wait for an event on one connection. We poll at least every
647 second to handle timeouts */
648 do {
649 ret = poll(poll_table, poll_entry - poll_table, delay);
650 if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
651 ff_neterrno() != FF_NETERROR(EINTR))
652 return -1;
653 } while (ret < 0);
655 cur_time = av_gettime() / 1000;
657 if (need_to_start_children) {
658 need_to_start_children = 0;
659 start_children(first_feed);
662 /* now handle the events */
663 for(c = first_http_ctx; c != NULL; c = c_next) {
664 c_next = c->next;
665 if (handle_connection(c) < 0) {
666 /* close and free the connection */
667 log_connection(c);
668 close_connection(c);
672 poll_entry = poll_table;
673 if (server_fd) {
674 /* new HTTP connection request ? */
675 if (poll_entry->revents & POLLIN)
676 new_connection(server_fd, 0);
677 poll_entry++;
679 if (rtsp_server_fd) {
680 /* new RTSP connection request ? */
681 if (poll_entry->revents & POLLIN)
682 new_connection(rtsp_server_fd, 1);
687 /* start waiting for a new HTTP/RTSP request */
688 static void start_wait_request(HTTPContext *c, int is_rtsp)
690 c->buffer_ptr = c->buffer;
691 c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
693 if (is_rtsp) {
694 c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
695 c->state = RTSPSTATE_WAIT_REQUEST;
696 } else {
697 c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
698 c->state = HTTPSTATE_WAIT_REQUEST;
702 static void new_connection(int server_fd, int is_rtsp)
704 struct sockaddr_in from_addr;
705 int fd, len;
706 HTTPContext *c = NULL;
708 len = sizeof(from_addr);
709 fd = accept(server_fd, (struct sockaddr *)&from_addr,
710 &len);
711 if (fd < 0) {
712 http_log("error during accept %s\n", strerror(errno));
713 return;
715 ff_socket_nonblock(fd, 1);
717 /* XXX: should output a warning page when coming
718 close to the connection limit */
719 if (nb_connections >= nb_max_connections)
720 goto fail;
722 /* add a new connection */
723 c = av_mallocz(sizeof(HTTPContext));
724 if (!c)
725 goto fail;
727 c->fd = fd;
728 c->poll_entry = NULL;
729 c->from_addr = from_addr;
730 c->buffer_size = IOBUFFER_INIT_SIZE;
731 c->buffer = av_malloc(c->buffer_size);
732 if (!c->buffer)
733 goto fail;
735 c->next = first_http_ctx;
736 first_http_ctx = c;
737 nb_connections++;
739 start_wait_request(c, is_rtsp);
741 return;
743 fail:
744 if (c) {
745 av_free(c->buffer);
746 av_free(c);
748 closesocket(fd);
751 static void close_connection(HTTPContext *c)
753 HTTPContext **cp, *c1;
754 int i, nb_streams;
755 AVFormatContext *ctx;
756 URLContext *h;
757 AVStream *st;
759 /* remove connection from list */
760 cp = &first_http_ctx;
761 while ((*cp) != NULL) {
762 c1 = *cp;
763 if (c1 == c)
764 *cp = c->next;
765 else
766 cp = &c1->next;
769 /* remove references, if any (XXX: do it faster) */
770 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
771 if (c1->rtsp_c == c)
772 c1->rtsp_c = NULL;
775 /* remove connection associated resources */
776 if (c->fd >= 0)
777 closesocket(c->fd);
778 if (c->fmt_in) {
779 /* close each frame parser */
780 for(i=0;i<c->fmt_in->nb_streams;i++) {
781 st = c->fmt_in->streams[i];
782 if (st->codec->codec)
783 avcodec_close(st->codec);
785 av_close_input_file(c->fmt_in);
788 /* free RTP output streams if any */
789 nb_streams = 0;
790 if (c->stream)
791 nb_streams = c->stream->nb_streams;
793 for(i=0;i<nb_streams;i++) {
794 ctx = c->rtp_ctx[i];
795 if (ctx) {
796 av_write_trailer(ctx);
797 av_free(ctx);
799 h = c->rtp_handles[i];
800 if (h)
801 url_close(h);
804 ctx = &c->fmt_ctx;
806 if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
807 if (ctx->oformat) {
808 /* prepare header */
809 if (url_open_dyn_buf(&ctx->pb) >= 0) {
810 av_write_trailer(ctx);
811 av_freep(&c->pb_buffer);
812 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
817 for(i=0; i<ctx->nb_streams; i++)
818 av_free(ctx->streams[i]);
820 if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
821 current_bandwidth -= c->stream->bandwidth;
823 /* signal that there is no feed if we are the feeder socket */
824 if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
825 c->stream->feed_opened = 0;
826 close(c->feed_fd);
829 av_freep(&c->pb_buffer);
830 av_freep(&c->packet_buffer);
831 av_free(c->buffer);
832 av_free(c);
833 nb_connections--;
836 static int handle_connection(HTTPContext *c)
838 int len, ret;
840 switch(c->state) {
841 case HTTPSTATE_WAIT_REQUEST:
842 case RTSPSTATE_WAIT_REQUEST:
843 /* timeout ? */
844 if ((c->timeout - cur_time) < 0)
845 return -1;
846 if (c->poll_entry->revents & (POLLERR | POLLHUP))
847 return -1;
849 /* no need to read if no events */
850 if (!(c->poll_entry->revents & POLLIN))
851 return 0;
852 /* read the data */
853 read_loop:
854 len = recv(c->fd, c->buffer_ptr, 1, 0);
855 if (len < 0) {
856 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
857 ff_neterrno() != FF_NETERROR(EINTR))
858 return -1;
859 } else if (len == 0) {
860 return -1;
861 } else {
862 /* search for end of request. */
863 uint8_t *ptr;
864 c->buffer_ptr += len;
865 ptr = c->buffer_ptr;
866 if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
867 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
868 /* request found : parse it and reply */
869 if (c->state == HTTPSTATE_WAIT_REQUEST) {
870 ret = http_parse_request(c);
871 } else {
872 ret = rtsp_parse_request(c);
874 if (ret < 0)
875 return -1;
876 } else if (ptr >= c->buffer_end) {
877 /* request too long: cannot do anything */
878 return -1;
879 } else goto read_loop;
881 break;
883 case HTTPSTATE_SEND_HEADER:
884 if (c->poll_entry->revents & (POLLERR | POLLHUP))
885 return -1;
887 /* no need to write if no events */
888 if (!(c->poll_entry->revents & POLLOUT))
889 return 0;
890 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
891 if (len < 0) {
892 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
893 ff_neterrno() != FF_NETERROR(EINTR)) {
894 /* error : close connection */
895 av_freep(&c->pb_buffer);
896 return -1;
898 } else {
899 c->buffer_ptr += len;
900 if (c->stream)
901 c->stream->bytes_served += len;
902 c->data_count += len;
903 if (c->buffer_ptr >= c->buffer_end) {
904 av_freep(&c->pb_buffer);
905 /* if error, exit */
906 if (c->http_error)
907 return -1;
908 /* all the buffer was sent : synchronize to the incoming stream */
909 c->state = HTTPSTATE_SEND_DATA_HEADER;
910 c->buffer_ptr = c->buffer_end = c->buffer;
913 break;
915 case HTTPSTATE_SEND_DATA:
916 case HTTPSTATE_SEND_DATA_HEADER:
917 case HTTPSTATE_SEND_DATA_TRAILER:
918 /* for packetized output, we consider we can always write (the
919 input streams sets the speed). It may be better to verify
920 that we do not rely too much on the kernel queues */
921 if (!c->is_packetized) {
922 if (c->poll_entry->revents & (POLLERR | POLLHUP))
923 return -1;
925 /* no need to read if no events */
926 if (!(c->poll_entry->revents & POLLOUT))
927 return 0;
929 if (http_send_data(c) < 0)
930 return -1;
931 /* close connection if trailer sent */
932 if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
933 return -1;
934 break;
935 case HTTPSTATE_RECEIVE_DATA:
936 /* no need to read if no events */
937 if (c->poll_entry->revents & (POLLERR | POLLHUP))
938 return -1;
939 if (!(c->poll_entry->revents & POLLIN))
940 return 0;
941 if (http_receive_data(c) < 0)
942 return -1;
943 break;
944 case HTTPSTATE_WAIT_FEED:
945 /* no need to read if no events */
946 if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
947 return -1;
949 /* nothing to do, we'll be waken up by incoming feed packets */
950 break;
952 case RTSPSTATE_SEND_REPLY:
953 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
954 av_freep(&c->pb_buffer);
955 return -1;
957 /* no need to write if no events */
958 if (!(c->poll_entry->revents & POLLOUT))
959 return 0;
960 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
961 if (len < 0) {
962 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
963 ff_neterrno() != FF_NETERROR(EINTR)) {
964 /* error : close connection */
965 av_freep(&c->pb_buffer);
966 return -1;
968 } else {
969 c->buffer_ptr += len;
970 c->data_count += len;
971 if (c->buffer_ptr >= c->buffer_end) {
972 /* all the buffer was sent : wait for a new request */
973 av_freep(&c->pb_buffer);
974 start_wait_request(c, 1);
977 break;
978 case RTSPSTATE_SEND_PACKET:
979 if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
980 av_freep(&c->packet_buffer);
981 return -1;
983 /* no need to write if no events */
984 if (!(c->poll_entry->revents & POLLOUT))
985 return 0;
986 len = send(c->fd, c->packet_buffer_ptr,
987 c->packet_buffer_end - c->packet_buffer_ptr, 0);
988 if (len < 0) {
989 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
990 ff_neterrno() != FF_NETERROR(EINTR)) {
991 /* error : close connection */
992 av_freep(&c->packet_buffer);
993 return -1;
995 } else {
996 c->packet_buffer_ptr += len;
997 if (c->packet_buffer_ptr >= c->packet_buffer_end) {
998 /* all the buffer was sent : wait for a new request */
999 av_freep(&c->packet_buffer);
1000 c->state = RTSPSTATE_WAIT_REQUEST;
1003 break;
1004 case HTTPSTATE_READY:
1005 /* nothing to do */
1006 break;
1007 default:
1008 return -1;
1010 return 0;
1013 static int extract_rates(char *rates, int ratelen, const char *request)
1015 const char *p;
1017 for (p = request; *p && *p != '\r' && *p != '\n'; ) {
1018 if (strncasecmp(p, "Pragma:", 7) == 0) {
1019 const char *q = p + 7;
1021 while (*q && *q != '\n' && isspace(*q))
1022 q++;
1024 if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
1025 int stream_no;
1026 int rate_no;
1028 q += 20;
1030 memset(rates, 0xff, ratelen);
1032 while (1) {
1033 while (*q && *q != '\n' && *q != ':')
1034 q++;
1036 if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
1037 break;
1039 stream_no--;
1040 if (stream_no < ratelen && stream_no >= 0)
1041 rates[stream_no] = rate_no;
1043 while (*q && *q != '\n' && !isspace(*q))
1044 q++;
1047 return 1;
1050 p = strchr(p, '\n');
1051 if (!p)
1052 break;
1054 p++;
1057 return 0;
1060 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
1062 int i;
1063 int best_bitrate = 100000000;
1064 int best = -1;
1066 for (i = 0; i < feed->nb_streams; i++) {
1067 AVCodecContext *feed_codec = feed->streams[i]->codec;
1069 if (feed_codec->codec_id != codec->codec_id ||
1070 feed_codec->sample_rate != codec->sample_rate ||
1071 feed_codec->width != codec->width ||
1072 feed_codec->height != codec->height)
1073 continue;
1075 /* Potential stream */
1077 /* We want the fastest stream less than bit_rate, or the slowest
1078 * faster than bit_rate
1081 if (feed_codec->bit_rate <= bit_rate) {
1082 if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
1083 best_bitrate = feed_codec->bit_rate;
1084 best = i;
1086 } else {
1087 if (feed_codec->bit_rate < best_bitrate) {
1088 best_bitrate = feed_codec->bit_rate;
1089 best = i;
1094 return best;
1097 static int modify_current_stream(HTTPContext *c, char *rates)
1099 int i;
1100 FFStream *req = c->stream;
1101 int action_required = 0;
1103 /* Not much we can do for a feed */
1104 if (!req->feed)
1105 return 0;
1107 for (i = 0; i < req->nb_streams; i++) {
1108 AVCodecContext *codec = req->streams[i]->codec;
1110 switch(rates[i]) {
1111 case 0:
1112 c->switch_feed_streams[i] = req->feed_streams[i];
1113 break;
1114 case 1:
1115 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
1116 break;
1117 case 2:
1118 /* Wants off or slow */
1119 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
1120 #ifdef WANTS_OFF
1121 /* This doesn't work well when it turns off the only stream! */
1122 c->switch_feed_streams[i] = -2;
1123 c->feed_streams[i] = -2;
1124 #endif
1125 break;
1128 if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
1129 action_required = 1;
1132 return action_required;
1136 static void do_switch_stream(HTTPContext *c, int i)
1138 if (c->switch_feed_streams[i] >= 0) {
1139 #ifdef PHILIP
1140 c->feed_streams[i] = c->switch_feed_streams[i];
1141 #endif
1143 /* Now update the stream */
1145 c->switch_feed_streams[i] = -1;
1148 /* XXX: factorize in utils.c ? */
1149 /* XXX: take care with different space meaning */
1150 static void skip_spaces(const char **pp)
1152 const char *p;
1153 p = *pp;
1154 while (*p == ' ' || *p == '\t')
1155 p++;
1156 *pp = p;
1159 static void get_word(char *buf, int buf_size, const char **pp)
1161 const char *p;
1162 char *q;
1164 p = *pp;
1165 skip_spaces(&p);
1166 q = buf;
1167 while (!isspace(*p) && *p != '\0') {
1168 if ((q - buf) < buf_size - 1)
1169 *q++ = *p;
1170 p++;
1172 if (buf_size > 0)
1173 *q = '\0';
1174 *pp = p;
1177 static int validate_acl(FFStream *stream, HTTPContext *c)
1179 enum IPAddressAction last_action = IP_DENY;
1180 IPAddressACL *acl;
1181 struct in_addr *src = &c->from_addr.sin_addr;
1182 unsigned long src_addr = src->s_addr;
1184 for (acl = stream->acl; acl; acl = acl->next) {
1185 if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
1186 return (acl->action == IP_ALLOW) ? 1 : 0;
1187 last_action = acl->action;
1190 /* Nothing matched, so return not the last action */
1191 return (last_action == IP_DENY) ? 1 : 0;
1194 /* compute the real filename of a file by matching it without its
1195 extensions to all the stream filenames */
1196 static void compute_real_filename(char *filename, int max_size)
1198 char file1[1024];
1199 char file2[1024];
1200 char *p;
1201 FFStream *stream;
1203 /* compute filename by matching without the file extensions */
1204 av_strlcpy(file1, filename, sizeof(file1));
1205 p = strrchr(file1, '.');
1206 if (p)
1207 *p = '\0';
1208 for(stream = first_stream; stream != NULL; stream = stream->next) {
1209 av_strlcpy(file2, stream->filename, sizeof(file2));
1210 p = strrchr(file2, '.');
1211 if (p)
1212 *p = '\0';
1213 if (!strcmp(file1, file2)) {
1214 av_strlcpy(filename, stream->filename, max_size);
1215 break;
1220 enum RedirType {
1221 REDIR_NONE,
1222 REDIR_ASX,
1223 REDIR_RAM,
1224 REDIR_ASF,
1225 REDIR_RTSP,
1226 REDIR_SDP,
1229 /* parse http request and prepare header */
1230 static int http_parse_request(HTTPContext *c)
1232 char *p;
1233 enum RedirType redir_type;
1234 char cmd[32];
1235 char info[1024], filename[1024];
1236 char url[1024], *q;
1237 char protocol[32];
1238 char msg[1024];
1239 const char *mime_type;
1240 FFStream *stream;
1241 int i;
1242 char ratebuf[32];
1243 char *useragent = 0;
1245 p = c->buffer;
1246 get_word(cmd, sizeof(cmd), (const char **)&p);
1247 av_strlcpy(c->method, cmd, sizeof(c->method));
1249 if (!strcmp(cmd, "GET"))
1250 c->post = 0;
1251 else if (!strcmp(cmd, "POST"))
1252 c->post = 1;
1253 else
1254 return -1;
1256 get_word(url, sizeof(url), (const char **)&p);
1257 av_strlcpy(c->url, url, sizeof(c->url));
1259 get_word(protocol, sizeof(protocol), (const char **)&p);
1260 if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
1261 return -1;
1263 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
1265 if (ffserver_debug)
1266 http_log("New connection: %s %s\n", cmd, url);
1268 /* find the filename and the optional info string in the request */
1269 p = strchr(url, '?');
1270 if (p) {
1271 av_strlcpy(info, p, sizeof(info));
1272 *p = '\0';
1273 } else
1274 info[0] = '\0';
1276 av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
1278 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1279 if (strncasecmp(p, "User-Agent:", 11) == 0) {
1280 useragent = p + 11;
1281 if (*useragent && *useragent != '\n' && isspace(*useragent))
1282 useragent++;
1283 break;
1285 p = strchr(p, '\n');
1286 if (!p)
1287 break;
1289 p++;
1292 redir_type = REDIR_NONE;
1293 if (match_ext(filename, "asx")) {
1294 redir_type = REDIR_ASX;
1295 filename[strlen(filename)-1] = 'f';
1296 } else if (match_ext(filename, "asf") &&
1297 (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
1298 /* if this isn't WMP or lookalike, return the redirector file */
1299 redir_type = REDIR_ASF;
1300 } else if (match_ext(filename, "rpm,ram")) {
1301 redir_type = REDIR_RAM;
1302 strcpy(filename + strlen(filename)-2, "m");
1303 } else if (match_ext(filename, "rtsp")) {
1304 redir_type = REDIR_RTSP;
1305 compute_real_filename(filename, sizeof(filename) - 1);
1306 } else if (match_ext(filename, "sdp")) {
1307 redir_type = REDIR_SDP;
1308 compute_real_filename(filename, sizeof(filename) - 1);
1311 // "redirect" / request to index.html
1312 if (!strlen(filename))
1313 av_strlcpy(filename, "index.html", sizeof(filename) - 1);
1315 stream = first_stream;
1316 while (stream != NULL) {
1317 if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
1318 break;
1319 stream = stream->next;
1321 if (stream == NULL) {
1322 snprintf(msg, sizeof(msg), "File '%s' not found", url);
1323 goto send_error;
1326 c->stream = stream;
1327 memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
1328 memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
1330 if (stream->stream_type == STREAM_TYPE_REDIRECT) {
1331 c->http_error = 301;
1332 q = c->buffer;
1333 q += snprintf(q, c->buffer_size,
1334 "HTTP/1.0 301 Moved\r\n"
1335 "Location: %s\r\n"
1336 "Content-type: text/html\r\n"
1337 "\r\n"
1338 "<html><head><title>Moved</title></head><body>\r\n"
1339 "You should be <a href=\"%s\">redirected</a>.\r\n"
1340 "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
1341 /* prepare output buffer */
1342 c->buffer_ptr = c->buffer;
1343 c->buffer_end = q;
1344 c->state = HTTPSTATE_SEND_HEADER;
1345 return 0;
1348 /* If this is WMP, get the rate information */
1349 if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1350 if (modify_current_stream(c, ratebuf)) {
1351 for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
1352 if (c->switch_feed_streams[i] >= 0)
1353 do_switch_stream(c, i);
1358 /* If already streaming this feed, do not let start another feeder. */
1359 if (stream->feed_opened) {
1360 snprintf(msg, sizeof(msg), "This feed is already being received.");
1361 http_log("feed %s already being received\n", stream->feed_filename);
1362 goto send_error;
1365 if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
1366 current_bandwidth += stream->bandwidth;
1368 if (c->post == 0 && max_bandwidth < current_bandwidth) {
1369 c->http_error = 200;
1370 q = c->buffer;
1371 q += snprintf(q, c->buffer_size,
1372 "HTTP/1.0 200 Server too busy\r\n"
1373 "Content-type: text/html\r\n"
1374 "\r\n"
1375 "<html><head><title>Too busy</title></head><body>\r\n"
1376 "<p>The server is too busy to serve your request at this time.</p>\r\n"
1377 "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
1378 "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
1379 "</body></html>\r\n", current_bandwidth, max_bandwidth);
1380 /* prepare output buffer */
1381 c->buffer_ptr = c->buffer;
1382 c->buffer_end = q;
1383 c->state = HTTPSTATE_SEND_HEADER;
1384 return 0;
1387 if (redir_type != REDIR_NONE) {
1388 char *hostinfo = 0;
1390 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1391 if (strncasecmp(p, "Host:", 5) == 0) {
1392 hostinfo = p + 5;
1393 break;
1395 p = strchr(p, '\n');
1396 if (!p)
1397 break;
1399 p++;
1402 if (hostinfo) {
1403 char *eoh;
1404 char hostbuf[260];
1406 while (isspace(*hostinfo))
1407 hostinfo++;
1409 eoh = strchr(hostinfo, '\n');
1410 if (eoh) {
1411 if (eoh[-1] == '\r')
1412 eoh--;
1414 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
1415 memcpy(hostbuf, hostinfo, eoh - hostinfo);
1416 hostbuf[eoh - hostinfo] = 0;
1418 c->http_error = 200;
1419 q = c->buffer;
1420 switch(redir_type) {
1421 case REDIR_ASX:
1422 q += snprintf(q, c->buffer_size,
1423 "HTTP/1.0 200 ASX Follows\r\n"
1424 "Content-type: video/x-ms-asf\r\n"
1425 "\r\n"
1426 "<ASX Version=\"3\">\r\n"
1427 //"<!-- Autogenerated by ffserver -->\r\n"
1428 "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
1429 "</ASX>\r\n", hostbuf, filename, info);
1430 break;
1431 case REDIR_RAM:
1432 q += snprintf(q, c->buffer_size,
1433 "HTTP/1.0 200 RAM Follows\r\n"
1434 "Content-type: audio/x-pn-realaudio\r\n"
1435 "\r\n"
1436 "# Autogenerated by ffserver\r\n"
1437 "http://%s/%s%s\r\n", hostbuf, filename, info);
1438 break;
1439 case REDIR_ASF:
1440 q += snprintf(q, c->buffer_size,
1441 "HTTP/1.0 200 ASF Redirect follows\r\n"
1442 "Content-type: video/x-ms-asf\r\n"
1443 "\r\n"
1444 "[Reference]\r\n"
1445 "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
1446 break;
1447 case REDIR_RTSP:
1449 char hostname[256], *p;
1450 /* extract only hostname */
1451 av_strlcpy(hostname, hostbuf, sizeof(hostname));
1452 p = strrchr(hostname, ':');
1453 if (p)
1454 *p = '\0';
1455 q += snprintf(q, c->buffer_size,
1456 "HTTP/1.0 200 RTSP Redirect follows\r\n"
1457 /* XXX: incorrect mime type ? */
1458 "Content-type: application/x-rtsp\r\n"
1459 "\r\n"
1460 "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
1462 break;
1463 case REDIR_SDP:
1465 uint8_t *sdp_data;
1466 int sdp_data_size, len;
1467 struct sockaddr_in my_addr;
1469 q += snprintf(q, c->buffer_size,
1470 "HTTP/1.0 200 OK\r\n"
1471 "Content-type: application/sdp\r\n"
1472 "\r\n");
1474 len = sizeof(my_addr);
1475 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
1477 /* XXX: should use a dynamic buffer */
1478 sdp_data_size = prepare_sdp_description(stream,
1479 &sdp_data,
1480 my_addr.sin_addr);
1481 if (sdp_data_size > 0) {
1482 memcpy(q, sdp_data, sdp_data_size);
1483 q += sdp_data_size;
1484 *q = '\0';
1485 av_free(sdp_data);
1488 break;
1489 default:
1490 abort();
1491 break;
1494 /* prepare output buffer */
1495 c->buffer_ptr = c->buffer;
1496 c->buffer_end = q;
1497 c->state = HTTPSTATE_SEND_HEADER;
1498 return 0;
1503 snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
1504 goto send_error;
1507 stream->conns_served++;
1509 /* XXX: add there authenticate and IP match */
1511 if (c->post) {
1512 /* if post, it means a feed is being sent */
1513 if (!stream->is_feed) {
1514 /* However it might be a status report from WMP! Let us log the
1515 * data as it might come in handy one day. */
1516 char *logline = 0;
1517 int client_id = 0;
1519 for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
1520 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
1521 logline = p;
1522 break;
1524 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
1525 client_id = strtol(p + 18, 0, 10);
1526 p = strchr(p, '\n');
1527 if (!p)
1528 break;
1530 p++;
1533 if (logline) {
1534 char *eol = strchr(logline, '\n');
1536 logline += 17;
1538 if (eol) {
1539 if (eol[-1] == '\r')
1540 eol--;
1541 http_log("%.*s\n", (int) (eol - logline), logline);
1542 c->suppress_log = 1;
1546 #ifdef DEBUG_WMP
1547 http_log("\nGot request:\n%s\n", c->buffer);
1548 #endif
1550 if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
1551 HTTPContext *wmpc;
1553 /* Now we have to find the client_id */
1554 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
1555 if (wmpc->wmp_client_id == client_id)
1556 break;
1559 if (wmpc && modify_current_stream(wmpc, ratebuf))
1560 wmpc->switch_pending = 1;
1563 snprintf(msg, sizeof(msg), "POST command not handled");
1564 c->stream = 0;
1565 goto send_error;
1567 if (http_start_receive_data(c) < 0) {
1568 snprintf(msg, sizeof(msg), "could not open feed");
1569 goto send_error;
1571 c->http_error = 0;
1572 c->state = HTTPSTATE_RECEIVE_DATA;
1573 return 0;
1576 #ifdef DEBUG_WMP
1577 if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
1578 http_log("\nGot request:\n%s\n", c->buffer);
1579 #endif
1581 if (c->stream->stream_type == STREAM_TYPE_STATUS)
1582 goto send_status;
1584 /* open input stream */
1585 if (open_input_stream(c, info) < 0) {
1586 snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
1587 goto send_error;
1590 /* prepare http header */
1591 q = c->buffer;
1592 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
1593 mime_type = c->stream->fmt->mime_type;
1594 if (!mime_type)
1595 mime_type = "application/x-octet-stream";
1596 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
1598 /* for asf, we need extra headers */
1599 if (!strcmp(c->stream->fmt->name,"asf_stream")) {
1600 /* Need to allocate a client id */
1602 c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
1604 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "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);
1606 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
1607 q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
1609 /* prepare output buffer */
1610 c->http_error = 0;
1611 c->buffer_ptr = c->buffer;
1612 c->buffer_end = q;
1613 c->state = HTTPSTATE_SEND_HEADER;
1614 return 0;
1615 send_error:
1616 c->http_error = 404;
1617 q = c->buffer;
1618 q += snprintf(q, c->buffer_size,
1619 "HTTP/1.0 404 Not Found\r\n"
1620 "Content-type: text/html\r\n"
1621 "\r\n"
1622 "<HTML>\n"
1623 "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n"
1624 "<BODY>%s</BODY>\n"
1625 "</HTML>\n", msg);
1626 /* prepare output buffer */
1627 c->buffer_ptr = c->buffer;
1628 c->buffer_end = q;
1629 c->state = HTTPSTATE_SEND_HEADER;
1630 return 0;
1631 send_status:
1632 compute_status(c);
1633 c->http_error = 200; /* horrible : we use this value to avoid
1634 going to the send data state */
1635 c->state = HTTPSTATE_SEND_HEADER;
1636 return 0;
1639 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
1641 static const char *suffix = " kMGTP";
1642 const char *s;
1644 for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
1646 url_fprintf(pb, "%"PRId64"%c", count, *s);
1649 static void compute_status(HTTPContext *c)
1651 HTTPContext *c1;
1652 FFStream *stream;
1653 char *p;
1654 time_t ti;
1655 int i, len;
1656 ByteIOContext *pb;
1658 if (url_open_dyn_buf(&pb) < 0) {
1659 /* XXX: return an error ? */
1660 c->buffer_ptr = c->buffer;
1661 c->buffer_end = c->buffer;
1662 return;
1665 url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
1666 url_fprintf(pb, "Content-type: %s\r\n", "text/html");
1667 url_fprintf(pb, "Pragma: no-cache\r\n");
1668 url_fprintf(pb, "\r\n");
1670 url_fprintf(pb, "<HTML><HEAD><TITLE>%s Status</TITLE>\n", program_name);
1671 if (c->stream->feed_filename[0])
1672 url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
1673 url_fprintf(pb, "</HEAD>\n<BODY>");
1674 url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
1675 /* format status */
1676 url_fprintf(pb, "<H2>Available Streams</H2>\n");
1677 url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
1678 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");
1679 stream = first_stream;
1680 while (stream != NULL) {
1681 char sfilename[1024];
1682 char *eosf;
1684 if (stream->feed != stream) {
1685 av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
1686 eosf = sfilename + strlen(sfilename);
1687 if (eosf - sfilename >= 4) {
1688 if (strcmp(eosf - 4, ".asf") == 0)
1689 strcpy(eosf - 4, ".asx");
1690 else if (strcmp(eosf - 3, ".rm") == 0)
1691 strcpy(eosf - 3, ".ram");
1692 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
1693 /* generate a sample RTSP director if
1694 unicast. Generate an SDP redirector if
1695 multicast */
1696 eosf = strrchr(sfilename, '.');
1697 if (!eosf)
1698 eosf = sfilename + strlen(sfilename);
1699 if (stream->is_multicast)
1700 strcpy(eosf, ".sdp");
1701 else
1702 strcpy(eosf, ".rtsp");
1706 url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
1707 sfilename, stream->filename);
1708 url_fprintf(pb, "<td align=right> %d <td align=right> ",
1709 stream->conns_served);
1710 fmt_bytecount(pb, stream->bytes_served);
1711 switch(stream->stream_type) {
1712 case STREAM_TYPE_LIVE: {
1713 int audio_bit_rate = 0;
1714 int video_bit_rate = 0;
1715 const char *audio_codec_name = "";
1716 const char *video_codec_name = "";
1717 const char *audio_codec_name_extra = "";
1718 const char *video_codec_name_extra = "";
1720 for(i=0;i<stream->nb_streams;i++) {
1721 AVStream *st = stream->streams[i];
1722 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1723 switch(st->codec->codec_type) {
1724 case CODEC_TYPE_AUDIO:
1725 audio_bit_rate += st->codec->bit_rate;
1726 if (codec) {
1727 if (*audio_codec_name)
1728 audio_codec_name_extra = "...";
1729 audio_codec_name = codec->name;
1731 break;
1732 case CODEC_TYPE_VIDEO:
1733 video_bit_rate += st->codec->bit_rate;
1734 if (codec) {
1735 if (*video_codec_name)
1736 video_codec_name_extra = "...";
1737 video_codec_name = codec->name;
1739 break;
1740 case CODEC_TYPE_DATA:
1741 video_bit_rate += st->codec->bit_rate;
1742 break;
1743 default:
1744 abort();
1747 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",
1748 stream->fmt->name,
1749 stream->bandwidth,
1750 video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
1751 audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
1752 if (stream->feed)
1753 url_fprintf(pb, "<TD>%s", stream->feed->filename);
1754 else
1755 url_fprintf(pb, "<TD>%s", stream->feed_filename);
1756 url_fprintf(pb, "\n");
1758 break;
1759 default:
1760 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
1761 break;
1764 stream = stream->next;
1766 url_fprintf(pb, "</TABLE>\n");
1768 stream = first_stream;
1769 while (stream != NULL) {
1770 if (stream->feed == stream) {
1771 url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
1772 if (stream->pid) {
1773 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
1775 #if defined(linux) && !defined(CONFIG_NOCUTILS)
1777 FILE *pid_stat;
1778 char ps_cmd[64];
1780 /* This is somewhat linux specific I guess */
1781 snprintf(ps_cmd, sizeof(ps_cmd),
1782 "ps -o \"%%cpu,cputime\" --no-headers %d",
1783 stream->pid);
1785 pid_stat = popen(ps_cmd, "r");
1786 if (pid_stat) {
1787 char cpuperc[10];
1788 char cpuused[64];
1790 if (fscanf(pid_stat, "%10s %64s", cpuperc,
1791 cpuused) == 2) {
1792 url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
1793 cpuperc, cpuused);
1795 fclose(pid_stat);
1798 #endif
1800 url_fprintf(pb, "<p>");
1802 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");
1804 for (i = 0; i < stream->nb_streams; i++) {
1805 AVStream *st = stream->streams[i];
1806 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
1807 const char *type = "unknown";
1808 char parameters[64];
1810 parameters[0] = 0;
1812 switch(st->codec->codec_type) {
1813 case CODEC_TYPE_AUDIO:
1814 type = "audio";
1815 snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
1816 break;
1817 case CODEC_TYPE_VIDEO:
1818 type = "video";
1819 snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
1820 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
1821 break;
1822 default:
1823 abort();
1825 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
1826 i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
1828 url_fprintf(pb, "</table>\n");
1831 stream = stream->next;
1834 #if 0
1836 float avg;
1837 AVCodecContext *enc;
1838 char buf[1024];
1840 /* feed status */
1841 stream = first_feed;
1842 while (stream != NULL) {
1843 url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
1844 url_fprintf(pb, "<TABLE>\n");
1845 url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
1846 for(i=0;i<stream->nb_streams;i++) {
1847 AVStream *st = stream->streams[i];
1848 FeedData *fdata = st->priv_data;
1849 enc = st->codec;
1851 avcodec_string(buf, sizeof(buf), enc);
1852 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
1853 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
1854 avg /= enc->frame_size;
1855 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
1856 buf, enc->frame_number, fdata->data_count, avg / 1000.0);
1858 url_fprintf(pb, "</TABLE>\n");
1859 stream = stream->next_feed;
1862 #endif
1864 /* connection status */
1865 url_fprintf(pb, "<H2>Connection Status</H2>\n");
1867 url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
1868 nb_connections, nb_max_connections);
1870 url_fprintf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<BR>\n",
1871 current_bandwidth, max_bandwidth);
1873 url_fprintf(pb, "<TABLE>\n");
1874 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");
1875 c1 = first_http_ctx;
1876 i = 0;
1877 while (c1 != NULL) {
1878 int bitrate;
1879 int j;
1881 bitrate = 0;
1882 if (c1->stream) {
1883 for (j = 0; j < c1->stream->nb_streams; j++) {
1884 if (!c1->stream->feed)
1885 bitrate += c1->stream->streams[j]->codec->bit_rate;
1886 else if (c1->feed_streams[j] >= 0)
1887 bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
1891 i++;
1892 p = inet_ntoa(c1->from_addr.sin_addr);
1893 url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
1895 c1->stream ? c1->stream->filename : "",
1896 c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
1898 c1->protocol,
1899 http_state[c1->state]);
1900 fmt_bytecount(pb, bitrate);
1901 url_fprintf(pb, "<td align=right>");
1902 fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
1903 url_fprintf(pb, "<td align=right>");
1904 fmt_bytecount(pb, c1->data_count);
1905 url_fprintf(pb, "\n");
1906 c1 = c1->next;
1908 url_fprintf(pb, "</TABLE>\n");
1910 /* date */
1911 ti = time(NULL);
1912 p = ctime(&ti);
1913 url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
1914 url_fprintf(pb, "</BODY>\n</HTML>\n");
1916 len = url_close_dyn_buf(pb, &c->pb_buffer);
1917 c->buffer_ptr = c->pb_buffer;
1918 c->buffer_end = c->pb_buffer + len;
1921 /* check if the parser needs to be opened for stream i */
1922 static void open_parser(AVFormatContext *s, int i)
1924 AVStream *st = s->streams[i];
1925 AVCodec *codec;
1927 if (!st->codec->codec) {
1928 codec = avcodec_find_decoder(st->codec->codec_id);
1929 if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
1930 st->codec->parse_only = 1;
1931 if (avcodec_open(st->codec, codec) < 0)
1932 st->codec->parse_only = 0;
1937 static int open_input_stream(HTTPContext *c, const char *info)
1939 char buf[128];
1940 char input_filename[1024];
1941 AVFormatContext *s;
1942 int buf_size, i, ret;
1943 int64_t stream_pos;
1945 /* find file name */
1946 if (c->stream->feed) {
1947 strcpy(input_filename, c->stream->feed->feed_filename);
1948 buf_size = FFM_PACKET_SIZE;
1949 /* compute position (absolute time) */
1950 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1951 stream_pos = parse_date(buf, 0);
1952 if (stream_pos == INT64_MIN)
1953 return -1;
1954 } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
1955 int prebuffer = strtol(buf, 0, 10);
1956 stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
1957 } else
1958 stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
1959 } else {
1960 strcpy(input_filename, c->stream->feed_filename);
1961 buf_size = 0;
1962 /* compute position (relative time) */
1963 if (find_info_tag(buf, sizeof(buf), "date", info)) {
1964 stream_pos = parse_date(buf, 1);
1965 if (stream_pos == INT64_MIN)
1966 return -1;
1967 } else
1968 stream_pos = 0;
1970 if (input_filename[0] == '\0')
1971 return -1;
1973 #if 0
1974 { time_t when = stream_pos / 1000000;
1975 http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
1977 #endif
1979 /* open stream */
1980 if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
1981 buf_size, c->stream->ap_in)) < 0) {
1982 http_log("could not open %s: %d\n", input_filename, ret);
1983 return -1;
1985 s->flags |= AVFMT_FLAG_GENPTS;
1986 c->fmt_in = s;
1987 av_find_stream_info(c->fmt_in);
1989 /* open each parser */
1990 for(i=0;i<s->nb_streams;i++)
1991 open_parser(s, i);
1993 /* choose stream as clock source (we favorize video stream if
1994 present) for packet sending */
1995 c->pts_stream_index = 0;
1996 for(i=0;i<c->stream->nb_streams;i++) {
1997 if (c->pts_stream_index == 0 &&
1998 c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
1999 c->pts_stream_index = i;
2003 #if 1
2004 if (c->fmt_in->iformat->read_seek)
2005 av_seek_frame(c->fmt_in, -1, stream_pos, 0);
2006 #endif
2007 /* set the start time (needed for maxtime and RTP packet timing) */
2008 c->start_time = cur_time;
2009 c->first_pts = AV_NOPTS_VALUE;
2010 return 0;
2013 /* return the server clock (in us) */
2014 static int64_t get_server_clock(HTTPContext *c)
2016 /* compute current pts value from system time */
2017 return (cur_time - c->start_time) * 1000;
2020 /* return the estimated time at which the current packet must be sent
2021 (in us) */
2022 static int64_t get_packet_send_clock(HTTPContext *c)
2024 int bytes_left, bytes_sent, frame_bytes;
2026 frame_bytes = c->cur_frame_bytes;
2027 if (frame_bytes <= 0)
2028 return c->cur_pts;
2029 else {
2030 bytes_left = c->buffer_end - c->buffer_ptr;
2031 bytes_sent = frame_bytes - bytes_left;
2032 return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2037 static int http_prepare_data(HTTPContext *c)
2039 int i, len, ret;
2040 AVFormatContext *ctx;
2042 av_freep(&c->pb_buffer);
2043 switch(c->state) {
2044 case HTTPSTATE_SEND_DATA_HEADER:
2045 memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
2046 av_strlcpy(c->fmt_ctx.author, c->stream->author,
2047 sizeof(c->fmt_ctx.author));
2048 av_strlcpy(c->fmt_ctx.comment, c->stream->comment,
2049 sizeof(c->fmt_ctx.comment));
2050 av_strlcpy(c->fmt_ctx.copyright, c->stream->copyright,
2051 sizeof(c->fmt_ctx.copyright));
2052 av_strlcpy(c->fmt_ctx.title, c->stream->title,
2053 sizeof(c->fmt_ctx.title));
2055 for(i=0;i<c->stream->nb_streams;i++) {
2056 AVStream *st;
2057 AVStream *src;
2058 st = av_mallocz(sizeof(AVStream));
2059 c->fmt_ctx.streams[i] = st;
2060 /* if file or feed, then just take streams from FFStream struct */
2061 if (!c->stream->feed ||
2062 c->stream->feed == c->stream)
2063 src = c->stream->streams[i];
2064 else
2065 src = c->stream->feed->streams[c->stream->feed_streams[i]];
2067 *st = *src;
2068 st->priv_data = 0;
2069 st->codec->frame_number = 0; /* XXX: should be done in
2070 AVStream, not in codec */
2072 /* set output format parameters */
2073 c->fmt_ctx.oformat = c->stream->fmt;
2074 c->fmt_ctx.nb_streams = c->stream->nb_streams;
2076 c->got_key_frame = 0;
2078 /* prepare header and save header data in a stream */
2079 if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2080 /* XXX: potential leak */
2081 return -1;
2083 c->fmt_ctx.pb->is_streamed = 1;
2086 * HACK to avoid mpeg ps muxer to spit many underflow errors
2087 * Default value from FFmpeg
2088 * Try to set it use configuration option
2090 c->fmt_ctx.preload = (int)(0.5*AV_TIME_BASE);
2091 c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
2093 av_set_parameters(&c->fmt_ctx, NULL);
2094 if (av_write_header(&c->fmt_ctx) < 0) {
2095 http_log("Error writing output header\n");
2096 return -1;
2099 len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2100 c->buffer_ptr = c->pb_buffer;
2101 c->buffer_end = c->pb_buffer + len;
2103 c->state = HTTPSTATE_SEND_DATA;
2104 c->last_packet_sent = 0;
2105 break;
2106 case HTTPSTATE_SEND_DATA:
2107 /* find a new packet */
2108 /* read a packet from the input stream */
2109 if (c->stream->feed)
2110 ffm_set_write_index(c->fmt_in,
2111 c->stream->feed->feed_write_index,
2112 c->stream->feed->feed_size);
2114 if (c->stream->max_time &&
2115 c->stream->max_time + c->start_time - cur_time < 0)
2116 /* We have timed out */
2117 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2118 else {
2119 AVPacket pkt;
2120 redo:
2121 if (av_read_frame(c->fmt_in, &pkt) < 0) {
2122 if (c->stream->feed && c->stream->feed->feed_opened) {
2123 /* if coming from feed, it means we reached the end of the
2124 ffm file, so must wait for more data */
2125 c->state = HTTPSTATE_WAIT_FEED;
2126 return 1; /* state changed */
2127 } else {
2128 if (c->stream->loop) {
2129 av_close_input_file(c->fmt_in);
2130 c->fmt_in = NULL;
2131 if (open_input_stream(c, "") < 0)
2132 goto no_loop;
2133 goto redo;
2134 } else {
2135 no_loop:
2136 /* must send trailer now because eof or error */
2137 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2140 } else {
2141 int source_index = pkt.stream_index;
2142 /* update first pts if needed */
2143 if (c->first_pts == AV_NOPTS_VALUE) {
2144 c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
2145 c->start_time = cur_time;
2147 /* send it to the appropriate stream */
2148 if (c->stream->feed) {
2149 /* if coming from a feed, select the right stream */
2150 if (c->switch_pending) {
2151 c->switch_pending = 0;
2152 for(i=0;i<c->stream->nb_streams;i++) {
2153 if (c->switch_feed_streams[i] == pkt.stream_index)
2154 if (pkt.flags & PKT_FLAG_KEY)
2155 do_switch_stream(c, i);
2156 if (c->switch_feed_streams[i] >= 0)
2157 c->switch_pending = 1;
2160 for(i=0;i<c->stream->nb_streams;i++) {
2161 if (c->feed_streams[i] == pkt.stream_index) {
2162 AVStream *st = c->fmt_in->streams[source_index];
2163 pkt.stream_index = i;
2164 if (pkt.flags & PKT_FLAG_KEY &&
2165 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
2166 c->stream->nb_streams == 1))
2167 c->got_key_frame = 1;
2168 if (!c->stream->send_on_key || c->got_key_frame)
2169 goto send_it;
2172 } else {
2173 AVCodecContext *codec;
2174 AVStream *ist, *ost;
2175 send_it:
2176 ist = c->fmt_in->streams[source_index];
2177 /* specific handling for RTP: we use several
2178 output stream (one for each RTP
2179 connection). XXX: need more abstract handling */
2180 if (c->is_packetized) {
2181 /* compute send time and duration */
2182 c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
2183 if (ist->start_time != AV_NOPTS_VALUE)
2184 c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
2185 c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
2186 #if 0
2187 printf("index=%d pts=%0.3f duration=%0.6f\n",
2188 pkt.stream_index,
2189 (double)c->cur_pts /
2190 AV_TIME_BASE,
2191 (double)c->cur_frame_duration /
2192 AV_TIME_BASE);
2193 #endif
2194 /* find RTP context */
2195 c->packet_stream_index = pkt.stream_index;
2196 ctx = c->rtp_ctx[c->packet_stream_index];
2197 if(!ctx) {
2198 av_free_packet(&pkt);
2199 break;
2201 codec = ctx->streams[0]->codec;
2202 /* only one stream per RTP connection */
2203 pkt.stream_index = 0;
2204 } else {
2205 ctx = &c->fmt_ctx;
2206 /* Fudge here */
2207 codec = ctx->streams[pkt.stream_index]->codec;
2210 if (c->is_packetized) {
2211 int max_packet_size;
2212 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
2213 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
2214 else
2215 max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
2216 ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
2217 } else {
2218 ret = url_open_dyn_buf(&ctx->pb);
2220 if (ret < 0) {
2221 /* XXX: potential leak */
2222 return -1;
2224 ost = ctx->streams[pkt.stream_index];
2226 ctx->pb->is_streamed = 1;
2227 if (pkt.dts != AV_NOPTS_VALUE)
2228 pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
2229 if (pkt.pts != AV_NOPTS_VALUE)
2230 pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
2231 pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
2232 if (av_write_frame(ctx, &pkt) < 0) {
2233 http_log("Error writing frame to output\n");
2234 c->state = HTTPSTATE_SEND_DATA_TRAILER;
2237 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2238 c->cur_frame_bytes = len;
2239 c->buffer_ptr = c->pb_buffer;
2240 c->buffer_end = c->pb_buffer + len;
2242 codec->frame_number++;
2243 if (len == 0) {
2244 av_free_packet(&pkt);
2245 goto redo;
2248 av_free_packet(&pkt);
2251 break;
2252 default:
2253 case HTTPSTATE_SEND_DATA_TRAILER:
2254 /* last packet test ? */
2255 if (c->last_packet_sent || c->is_packetized)
2256 return -1;
2257 ctx = &c->fmt_ctx;
2258 /* prepare header */
2259 if (url_open_dyn_buf(&ctx->pb) < 0) {
2260 /* XXX: potential leak */
2261 return -1;
2263 c->fmt_ctx.pb->is_streamed = 1;
2264 av_write_trailer(ctx);
2265 len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2266 c->buffer_ptr = c->pb_buffer;
2267 c->buffer_end = c->pb_buffer + len;
2269 c->last_packet_sent = 1;
2270 break;
2272 return 0;
2275 /* should convert the format at the same time */
2276 /* send data starting at c->buffer_ptr to the output connection
2277 (either UDP or TCP connection) */
2278 static int http_send_data(HTTPContext *c)
2280 int len, ret;
2282 for(;;) {
2283 if (c->buffer_ptr >= c->buffer_end) {
2284 ret = http_prepare_data(c);
2285 if (ret < 0)
2286 return -1;
2287 else if (ret != 0)
2288 /* state change requested */
2289 break;
2290 } else {
2291 if (c->is_packetized) {
2292 /* RTP data output */
2293 len = c->buffer_end - c->buffer_ptr;
2294 if (len < 4) {
2295 /* fail safe - should never happen */
2296 fail1:
2297 c->buffer_ptr = c->buffer_end;
2298 return 0;
2300 len = (c->buffer_ptr[0] << 24) |
2301 (c->buffer_ptr[1] << 16) |
2302 (c->buffer_ptr[2] << 8) |
2303 (c->buffer_ptr[3]);
2304 if (len > (c->buffer_end - c->buffer_ptr))
2305 goto fail1;
2306 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
2307 /* nothing to send yet: we can wait */
2308 return 0;
2311 c->data_count += len;
2312 update_datarate(&c->datarate, c->data_count);
2313 if (c->stream)
2314 c->stream->bytes_served += len;
2316 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
2317 /* RTP packets are sent inside the RTSP TCP connection */
2318 ByteIOContext *pb;
2319 int interleaved_index, size;
2320 uint8_t header[4];
2321 HTTPContext *rtsp_c;
2323 rtsp_c = c->rtsp_c;
2324 /* if no RTSP connection left, error */
2325 if (!rtsp_c)
2326 return -1;
2327 /* if already sending something, then wait. */
2328 if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
2329 break;
2330 if (url_open_dyn_buf(&pb) < 0)
2331 goto fail1;
2332 interleaved_index = c->packet_stream_index * 2;
2333 /* RTCP packets are sent at odd indexes */
2334 if (c->buffer_ptr[1] == 200)
2335 interleaved_index++;
2336 /* write RTSP TCP header */
2337 header[0] = '$';
2338 header[1] = interleaved_index;
2339 header[2] = len >> 8;
2340 header[3] = len;
2341 put_buffer(pb, header, 4);
2342 /* write RTP packet data */
2343 c->buffer_ptr += 4;
2344 put_buffer(pb, c->buffer_ptr, len);
2345 size = url_close_dyn_buf(pb, &c->packet_buffer);
2346 /* prepare asynchronous TCP sending */
2347 rtsp_c->packet_buffer_ptr = c->packet_buffer;
2348 rtsp_c->packet_buffer_end = c->packet_buffer + size;
2349 c->buffer_ptr += len;
2351 /* send everything we can NOW */
2352 len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
2353 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
2354 if (len > 0)
2355 rtsp_c->packet_buffer_ptr += len;
2356 if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
2357 /* if we could not send all the data, we will
2358 send it later, so a new state is needed to
2359 "lock" the RTSP TCP connection */
2360 rtsp_c->state = RTSPSTATE_SEND_PACKET;
2361 break;
2362 } else
2363 /* all data has been sent */
2364 av_freep(&c->packet_buffer);
2365 } else {
2366 /* send RTP packet directly in UDP */
2367 c->buffer_ptr += 4;
2368 url_write(c->rtp_handles[c->packet_stream_index],
2369 c->buffer_ptr, len);
2370 c->buffer_ptr += len;
2371 /* here we continue as we can send several packets per 10 ms slot */
2373 } else {
2374 /* TCP data output */
2375 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2376 if (len < 0) {
2377 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2378 ff_neterrno() != FF_NETERROR(EINTR))
2379 /* error : close connection */
2380 return -1;
2381 else
2382 return 0;
2383 } else
2384 c->buffer_ptr += len;
2386 c->data_count += len;
2387 update_datarate(&c->datarate, c->data_count);
2388 if (c->stream)
2389 c->stream->bytes_served += len;
2390 break;
2393 } /* for(;;) */
2394 return 0;
2397 static int http_start_receive_data(HTTPContext *c)
2399 int fd;
2401 if (c->stream->feed_opened)
2402 return -1;
2404 /* Don't permit writing to this one */
2405 if (c->stream->readonly)
2406 return -1;
2408 /* open feed */
2409 fd = open(c->stream->feed_filename, O_RDWR);
2410 if (fd < 0) {
2411 http_log("Error opening feeder file: %s\n", strerror(errno));
2412 return -1;
2414 c->feed_fd = fd;
2416 c->stream->feed_write_index = ffm_read_write_index(fd);
2417 c->stream->feed_size = lseek(fd, 0, SEEK_END);
2418 lseek(fd, 0, SEEK_SET);
2420 /* init buffer input */
2421 c->buffer_ptr = c->buffer;
2422 c->buffer_end = c->buffer + FFM_PACKET_SIZE;
2423 c->stream->feed_opened = 1;
2424 return 0;
2427 static int http_receive_data(HTTPContext *c)
2429 HTTPContext *c1;
2431 if (c->buffer_end > c->buffer_ptr) {
2432 int len;
2434 len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2435 if (len < 0) {
2436 if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
2437 ff_neterrno() != FF_NETERROR(EINTR))
2438 /* error : close connection */
2439 goto fail;
2440 } else if (len == 0)
2441 /* end of connection : close it */
2442 goto fail;
2443 else {
2444 c->buffer_ptr += len;
2445 c->data_count += len;
2446 update_datarate(&c->datarate, c->data_count);
2450 if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
2451 if (c->buffer[0] != 'f' ||
2452 c->buffer[1] != 'm') {
2453 http_log("Feed stream has become desynchronized -- disconnecting\n");
2454 goto fail;
2458 if (c->buffer_ptr >= c->buffer_end) {
2459 FFStream *feed = c->stream;
2460 /* a packet has been received : write it in the store, except
2461 if header */
2462 if (c->data_count > FFM_PACKET_SIZE) {
2464 // printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
2465 /* XXX: use llseek or url_seek */
2466 lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
2467 if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
2468 http_log("Error writing to feed file: %s\n", strerror(errno));
2469 goto fail;
2472 feed->feed_write_index += FFM_PACKET_SIZE;
2473 /* update file size */
2474 if (feed->feed_write_index > c->stream->feed_size)
2475 feed->feed_size = feed->feed_write_index;
2477 /* handle wrap around if max file size reached */
2478 if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
2479 feed->feed_write_index = FFM_PACKET_SIZE;
2481 /* write index */
2482 ffm_write_write_index(c->feed_fd, feed->feed_write_index);
2484 /* wake up any waiting connections */
2485 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2486 if (c1->state == HTTPSTATE_WAIT_FEED &&
2487 c1->stream->feed == c->stream->feed)
2488 c1->state = HTTPSTATE_SEND_DATA;
2490 } else {
2491 /* We have a header in our hands that contains useful data */
2492 AVFormatContext *s = NULL;
2493 ByteIOContext *pb;
2494 AVInputFormat *fmt_in;
2495 int i;
2497 /* use feed output format name to find corresponding input format */
2498 fmt_in = av_find_input_format(feed->fmt->name);
2499 if (!fmt_in)
2500 goto fail;
2502 url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
2503 pb->is_streamed = 1;
2505 if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
2506 av_free(pb);
2507 goto fail;
2510 /* Now we have the actual streams */
2511 if (s->nb_streams != feed->nb_streams) {
2512 av_close_input_stream(s);
2513 av_free(pb);
2514 goto fail;
2517 for (i = 0; i < s->nb_streams; i++) {
2518 AVStream *fst = feed->streams[i];
2519 AVStream *st = s->streams[i];
2520 memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
2521 if (fst->codec->extradata_size) {
2522 fst->codec->extradata = av_malloc(fst->codec->extradata_size);
2523 if (!fst->codec->extradata)
2524 goto fail;
2525 memcpy(fst->codec->extradata, st->codec->extradata,
2526 fst->codec->extradata_size);
2530 av_close_input_stream(s);
2531 av_free(pb);
2533 c->buffer_ptr = c->buffer;
2536 return 0;
2537 fail:
2538 c->stream->feed_opened = 0;
2539 close(c->feed_fd);
2540 /* wake up any waiting connections to stop waiting for feed */
2541 for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
2542 if (c1->state == HTTPSTATE_WAIT_FEED &&
2543 c1->stream->feed == c->stream->feed)
2544 c1->state = HTTPSTATE_SEND_DATA_TRAILER;
2546 return -1;
2549 /********************************************************************/
2550 /* RTSP handling */
2552 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
2554 const char *str;
2555 time_t ti;
2556 char *p;
2557 char buf2[32];
2559 switch(error_number) {
2560 case RTSP_STATUS_OK:
2561 str = "OK";
2562 break;
2563 case RTSP_STATUS_METHOD:
2564 str = "Method Not Allowed";
2565 break;
2566 case RTSP_STATUS_BANDWIDTH:
2567 str = "Not Enough Bandwidth";
2568 break;
2569 case RTSP_STATUS_SESSION:
2570 str = "Session Not Found";
2571 break;
2572 case RTSP_STATUS_STATE:
2573 str = "Method Not Valid in This State";
2574 break;
2575 case RTSP_STATUS_AGGREGATE:
2576 str = "Aggregate operation not allowed";
2577 break;
2578 case RTSP_STATUS_ONLY_AGGREGATE:
2579 str = "Only aggregate operation allowed";
2580 break;
2581 case RTSP_STATUS_TRANSPORT:
2582 str = "Unsupported transport";
2583 break;
2584 case RTSP_STATUS_INTERNAL:
2585 str = "Internal Server Error";
2586 break;
2587 case RTSP_STATUS_SERVICE:
2588 str = "Service Unavailable";
2589 break;
2590 case RTSP_STATUS_VERSION:
2591 str = "RTSP Version not supported";
2592 break;
2593 default:
2594 str = "Unknown Error";
2595 break;
2598 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
2599 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2601 /* output GMT time */
2602 ti = time(NULL);
2603 p = ctime(&ti);
2604 strcpy(buf2, p);
2605 p = buf2 + strlen(p) - 1;
2606 if (*p == '\n')
2607 *p = '\0';
2608 url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
2611 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
2613 rtsp_reply_header(c, error_number);
2614 url_fprintf(c->pb, "\r\n");
2617 static int rtsp_parse_request(HTTPContext *c)
2619 const char *p, *p1, *p2;
2620 char cmd[32];
2621 char url[1024];
2622 char protocol[32];
2623 char line[1024];
2624 int len;
2625 RTSPHeader header1, *header = &header1;
2627 c->buffer_ptr[0] = '\0';
2628 p = c->buffer;
2630 get_word(cmd, sizeof(cmd), &p);
2631 get_word(url, sizeof(url), &p);
2632 get_word(protocol, sizeof(protocol), &p);
2634 av_strlcpy(c->method, cmd, sizeof(c->method));
2635 av_strlcpy(c->url, url, sizeof(c->url));
2636 av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2638 if (url_open_dyn_buf(&c->pb) < 0) {
2639 /* XXX: cannot do more */
2640 c->pb = NULL; /* safety */
2641 return -1;
2644 /* check version name */
2645 if (strcmp(protocol, "RTSP/1.0") != 0) {
2646 rtsp_reply_error(c, RTSP_STATUS_VERSION);
2647 goto the_end;
2650 /* parse each header line */
2651 memset(header, 0, sizeof(RTSPHeader));
2652 /* skip to next line */
2653 while (*p != '\n' && *p != '\0')
2654 p++;
2655 if (*p == '\n')
2656 p++;
2657 while (*p != '\0') {
2658 p1 = strchr(p, '\n');
2659 if (!p1)
2660 break;
2661 p2 = p1;
2662 if (p2 > p && p2[-1] == '\r')
2663 p2--;
2664 /* skip empty line */
2665 if (p2 == p)
2666 break;
2667 len = p2 - p;
2668 if (len > sizeof(line) - 1)
2669 len = sizeof(line) - 1;
2670 memcpy(line, p, len);
2671 line[len] = '\0';
2672 rtsp_parse_line(header, line);
2673 p = p1 + 1;
2676 /* handle sequence number */
2677 c->seq = header->seq;
2679 if (!strcmp(cmd, "DESCRIBE"))
2680 rtsp_cmd_describe(c, url);
2681 else if (!strcmp(cmd, "OPTIONS"))
2682 rtsp_cmd_options(c, url);
2683 else if (!strcmp(cmd, "SETUP"))
2684 rtsp_cmd_setup(c, url, header);
2685 else if (!strcmp(cmd, "PLAY"))
2686 rtsp_cmd_play(c, url, header);
2687 else if (!strcmp(cmd, "PAUSE"))
2688 rtsp_cmd_pause(c, url, header);
2689 else if (!strcmp(cmd, "TEARDOWN"))
2690 rtsp_cmd_teardown(c, url, header);
2691 else
2692 rtsp_reply_error(c, RTSP_STATUS_METHOD);
2694 the_end:
2695 len = url_close_dyn_buf(c->pb, &c->pb_buffer);
2696 c->pb = NULL; /* safety */
2697 if (len < 0) {
2698 /* XXX: cannot do more */
2699 return -1;
2701 c->buffer_ptr = c->pb_buffer;
2702 c->buffer_end = c->pb_buffer + len;
2703 c->state = RTSPSTATE_SEND_REPLY;
2704 return 0;
2707 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
2708 struct in_addr my_ip)
2710 AVFormatContext *avc;
2711 AVStream avs[MAX_STREAMS];
2712 int i;
2714 avc = av_alloc_format_context();
2715 if (avc == NULL) {
2716 return -1;
2718 if (stream->title[0] != 0) {
2719 av_strlcpy(avc->title, stream->title, sizeof(avc->title));
2720 } else {
2721 av_strlcpy(avc->title, "No Title", sizeof(avc->title));
2723 avc->nb_streams = stream->nb_streams;
2724 if (stream->is_multicast) {
2725 snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
2726 inet_ntoa(stream->multicast_ip),
2727 stream->multicast_port, stream->multicast_ttl);
2730 for(i = 0; i < stream->nb_streams; i++) {
2731 avc->streams[i] = &avs[i];
2732 avc->streams[i]->codec = stream->streams[i]->codec;
2734 *pbuffer = av_mallocz(2048);
2735 avf_sdp_create(&avc, 1, *pbuffer, 2048);
2736 av_free(avc);
2738 return strlen(*pbuffer);
2741 static void rtsp_cmd_options(HTTPContext *c, const char *url)
2743 // rtsp_reply_header(c, RTSP_STATUS_OK);
2744 url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
2745 url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
2746 url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
2747 url_fprintf(c->pb, "\r\n");
2750 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
2752 FFStream *stream;
2753 char path1[1024];
2754 const char *path;
2755 uint8_t *content;
2756 int content_length, len;
2757 struct sockaddr_in my_addr;
2759 /* find which url is asked */
2760 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2761 path = path1;
2762 if (*path == '/')
2763 path++;
2765 for(stream = first_stream; stream != NULL; stream = stream->next) {
2766 if (!stream->is_feed &&
2767 stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2768 !strcmp(path, stream->filename)) {
2769 goto found;
2772 /* no stream found */
2773 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2774 return;
2776 found:
2777 /* prepare the media description in sdp format */
2779 /* get the host IP */
2780 len = sizeof(my_addr);
2781 getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
2782 content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2783 if (content_length < 0) {
2784 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2785 return;
2787 rtsp_reply_header(c, RTSP_STATUS_OK);
2788 url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
2789 url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
2790 url_fprintf(c->pb, "\r\n");
2791 put_buffer(c->pb, content, content_length);
2794 static HTTPContext *find_rtp_session(const char *session_id)
2796 HTTPContext *c;
2798 if (session_id[0] == '\0')
2799 return NULL;
2801 for(c = first_http_ctx; c != NULL; c = c->next) {
2802 if (!strcmp(c->session_id, session_id))
2803 return c;
2805 return NULL;
2808 static RTSPTransportField *find_transport(RTSPHeader *h, enum RTSPLowerTransport lower_transport)
2810 RTSPTransportField *th;
2811 int i;
2813 for(i=0;i<h->nb_transports;i++) {
2814 th = &h->transports[i];
2815 if (th->lower_transport == lower_transport)
2816 return th;
2818 return NULL;
2821 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
2822 RTSPHeader *h)
2824 FFStream *stream;
2825 int stream_index, port;
2826 char buf[1024];
2827 char path1[1024];
2828 const char *path;
2829 HTTPContext *rtp_c;
2830 RTSPTransportField *th;
2831 struct sockaddr_in dest_addr;
2832 RTSPActionServerSetup setup;
2834 /* find which url is asked */
2835 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2836 path = path1;
2837 if (*path == '/')
2838 path++;
2840 /* now check each stream */
2841 for(stream = first_stream; stream != NULL; stream = stream->next) {
2842 if (!stream->is_feed &&
2843 stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2844 /* accept aggregate filenames only if single stream */
2845 if (!strcmp(path, stream->filename)) {
2846 if (stream->nb_streams != 1) {
2847 rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
2848 return;
2850 stream_index = 0;
2851 goto found;
2854 for(stream_index = 0; stream_index < stream->nb_streams;
2855 stream_index++) {
2856 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2857 stream->filename, stream_index);
2858 if (!strcmp(path, buf))
2859 goto found;
2863 /* no stream found */
2864 rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
2865 return;
2866 found:
2868 /* generate session id if needed */
2869 if (h->session_id[0] == '\0')
2870 snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
2871 av_random(&random_state), av_random(&random_state));
2873 /* find rtp session, and create it if none found */
2874 rtp_c = find_rtp_session(h->session_id);
2875 if (!rtp_c) {
2876 /* always prefer UDP */
2877 th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
2878 if (!th) {
2879 th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
2880 if (!th) {
2881 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2882 return;
2886 rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
2887 th->lower_transport);
2888 if (!rtp_c) {
2889 rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
2890 return;
2893 /* open input stream */
2894 if (open_input_stream(rtp_c, "") < 0) {
2895 rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
2896 return;
2900 /* test if stream is OK (test needed because several SETUP needs
2901 to be done for a given file) */
2902 if (rtp_c->stream != stream) {
2903 rtsp_reply_error(c, RTSP_STATUS_SERVICE);
2904 return;
2907 /* test if stream is already set up */
2908 if (rtp_c->rtp_ctx[stream_index]) {
2909 rtsp_reply_error(c, RTSP_STATUS_STATE);
2910 return;
2913 /* check transport */
2914 th = find_transport(h, rtp_c->rtp_protocol);
2915 if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
2916 th->client_port_min <= 0)) {
2917 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2918 return;
2921 /* setup default options */
2922 setup.transport_option[0] = '\0';
2923 dest_addr = rtp_c->from_addr;
2924 dest_addr.sin_port = htons(th->client_port_min);
2926 /* setup stream */
2927 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2928 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
2929 return;
2932 /* now everything is OK, so we can send the connection parameters */
2933 rtsp_reply_header(c, RTSP_STATUS_OK);
2934 /* session ID */
2935 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2937 switch(rtp_c->rtp_protocol) {
2938 case RTSP_LOWER_TRANSPORT_UDP:
2939 port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
2940 url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2941 "client_port=%d-%d;server_port=%d-%d",
2942 th->client_port_min, th->client_port_min + 1,
2943 port, port + 1);
2944 break;
2945 case RTSP_LOWER_TRANSPORT_TCP:
2946 url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2947 stream_index * 2, stream_index * 2 + 1);
2948 break;
2949 default:
2950 break;
2952 if (setup.transport_option[0] != '\0')
2953 url_fprintf(c->pb, ";%s", setup.transport_option);
2954 url_fprintf(c->pb, "\r\n");
2957 url_fprintf(c->pb, "\r\n");
2961 /* find an rtp connection by using the session ID. Check consistency
2962 with filename */
2963 static HTTPContext *find_rtp_session_with_url(const char *url,
2964 const char *session_id)
2966 HTTPContext *rtp_c;
2967 char path1[1024];
2968 const char *path;
2969 char buf[1024];
2970 int s;
2972 rtp_c = find_rtp_session(session_id);
2973 if (!rtp_c)
2974 return NULL;
2976 /* find which url is asked */
2977 url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2978 path = path1;
2979 if (*path == '/')
2980 path++;
2981 if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
2982 for(s=0; s<rtp_c->stream->nb_streams; ++s) {
2983 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2984 rtp_c->stream->filename, s);
2985 if(!strncmp(path, buf, sizeof(buf))) {
2986 // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
2987 return rtp_c;
2990 return NULL;
2993 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPHeader *h)
2995 HTTPContext *rtp_c;
2997 rtp_c = find_rtp_session_with_url(url, h->session_id);
2998 if (!rtp_c) {
2999 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3000 return;
3003 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3004 rtp_c->state != HTTPSTATE_WAIT_FEED &&
3005 rtp_c->state != HTTPSTATE_READY) {
3006 rtsp_reply_error(c, RTSP_STATUS_STATE);
3007 return;
3010 #if 0
3011 /* XXX: seek in stream */
3012 if (h->range_start != AV_NOPTS_VALUE) {
3013 printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
3014 av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
3016 #endif
3018 rtp_c->state = HTTPSTATE_SEND_DATA;
3020 /* now everything is OK, so we can send the connection parameters */
3021 rtsp_reply_header(c, RTSP_STATUS_OK);
3022 /* session ID */
3023 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3024 url_fprintf(c->pb, "\r\n");
3027 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPHeader *h)
3029 HTTPContext *rtp_c;
3031 rtp_c = find_rtp_session_with_url(url, h->session_id);
3032 if (!rtp_c) {
3033 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3034 return;
3037 if (rtp_c->state != HTTPSTATE_SEND_DATA &&
3038 rtp_c->state != HTTPSTATE_WAIT_FEED) {
3039 rtsp_reply_error(c, RTSP_STATUS_STATE);
3040 return;
3043 rtp_c->state = HTTPSTATE_READY;
3044 rtp_c->first_pts = AV_NOPTS_VALUE;
3045 /* now everything is OK, so we can send the connection parameters */
3046 rtsp_reply_header(c, RTSP_STATUS_OK);
3047 /* session ID */
3048 url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
3049 url_fprintf(c->pb, "\r\n");
3052 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPHeader *h)
3054 HTTPContext *rtp_c;
3055 char session_id[32];
3057 rtp_c = find_rtp_session_with_url(url, h->session_id);
3058 if (!rtp_c) {
3059 rtsp_reply_error(c, RTSP_STATUS_SESSION);
3060 return;
3063 av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
3065 /* abort the session */
3066 close_connection(rtp_c);
3068 /* now everything is OK, so we can send the connection parameters */
3069 rtsp_reply_header(c, RTSP_STATUS_OK);
3070 /* session ID */
3071 url_fprintf(c->pb, "Session: %s\r\n", session_id);
3072 url_fprintf(c->pb, "\r\n");
3076 /********************************************************************/
3077 /* RTP handling */
3079 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
3080 FFStream *stream, const char *session_id,
3081 enum RTSPLowerTransport rtp_protocol)
3083 HTTPContext *c = NULL;
3084 const char *proto_str;
3086 /* XXX: should output a warning page when coming
3087 close to the connection limit */
3088 if (nb_connections >= nb_max_connections)
3089 goto fail;
3091 /* add a new connection */
3092 c = av_mallocz(sizeof(HTTPContext));
3093 if (!c)
3094 goto fail;
3096 c->fd = -1;
3097 c->poll_entry = NULL;
3098 c->from_addr = *from_addr;
3099 c->buffer_size = IOBUFFER_INIT_SIZE;
3100 c->buffer = av_malloc(c->buffer_size);
3101 if (!c->buffer)
3102 goto fail;
3103 nb_connections++;
3104 c->stream = stream;
3105 av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
3106 c->state = HTTPSTATE_READY;
3107 c->is_packetized = 1;
3108 c->rtp_protocol = rtp_protocol;
3110 /* protocol is shown in statistics */
3111 switch(c->rtp_protocol) {
3112 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3113 proto_str = "MCAST";
3114 break;
3115 case RTSP_LOWER_TRANSPORT_UDP:
3116 proto_str = "UDP";
3117 break;
3118 case RTSP_LOWER_TRANSPORT_TCP:
3119 proto_str = "TCP";
3120 break;
3121 default:
3122 proto_str = "???";
3123 break;
3125 av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
3126 av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
3128 current_bandwidth += stream->bandwidth;
3130 c->next = first_http_ctx;
3131 first_http_ctx = c;
3132 return c;
3134 fail:
3135 if (c) {
3136 av_free(c->buffer);
3137 av_free(c);
3139 return NULL;
3142 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
3143 command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
3144 used. */
3145 static int rtp_new_av_stream(HTTPContext *c,
3146 int stream_index, struct sockaddr_in *dest_addr,
3147 HTTPContext *rtsp_c)
3149 AVFormatContext *ctx;
3150 AVStream *st;
3151 char *ipaddr;
3152 URLContext *h = NULL;
3153 uint8_t *dummy_buf;
3154 int max_packet_size;
3156 /* now we can open the relevant output stream */
3157 ctx = av_alloc_format_context();
3158 if (!ctx)
3159 return -1;
3160 ctx->oformat = guess_format("rtp", NULL, NULL);
3162 st = av_mallocz(sizeof(AVStream));
3163 if (!st)
3164 goto fail;
3165 st->codec= avcodec_alloc_context();
3166 ctx->nb_streams = 1;
3167 ctx->streams[0] = st;
3169 if (!c->stream->feed ||
3170 c->stream->feed == c->stream)
3171 memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
3172 else
3173 memcpy(st,
3174 c->stream->feed->streams[c->stream->feed_streams[stream_index]],
3175 sizeof(AVStream));
3176 st->priv_data = NULL;
3178 /* build destination RTP address */
3179 ipaddr = inet_ntoa(dest_addr->sin_addr);
3181 switch(c->rtp_protocol) {
3182 case RTSP_LOWER_TRANSPORT_UDP:
3183 case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
3184 /* RTP/UDP case */
3186 /* XXX: also pass as parameter to function ? */
3187 if (c->stream->is_multicast) {
3188 int ttl;
3189 ttl = c->stream->multicast_ttl;
3190 if (!ttl)
3191 ttl = 16;
3192 snprintf(ctx->filename, sizeof(ctx->filename),
3193 "rtp://%s:%d?multicast=1&ttl=%d",
3194 ipaddr, ntohs(dest_addr->sin_port), ttl);
3195 } else {
3196 snprintf(ctx->filename, sizeof(ctx->filename),
3197 "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
3200 if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
3201 goto fail;
3202 c->rtp_handles[stream_index] = h;
3203 max_packet_size = url_get_max_packet_size(h);
3204 break;
3205 case RTSP_LOWER_TRANSPORT_TCP:
3206 /* RTP/TCP case */
3207 c->rtsp_c = rtsp_c;
3208 max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
3209 break;
3210 default:
3211 goto fail;
3214 http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
3215 ipaddr, ntohs(dest_addr->sin_port),
3216 c->stream->filename, stream_index, c->protocol);
3218 /* normally, no packets should be output here, but the packet size may be checked */
3219 if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
3220 /* XXX: close stream */
3221 goto fail;
3223 av_set_parameters(ctx, NULL);
3224 if (av_write_header(ctx) < 0) {
3225 fail:
3226 if (h)
3227 url_close(h);
3228 av_free(ctx);
3229 return -1;
3231 url_close_dyn_buf(ctx->pb, &dummy_buf);
3232 av_free(dummy_buf);
3234 c->rtp_ctx[stream_index] = ctx;
3235 return 0;
3238 /********************************************************************/
3239 /* ffserver initialization */
3241 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
3243 AVStream *fst;
3245 fst = av_mallocz(sizeof(AVStream));
3246 if (!fst)
3247 return NULL;
3248 fst->codec= avcodec_alloc_context();
3249 fst->priv_data = av_mallocz(sizeof(FeedData));
3250 memcpy(fst->codec, codec, sizeof(AVCodecContext));
3251 fst->index = stream->nb_streams;
3252 av_set_pts_info(fst, 33, 1, 90000);
3253 stream->streams[stream->nb_streams++] = fst;
3254 return fst;
3257 /* return the stream number in the feed */
3258 static int add_av_stream(FFStream *feed, AVStream *st)
3260 AVStream *fst;
3261 AVCodecContext *av, *av1;
3262 int i;
3264 av = st->codec;
3265 for(i=0;i<feed->nb_streams;i++) {
3266 st = feed->streams[i];
3267 av1 = st->codec;
3268 if (av1->codec_id == av->codec_id &&
3269 av1->codec_type == av->codec_type &&
3270 av1->bit_rate == av->bit_rate) {
3272 switch(av->codec_type) {
3273 case CODEC_TYPE_AUDIO:
3274 if (av1->channels == av->channels &&
3275 av1->sample_rate == av->sample_rate)
3276 goto found;
3277 break;
3278 case CODEC_TYPE_VIDEO:
3279 if (av1->width == av->width &&
3280 av1->height == av->height &&
3281 av1->time_base.den == av->time_base.den &&
3282 av1->time_base.num == av->time_base.num &&
3283 av1->gop_size == av->gop_size)
3284 goto found;
3285 break;
3286 default:
3287 abort();
3292 fst = add_av_stream1(feed, av);
3293 if (!fst)
3294 return -1;
3295 return feed->nb_streams - 1;
3296 found:
3297 return i;
3300 static void remove_stream(FFStream *stream)
3302 FFStream **ps;
3303 ps = &first_stream;
3304 while (*ps != NULL) {
3305 if (*ps == stream)
3306 *ps = (*ps)->next;
3307 else
3308 ps = &(*ps)->next;
3312 /* specific mpeg4 handling : we extract the raw parameters */
3313 static void extract_mpeg4_header(AVFormatContext *infile)
3315 int mpeg4_count, i, size;
3316 AVPacket pkt;
3317 AVStream *st;
3318 const uint8_t *p;
3320 mpeg4_count = 0;
3321 for(i=0;i<infile->nb_streams;i++) {
3322 st = infile->streams[i];
3323 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3324 st->codec->extradata_size == 0) {
3325 mpeg4_count++;
3328 if (!mpeg4_count)
3329 return;
3331 printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
3332 while (mpeg4_count > 0) {
3333 if (av_read_packet(infile, &pkt) < 0)
3334 break;
3335 st = infile->streams[pkt.stream_index];
3336 if (st->codec->codec_id == CODEC_ID_MPEG4 &&
3337 st->codec->extradata_size == 0) {
3338 av_freep(&st->codec->extradata);
3339 /* fill extradata with the header */
3340 /* XXX: we make hard suppositions here ! */
3341 p = pkt.data;
3342 while (p < pkt.data + pkt.size - 4) {
3343 /* stop when vop header is found */
3344 if (p[0] == 0x00 && p[1] == 0x00 &&
3345 p[2] == 0x01 && p[3] == 0xb6) {
3346 size = p - pkt.data;
3347 // av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
3348 st->codec->extradata = av_malloc(size);
3349 st->codec->extradata_size = size;
3350 memcpy(st->codec->extradata, pkt.data, size);
3351 break;
3353 p++;
3355 mpeg4_count--;
3357 av_free_packet(&pkt);
3361 /* compute the needed AVStream for each file */
3362 static void build_file_streams(void)
3364 FFStream *stream, *stream_next;
3365 AVFormatContext *infile;
3366 int i, ret;
3368 /* gather all streams */
3369 for(stream = first_stream; stream != NULL; stream = stream_next) {
3370 stream_next = stream->next;
3371 if (stream->stream_type == STREAM_TYPE_LIVE &&
3372 !stream->feed) {
3373 /* the stream comes from a file */
3374 /* try to open the file */
3375 /* open stream */
3376 stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
3377 if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
3378 /* specific case : if transport stream output to RTP,
3379 we use a raw transport stream reader */
3380 stream->ap_in->mpeg2ts_raw = 1;
3381 stream->ap_in->mpeg2ts_compute_pcr = 1;
3384 if ((ret = av_open_input_file(&infile, stream->feed_filename,
3385 stream->ifmt, 0, stream->ap_in)) < 0) {
3386 http_log("could not open %s: %d\n", stream->feed_filename, ret);
3387 /* remove stream (no need to spend more time on it) */
3388 fail:
3389 remove_stream(stream);
3390 } else {
3391 /* find all the AVStreams inside and reference them in
3392 'stream' */
3393 if (av_find_stream_info(infile) < 0) {
3394 http_log("Could not find codec parameters from '%s'\n",
3395 stream->feed_filename);
3396 av_close_input_file(infile);
3397 goto fail;
3399 extract_mpeg4_header(infile);
3401 for(i=0;i<infile->nb_streams;i++)
3402 add_av_stream1(stream, infile->streams[i]->codec);
3404 av_close_input_file(infile);
3410 /* compute the needed AVStream for each feed */
3411 static void build_feed_streams(void)
3413 FFStream *stream, *feed;
3414 int i;
3416 /* gather all streams */
3417 for(stream = first_stream; stream != NULL; stream = stream->next) {
3418 feed = stream->feed;
3419 if (feed) {
3420 if (!stream->is_feed) {
3421 /* we handle a stream coming from a feed */
3422 for(i=0;i<stream->nb_streams;i++)
3423 stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
3428 /* gather all streams */
3429 for(stream = first_stream; stream != NULL; stream = stream->next) {
3430 feed = stream->feed;
3431 if (feed) {
3432 if (stream->is_feed) {
3433 for(i=0;i<stream->nb_streams;i++)
3434 stream->feed_streams[i] = i;
3439 /* create feed files if needed */
3440 for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
3441 int fd;
3443 if (url_exist(feed->feed_filename)) {
3444 /* See if it matches */
3445 AVFormatContext *s;
3446 int matches = 0;
3448 if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
3449 /* Now see if it matches */
3450 if (s->nb_streams == feed->nb_streams) {
3451 matches = 1;
3452 for(i=0;i<s->nb_streams;i++) {
3453 AVStream *sf, *ss;
3454 sf = feed->streams[i];
3455 ss = s->streams[i];
3457 if (sf->index != ss->index ||
3458 sf->id != ss->id) {
3459 http_log("Index & Id do not match for stream %d (%s)\n",
3460 i, feed->feed_filename);
3461 matches = 0;
3462 } else {
3463 AVCodecContext *ccf, *ccs;
3465 ccf = sf->codec;
3466 ccs = ss->codec;
3467 #define CHECK_CODEC(x) (ccf->x != ccs->x)
3469 if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
3470 http_log("Codecs do not match for stream %d\n", i);
3471 matches = 0;
3472 } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
3473 http_log("Codec bitrates do not match for stream %d\n", i);
3474 matches = 0;
3475 } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
3476 if (CHECK_CODEC(time_base.den) ||
3477 CHECK_CODEC(time_base.num) ||
3478 CHECK_CODEC(width) ||
3479 CHECK_CODEC(height)) {
3480 http_log("Codec width, height and framerate do not match for stream %d\n", i);
3481 matches = 0;
3483 } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
3484 if (CHECK_CODEC(sample_rate) ||
3485 CHECK_CODEC(channels) ||
3486 CHECK_CODEC(frame_size)) {
3487 http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
3488 matches = 0;
3490 } else {
3491 http_log("Unknown codec type\n");
3492 matches = 0;
3495 if (!matches)
3496 break;
3498 } else
3499 http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
3500 feed->feed_filename, s->nb_streams, feed->nb_streams);
3502 av_close_input_file(s);
3503 } else
3504 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
3505 feed->feed_filename);
3507 if (!matches) {
3508 if (feed->readonly) {
3509 http_log("Unable to delete feed file '%s' as it is marked readonly\n",
3510 feed->feed_filename);
3511 exit(1);
3513 unlink(feed->feed_filename);
3516 if (!url_exist(feed->feed_filename)) {
3517 AVFormatContext s1 = {0}, *s = &s1;
3519 if (feed->readonly) {
3520 http_log("Unable to create feed file '%s' as it is marked readonly\n",
3521 feed->feed_filename);
3522 exit(1);
3525 /* only write the header of the ffm file */
3526 if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
3527 http_log("Could not open output feed file '%s'\n",
3528 feed->feed_filename);
3529 exit(1);
3531 s->oformat = feed->fmt;
3532 s->nb_streams = feed->nb_streams;
3533 for(i=0;i<s->nb_streams;i++) {
3534 AVStream *st;
3535 st = feed->streams[i];
3536 s->streams[i] = st;
3538 av_set_parameters(s, NULL);
3539 if (av_write_header(s) < 0) {
3540 http_log("Container doesn't supports the required parameters\n");
3541 exit(1);
3543 /* XXX: need better api */
3544 av_freep(&s->priv_data);
3545 url_fclose(s->pb);
3547 /* get feed size and write index */
3548 fd = open(feed->feed_filename, O_RDONLY);
3549 if (fd < 0) {
3550 http_log("Could not open output feed file '%s'\n",
3551 feed->feed_filename);
3552 exit(1);
3555 feed->feed_write_index = ffm_read_write_index(fd);
3556 feed->feed_size = lseek(fd, 0, SEEK_END);
3557 /* ensure that we do not wrap before the end of file */
3558 if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
3559 feed->feed_max_size = feed->feed_size;
3561 close(fd);
3565 /* compute the bandwidth used by each stream */
3566 static void compute_bandwidth(void)
3568 unsigned bandwidth;
3569 int i;
3570 FFStream *stream;
3572 for(stream = first_stream; stream != NULL; stream = stream->next) {
3573 bandwidth = 0;
3574 for(i=0;i<stream->nb_streams;i++) {
3575 AVStream *st = stream->streams[i];
3576 switch(st->codec->codec_type) {
3577 case CODEC_TYPE_AUDIO:
3578 case CODEC_TYPE_VIDEO:
3579 bandwidth += st->codec->bit_rate;
3580 break;
3581 default:
3582 break;
3585 stream->bandwidth = (bandwidth + 999) / 1000;
3589 static void get_arg(char *buf, int buf_size, const char **pp)
3591 const char *p;
3592 char *q;
3593 int quote;
3595 p = *pp;
3596 while (isspace(*p)) p++;
3597 q = buf;
3598 quote = 0;
3599 if (*p == '\"' || *p == '\'')
3600 quote = *p++;
3601 for(;;) {
3602 if (quote) {
3603 if (*p == quote)
3604 break;
3605 } else {
3606 if (isspace(*p))
3607 break;
3609 if (*p == '\0')
3610 break;
3611 if ((q - buf) < buf_size - 1)
3612 *q++ = *p;
3613 p++;
3615 *q = '\0';
3616 if (quote && *p == quote)
3617 p++;
3618 *pp = p;
3621 /* add a codec and set the default parameters */
3622 static void add_codec(FFStream *stream, AVCodecContext *av)
3624 AVStream *st;
3626 /* compute default parameters */
3627 switch(av->codec_type) {
3628 case CODEC_TYPE_AUDIO:
3629 if (av->bit_rate == 0)
3630 av->bit_rate = 64000;
3631 if (av->sample_rate == 0)
3632 av->sample_rate = 22050;
3633 if (av->channels == 0)
3634 av->channels = 1;
3635 break;
3636 case CODEC_TYPE_VIDEO:
3637 if (av->bit_rate == 0)
3638 av->bit_rate = 64000;
3639 if (av->time_base.num == 0){
3640 av->time_base.den = 5;
3641 av->time_base.num = 1;
3643 if (av->width == 0 || av->height == 0) {
3644 av->width = 160;
3645 av->height = 128;
3647 /* Bitrate tolerance is less for streaming */
3648 if (av->bit_rate_tolerance == 0)
3649 av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
3650 (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
3651 if (av->qmin == 0)
3652 av->qmin = 3;
3653 if (av->qmax == 0)
3654 av->qmax = 31;
3655 if (av->max_qdiff == 0)
3656 av->max_qdiff = 3;
3657 av->qcompress = 0.5;
3658 av->qblur = 0.5;
3660 if (!av->nsse_weight)
3661 av->nsse_weight = 8;
3663 av->frame_skip_cmp = FF_CMP_DCTMAX;
3664 av->me_method = ME_EPZS;
3665 av->rc_buffer_aggressivity = 1.0;
3667 if (!av->rc_eq)
3668 av->rc_eq = "tex^qComp";
3669 if (!av->i_quant_factor)
3670 av->i_quant_factor = -0.8;
3671 if (!av->b_quant_factor)
3672 av->b_quant_factor = 1.25;
3673 if (!av->b_quant_offset)
3674 av->b_quant_offset = 1.25;
3675 if (!av->rc_max_rate)
3676 av->rc_max_rate = av->bit_rate * 2;
3678 if (av->rc_max_rate && !av->rc_buffer_size) {
3679 av->rc_buffer_size = av->rc_max_rate;
3683 break;
3684 default:
3685 abort();
3688 st = av_mallocz(sizeof(AVStream));
3689 if (!st)
3690 return;
3691 st->codec = avcodec_alloc_context();
3692 stream->streams[stream->nb_streams++] = st;
3693 memcpy(st->codec, av, sizeof(AVCodecContext));
3696 static enum CodecID opt_audio_codec(const char *arg)
3698 AVCodec *p= avcodec_find_encoder_by_name(arg);
3700 if (p == NULL || p->type != CODEC_TYPE_AUDIO)
3701 return CODEC_ID_NONE;
3703 return p->id;
3706 static enum CodecID opt_video_codec(const char *arg)
3708 AVCodec *p= avcodec_find_encoder_by_name(arg);
3710 if (p == NULL || p->type != CODEC_TYPE_VIDEO)
3711 return CODEC_ID_NONE;
3713 return p->id;
3716 /* simplistic plugin support */
3718 #if HAVE_DLOPEN
3719 static void load_module(const char *filename)
3721 void *dll;
3722 void (*init_func)(void);
3723 dll = dlopen(filename, RTLD_NOW);
3724 if (!dll) {
3725 fprintf(stderr, "Could not load module '%s' - %s\n",
3726 filename, dlerror());
3727 return;
3730 init_func = dlsym(dll, "ffserver_module_init");
3731 if (!init_func) {
3732 fprintf(stderr,
3733 "%s: init function 'ffserver_module_init()' not found\n",
3734 filename);
3735 dlclose(dll);
3738 init_func();
3740 #endif
3742 static int ffserver_opt_default(const char *opt, const char *arg,
3743 AVCodecContext *avctx, int type)
3745 int ret = 0;
3746 const AVOption *o = av_find_opt(avctx, opt, NULL, type, type);
3747 if(o)
3748 ret = av_set_string3(avctx, opt, arg, 1, NULL);
3749 return ret;
3752 static int parse_ffconfig(const char *filename)
3754 FILE *f;
3755 char line[1024];
3756 char cmd[64];
3757 char arg[1024];
3758 const char *p;
3759 int val, errors, line_num;
3760 FFStream **last_stream, *stream, *redirect;
3761 FFStream **last_feed, *feed;
3762 AVCodecContext audio_enc, video_enc;
3763 enum CodecID audio_id, video_id;
3765 f = fopen(filename, "r");
3766 if (!f) {
3767 perror(filename);
3768 return -1;
3771 errors = 0;
3772 line_num = 0;
3773 first_stream = NULL;
3774 last_stream = &first_stream;
3775 first_feed = NULL;
3776 last_feed = &first_feed;
3777 stream = NULL;
3778 feed = NULL;
3779 redirect = NULL;
3780 audio_id = CODEC_ID_NONE;
3781 video_id = CODEC_ID_NONE;
3782 for(;;) {
3783 if (fgets(line, sizeof(line), f) == NULL)
3784 break;
3785 line_num++;
3786 p = line;
3787 while (isspace(*p))
3788 p++;
3789 if (*p == '\0' || *p == '#')
3790 continue;
3792 get_arg(cmd, sizeof(cmd), &p);
3794 if (!strcasecmp(cmd, "Port")) {
3795 get_arg(arg, sizeof(arg), &p);
3796 val = atoi(arg);
3797 if (val < 1 || val > 65536) {
3798 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3799 filename, line_num, arg);
3800 errors++;
3802 my_http_addr.sin_port = htons(val);
3803 } else if (!strcasecmp(cmd, "BindAddress")) {
3804 get_arg(arg, sizeof(arg), &p);
3805 if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
3806 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3807 filename, line_num, arg);
3808 errors++;
3810 } else if (!strcasecmp(cmd, "NoDaemon")) {
3811 ffserver_daemon = 0;
3812 } else if (!strcasecmp(cmd, "RTSPPort")) {
3813 get_arg(arg, sizeof(arg), &p);
3814 val = atoi(arg);
3815 if (val < 1 || val > 65536) {
3816 fprintf(stderr, "%s:%d: Invalid port: %s\n",
3817 filename, line_num, arg);
3818 errors++;
3820 my_rtsp_addr.sin_port = htons(atoi(arg));
3821 } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
3822 get_arg(arg, sizeof(arg), &p);
3823 if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
3824 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
3825 filename, line_num, arg);
3826 errors++;
3828 } else if (!strcasecmp(cmd, "MaxHTTPConnections")) {
3829 get_arg(arg, sizeof(arg), &p);
3830 val = atoi(arg);
3831 if (val < 1 || val > 65536) {
3832 fprintf(stderr, "%s:%d: Invalid MaxHTTPConnections: %s\n",
3833 filename, line_num, arg);
3834 errors++;
3836 nb_max_http_connections = val;
3837 } else if (!strcasecmp(cmd, "MaxClients")) {
3838 get_arg(arg, sizeof(arg), &p);
3839 val = atoi(arg);
3840 if (val < 1 || val > nb_max_http_connections) {
3841 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
3842 filename, line_num, arg);
3843 errors++;
3844 } else {
3845 nb_max_connections = val;
3847 } else if (!strcasecmp(cmd, "MaxBandwidth")) {
3848 int64_t llval;
3849 get_arg(arg, sizeof(arg), &p);
3850 llval = atoll(arg);
3851 if (llval < 10 || llval > 10000000) {
3852 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
3853 filename, line_num, arg);
3854 errors++;
3855 } else
3856 max_bandwidth = llval;
3857 } else if (!strcasecmp(cmd, "CustomLog")) {
3858 if (!ffserver_debug)
3859 get_arg(logfilename, sizeof(logfilename), &p);
3860 } else if (!strcasecmp(cmd, "<Feed")) {
3861 /*********************************************/
3862 /* Feed related options */
3863 char *q;
3864 if (stream || feed) {
3865 fprintf(stderr, "%s:%d: Already in a tag\n",
3866 filename, line_num);
3867 } else {
3868 feed = av_mallocz(sizeof(FFStream));
3869 /* add in stream list */
3870 *last_stream = feed;
3871 last_stream = &feed->next;
3872 /* add in feed list */
3873 *last_feed = feed;
3874 last_feed = &feed->next_feed;
3876 get_arg(feed->filename, sizeof(feed->filename), &p);
3877 q = strrchr(feed->filename, '>');
3878 if (*q)
3879 *q = '\0';
3880 feed->fmt = guess_format("ffm", NULL, NULL);
3881 /* defaut feed file */
3882 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
3883 "/tmp/%s.ffm", feed->filename);
3884 feed->feed_max_size = 5 * 1024 * 1024;
3885 feed->is_feed = 1;
3886 feed->feed = feed; /* self feeding :-) */
3888 } else if (!strcasecmp(cmd, "Launch")) {
3889 if (feed) {
3890 int i;
3892 feed->child_argv = av_mallocz(64 * sizeof(char *));
3894 for (i = 0; i < 62; i++) {
3895 get_arg(arg, sizeof(arg), &p);
3896 if (!arg[0])
3897 break;
3899 feed->child_argv[i] = av_strdup(arg);
3902 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
3904 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
3905 "http://%s:%d/%s",
3906 (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
3907 inet_ntoa(my_http_addr.sin_addr),
3908 ntohs(my_http_addr.sin_port), feed->filename);
3910 } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
3911 if (feed) {
3912 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3913 feed->readonly = 1;
3914 } else if (stream) {
3915 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3917 } else if (!strcasecmp(cmd, "File")) {
3918 if (feed) {
3919 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
3920 } else if (stream)
3921 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
3922 } else if (!strcasecmp(cmd, "FileMaxSize")) {
3923 if (feed) {
3924 char *p1;
3925 double fsize;
3927 get_arg(arg, sizeof(arg), &p);
3928 p1 = arg;
3929 fsize = strtod(p1, &p1);
3930 switch(toupper(*p1)) {
3931 case 'K':
3932 fsize *= 1024;
3933 break;
3934 case 'M':
3935 fsize *= 1024 * 1024;
3936 break;
3937 case 'G':
3938 fsize *= 1024 * 1024 * 1024;
3939 break;
3941 feed->feed_max_size = (int64_t)fsize;
3943 } else if (!strcasecmp(cmd, "</Feed>")) {
3944 if (!feed) {
3945 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
3946 filename, line_num);
3947 errors++;
3949 feed = NULL;
3950 } else if (!strcasecmp(cmd, "<Stream")) {
3951 /*********************************************/
3952 /* Stream related options */
3953 char *q;
3954 if (stream || feed) {
3955 fprintf(stderr, "%s:%d: Already in a tag\n",
3956 filename, line_num);
3957 } else {
3958 const AVClass *class;
3959 stream = av_mallocz(sizeof(FFStream));
3960 *last_stream = stream;
3961 last_stream = &stream->next;
3963 get_arg(stream->filename, sizeof(stream->filename), &p);
3964 q = strrchr(stream->filename, '>');
3965 if (*q)
3966 *q = '\0';
3967 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
3968 /* fetch avclass so AVOption works
3969 * FIXME try to use avcodec_get_context_defaults2
3970 * without changing defaults too much */
3971 avcodec_get_context_defaults(&video_enc);
3972 class = video_enc.av_class;
3973 memset(&audio_enc, 0, sizeof(AVCodecContext));
3974 memset(&video_enc, 0, sizeof(AVCodecContext));
3975 audio_enc.av_class = class;
3976 video_enc.av_class = class;
3977 audio_id = CODEC_ID_NONE;
3978 video_id = CODEC_ID_NONE;
3979 if (stream->fmt) {
3980 audio_id = stream->fmt->audio_codec;
3981 video_id = stream->fmt->video_codec;
3984 } else if (!strcasecmp(cmd, "Feed")) {
3985 get_arg(arg, sizeof(arg), &p);
3986 if (stream) {
3987 FFStream *sfeed;
3989 sfeed = first_feed;
3990 while (sfeed != NULL) {
3991 if (!strcmp(sfeed->filename, arg))
3992 break;
3993 sfeed = sfeed->next_feed;
3995 if (!sfeed)
3996 fprintf(stderr, "%s:%d: feed '%s' not defined\n",
3997 filename, line_num, arg);
3998 else
3999 stream->feed = sfeed;
4001 } else if (!strcasecmp(cmd, "Format")) {
4002 get_arg(arg, sizeof(arg), &p);
4003 if (stream) {
4004 if (!strcmp(arg, "status")) {
4005 stream->stream_type = STREAM_TYPE_STATUS;
4006 stream->fmt = NULL;
4007 } else {
4008 stream->stream_type = STREAM_TYPE_LIVE;
4009 /* jpeg cannot be used here, so use single frame jpeg */
4010 if (!strcmp(arg, "jpeg"))
4011 strcpy(arg, "mjpeg");
4012 stream->fmt = guess_stream_format(arg, NULL, NULL);
4013 if (!stream->fmt) {
4014 fprintf(stderr, "%s:%d: Unknown Format: %s\n",
4015 filename, line_num, arg);
4016 errors++;
4019 if (stream->fmt) {
4020 audio_id = stream->fmt->audio_codec;
4021 video_id = stream->fmt->video_codec;
4024 } else if (!strcasecmp(cmd, "InputFormat")) {
4025 get_arg(arg, sizeof(arg), &p);
4026 if (stream) {
4027 stream->ifmt = av_find_input_format(arg);
4028 if (!stream->ifmt) {
4029 fprintf(stderr, "%s:%d: Unknown input format: %s\n",
4030 filename, line_num, arg);
4033 } else if (!strcasecmp(cmd, "FaviconURL")) {
4034 if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
4035 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
4036 } else {
4037 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
4038 filename, line_num);
4039 errors++;
4041 } else if (!strcasecmp(cmd, "Author")) {
4042 if (stream)
4043 get_arg(stream->author, sizeof(stream->author), &p);
4044 } else if (!strcasecmp(cmd, "Comment")) {
4045 if (stream)
4046 get_arg(stream->comment, sizeof(stream->comment), &p);
4047 } else if (!strcasecmp(cmd, "Copyright")) {
4048 if (stream)
4049 get_arg(stream->copyright, sizeof(stream->copyright), &p);
4050 } else if (!strcasecmp(cmd, "Title")) {
4051 if (stream)
4052 get_arg(stream->title, sizeof(stream->title), &p);
4053 } else if (!strcasecmp(cmd, "Preroll")) {
4054 get_arg(arg, sizeof(arg), &p);
4055 if (stream)
4056 stream->prebuffer = atof(arg) * 1000;
4057 } else if (!strcasecmp(cmd, "StartSendOnKey")) {
4058 if (stream)
4059 stream->send_on_key = 1;
4060 } else if (!strcasecmp(cmd, "AudioCodec")) {
4061 get_arg(arg, sizeof(arg), &p);
4062 audio_id = opt_audio_codec(arg);
4063 if (audio_id == CODEC_ID_NONE) {
4064 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
4065 filename, line_num, arg);
4066 errors++;
4068 } else if (!strcasecmp(cmd, "VideoCodec")) {
4069 get_arg(arg, sizeof(arg), &p);
4070 video_id = opt_video_codec(arg);
4071 if (video_id == CODEC_ID_NONE) {
4072 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
4073 filename, line_num, arg);
4074 errors++;
4076 } else if (!strcasecmp(cmd, "MaxTime")) {
4077 get_arg(arg, sizeof(arg), &p);
4078 if (stream)
4079 stream->max_time = atof(arg) * 1000;
4080 } else if (!strcasecmp(cmd, "AudioBitRate")) {
4081 get_arg(arg, sizeof(arg), &p);
4082 if (stream)
4083 audio_enc.bit_rate = atoi(arg) * 1000;
4084 } else if (!strcasecmp(cmd, "AudioChannels")) {
4085 get_arg(arg, sizeof(arg), &p);
4086 if (stream)
4087 audio_enc.channels = atoi(arg);
4088 } else if (!strcasecmp(cmd, "AudioSampleRate")) {
4089 get_arg(arg, sizeof(arg), &p);
4090 if (stream)
4091 audio_enc.sample_rate = atoi(arg);
4092 } else if (!strcasecmp(cmd, "AudioQuality")) {
4093 get_arg(arg, sizeof(arg), &p);
4094 if (stream) {
4095 // audio_enc.quality = atof(arg) * 1000;
4097 } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
4098 if (stream) {
4099 int minrate, maxrate;
4101 get_arg(arg, sizeof(arg), &p);
4103 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
4104 video_enc.rc_min_rate = minrate * 1000;
4105 video_enc.rc_max_rate = maxrate * 1000;
4106 } else {
4107 fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
4108 filename, line_num, arg);
4109 errors++;
4112 } else if (!strcasecmp(cmd, "Debug")) {
4113 if (stream) {
4114 get_arg(arg, sizeof(arg), &p);
4115 video_enc.debug = strtol(arg,0,0);
4117 } else if (!strcasecmp(cmd, "Strict")) {
4118 if (stream) {
4119 get_arg(arg, sizeof(arg), &p);
4120 video_enc.strict_std_compliance = atoi(arg);
4122 } else if (!strcasecmp(cmd, "VideoBufferSize")) {
4123 if (stream) {
4124 get_arg(arg, sizeof(arg), &p);
4125 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
4127 } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
4128 if (stream) {
4129 get_arg(arg, sizeof(arg), &p);
4130 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
4132 } else if (!strcasecmp(cmd, "VideoBitRate")) {
4133 get_arg(arg, sizeof(arg), &p);
4134 if (stream) {
4135 video_enc.bit_rate = atoi(arg) * 1000;
4137 } else if (!strcasecmp(cmd, "VideoSize")) {
4138 get_arg(arg, sizeof(arg), &p);
4139 if (stream) {
4140 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
4141 if ((video_enc.width % 16) != 0 ||
4142 (video_enc.height % 16) != 0) {
4143 fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
4144 filename, line_num);
4145 errors++;
4148 } else if (!strcasecmp(cmd, "VideoFrameRate")) {
4149 get_arg(arg, sizeof(arg), &p);
4150 if (stream) {
4151 AVRational frame_rate;
4152 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
4153 fprintf(stderr, "Incorrect frame rate\n");
4154 errors++;
4155 } else {
4156 video_enc.time_base.num = frame_rate.den;
4157 video_enc.time_base.den = frame_rate.num;
4160 } else if (!strcasecmp(cmd, "VideoGopSize")) {
4161 get_arg(arg, sizeof(arg), &p);
4162 if (stream)
4163 video_enc.gop_size = atoi(arg);
4164 } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
4165 if (stream)
4166 video_enc.gop_size = 1;
4167 } else if (!strcasecmp(cmd, "VideoHighQuality")) {
4168 if (stream)
4169 video_enc.mb_decision = FF_MB_DECISION_BITS;
4170 } else if (!strcasecmp(cmd, "Video4MotionVector")) {
4171 if (stream) {
4172 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
4173 video_enc.flags |= CODEC_FLAG_4MV;
4175 } else if (!strcasecmp(cmd, "AVOptionVideo") ||
4176 !strcasecmp(cmd, "AVOptionAudio")) {
4177 char arg2[1024];
4178 AVCodecContext *avctx;
4179 int type;
4180 get_arg(arg, sizeof(arg), &p);
4181 get_arg(arg2, sizeof(arg2), &p);
4182 if (!strcasecmp(cmd, "AVOptionVideo")) {
4183 avctx = &video_enc;
4184 type = AV_OPT_FLAG_VIDEO_PARAM;
4185 } else {
4186 avctx = &audio_enc;
4187 type = AV_OPT_FLAG_AUDIO_PARAM;
4189 if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
4190 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
4191 errors++;
4193 } else if (!strcasecmp(cmd, "VideoTag")) {
4194 get_arg(arg, sizeof(arg), &p);
4195 if ((strlen(arg) == 4) && stream)
4196 video_enc.codec_tag = AV_RL32(arg);
4197 } else if (!strcasecmp(cmd, "BitExact")) {
4198 if (stream)
4199 video_enc.flags |= CODEC_FLAG_BITEXACT;
4200 } else if (!strcasecmp(cmd, "DctFastint")) {
4201 if (stream)
4202 video_enc.dct_algo = FF_DCT_FASTINT;
4203 } else if (!strcasecmp(cmd, "IdctSimple")) {
4204 if (stream)
4205 video_enc.idct_algo = FF_IDCT_SIMPLE;
4206 } else if (!strcasecmp(cmd, "Qscale")) {
4207 get_arg(arg, sizeof(arg), &p);
4208 if (stream) {
4209 video_enc.flags |= CODEC_FLAG_QSCALE;
4210 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
4212 } else if (!strcasecmp(cmd, "VideoQDiff")) {
4213 get_arg(arg, sizeof(arg), &p);
4214 if (stream) {
4215 video_enc.max_qdiff = atoi(arg);
4216 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
4217 fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
4218 filename, line_num);
4219 errors++;
4222 } else if (!strcasecmp(cmd, "VideoQMax")) {
4223 get_arg(arg, sizeof(arg), &p);
4224 if (stream) {
4225 video_enc.qmax = atoi(arg);
4226 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
4227 fprintf(stderr, "%s:%d: VideoQMax out of range\n",
4228 filename, line_num);
4229 errors++;
4232 } else if (!strcasecmp(cmd, "VideoQMin")) {
4233 get_arg(arg, sizeof(arg), &p);
4234 if (stream) {
4235 video_enc.qmin = atoi(arg);
4236 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
4237 fprintf(stderr, "%s:%d: VideoQMin out of range\n",
4238 filename, line_num);
4239 errors++;
4242 } else if (!strcasecmp(cmd, "LumaElim")) {
4243 get_arg(arg, sizeof(arg), &p);
4244 if (stream)
4245 video_enc.luma_elim_threshold = atoi(arg);
4246 } else if (!strcasecmp(cmd, "ChromaElim")) {
4247 get_arg(arg, sizeof(arg), &p);
4248 if (stream)
4249 video_enc.chroma_elim_threshold = atoi(arg);
4250 } else if (!strcasecmp(cmd, "LumiMask")) {
4251 get_arg(arg, sizeof(arg), &p);
4252 if (stream)
4253 video_enc.lumi_masking = atof(arg);
4254 } else if (!strcasecmp(cmd, "DarkMask")) {
4255 get_arg(arg, sizeof(arg), &p);
4256 if (stream)
4257 video_enc.dark_masking = atof(arg);
4258 } else if (!strcasecmp(cmd, "NoVideo")) {
4259 video_id = CODEC_ID_NONE;
4260 } else if (!strcasecmp(cmd, "NoAudio")) {
4261 audio_id = CODEC_ID_NONE;
4262 } else if (!strcasecmp(cmd, "ACL")) {
4263 IPAddressACL acl;
4265 get_arg(arg, sizeof(arg), &p);
4266 if (strcasecmp(arg, "allow") == 0)
4267 acl.action = IP_ALLOW;
4268 else if (strcasecmp(arg, "deny") == 0)
4269 acl.action = IP_DENY;
4270 else {
4271 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
4272 filename, line_num, arg);
4273 errors++;
4276 get_arg(arg, sizeof(arg), &p);
4278 if (resolve_host(&acl.first, arg) != 0) {
4279 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4280 filename, line_num, arg);
4281 errors++;
4282 } else
4283 acl.last = acl.first;
4285 get_arg(arg, sizeof(arg), &p);
4287 if (arg[0]) {
4288 if (resolve_host(&acl.last, arg) != 0) {
4289 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
4290 filename, line_num, arg);
4291 errors++;
4295 if (!errors) {
4296 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
4297 IPAddressACL **naclp = 0;
4299 acl.next = 0;
4300 *nacl = acl;
4302 if (stream)
4303 naclp = &stream->acl;
4304 else if (feed)
4305 naclp = &feed->acl;
4306 else {
4307 fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
4308 filename, line_num);
4309 errors++;
4312 if (naclp) {
4313 while (*naclp)
4314 naclp = &(*naclp)->next;
4316 *naclp = nacl;
4319 } else if (!strcasecmp(cmd, "RTSPOption")) {
4320 get_arg(arg, sizeof(arg), &p);
4321 if (stream) {
4322 av_freep(&stream->rtsp_option);
4323 stream->rtsp_option = av_strdup(arg);
4325 } else if (!strcasecmp(cmd, "MulticastAddress")) {
4326 get_arg(arg, sizeof(arg), &p);
4327 if (stream) {
4328 if (resolve_host(&stream->multicast_ip, arg) != 0) {
4329 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
4330 filename, line_num, arg);
4331 errors++;
4333 stream->is_multicast = 1;
4334 stream->loop = 1; /* default is looping */
4336 } else if (!strcasecmp(cmd, "MulticastPort")) {
4337 get_arg(arg, sizeof(arg), &p);
4338 if (stream)
4339 stream->multicast_port = atoi(arg);
4340 } else if (!strcasecmp(cmd, "MulticastTTL")) {
4341 get_arg(arg, sizeof(arg), &p);
4342 if (stream)
4343 stream->multicast_ttl = atoi(arg);
4344 } else if (!strcasecmp(cmd, "NoLoop")) {
4345 if (stream)
4346 stream->loop = 0;
4347 } else if (!strcasecmp(cmd, "</Stream>")) {
4348 if (!stream) {
4349 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
4350 filename, line_num);
4351 errors++;
4352 } else {
4353 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
4354 if (audio_id != CODEC_ID_NONE) {
4355 audio_enc.codec_type = CODEC_TYPE_AUDIO;
4356 audio_enc.codec_id = audio_id;
4357 add_codec(stream, &audio_enc);
4359 if (video_id != CODEC_ID_NONE) {
4360 video_enc.codec_type = CODEC_TYPE_VIDEO;
4361 video_enc.codec_id = video_id;
4362 add_codec(stream, &video_enc);
4365 stream = NULL;
4367 } else if (!strcasecmp(cmd, "<Redirect")) {
4368 /*********************************************/
4369 char *q;
4370 if (stream || feed || redirect) {
4371 fprintf(stderr, "%s:%d: Already in a tag\n",
4372 filename, line_num);
4373 errors++;
4374 } else {
4375 redirect = av_mallocz(sizeof(FFStream));
4376 *last_stream = redirect;
4377 last_stream = &redirect->next;
4379 get_arg(redirect->filename, sizeof(redirect->filename), &p);
4380 q = strrchr(redirect->filename, '>');
4381 if (*q)
4382 *q = '\0';
4383 redirect->stream_type = STREAM_TYPE_REDIRECT;
4385 } else if (!strcasecmp(cmd, "URL")) {
4386 if (redirect)
4387 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
4388 } else if (!strcasecmp(cmd, "</Redirect>")) {
4389 if (!redirect) {
4390 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
4391 filename, line_num);
4392 errors++;
4393 } else {
4394 if (!redirect->feed_filename[0]) {
4395 fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
4396 filename, line_num);
4397 errors++;
4399 redirect = NULL;
4401 } else if (!strcasecmp(cmd, "LoadModule")) {
4402 get_arg(arg, sizeof(arg), &p);
4403 #if HAVE_DLOPEN
4404 load_module(arg);
4405 #else
4406 fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
4407 filename, line_num, arg);
4408 errors++;
4409 #endif
4410 } else {
4411 fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
4412 filename, line_num, cmd);
4413 errors++;
4417 fclose(f);
4418 if (errors)
4419 return -1;
4420 else
4421 return 0;
4424 static void handle_child_exit(int sig)
4426 pid_t pid;
4427 int status;
4429 while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
4430 FFStream *feed;
4432 for (feed = first_feed; feed; feed = feed->next) {
4433 if (feed->pid == pid) {
4434 int uptime = time(0) - feed->pid_start;
4436 feed->pid = 0;
4437 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
4439 if (uptime < 30)
4440 /* Turn off any more restarts */
4441 feed->child_argv = 0;
4446 need_to_start_children = 1;
4449 static void opt_debug(void)
4451 ffserver_debug = 1;
4452 ffserver_daemon = 0;
4453 logfilename[0] = '-';
4456 static void opt_show_help(void)
4458 printf("usage: ffserver [options]\n"
4459 "Hyper fast multi format Audio/Video streaming server\n");
4460 printf("\n");
4461 show_help_options(options, "Main options:\n", 0, 0);
4464 static const OptionDef options[] = {
4465 { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
4466 { "version", OPT_EXIT, {(void*)show_version}, "show version" },
4467 { "L", OPT_EXIT, {(void*)show_license}, "show license" },
4468 { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
4469 { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
4470 { "d", 0, {(void*)opt_debug}, "enable debug mode" },
4471 { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
4472 { NULL },
4475 int main(int argc, char **argv)
4477 struct sigaction sigact;
4479 av_register_all();
4481 show_banner();
4483 config_filename = "/etc/ffserver.conf";
4485 my_program_name = argv[0];
4486 my_program_dir = getcwd(0, 0);
4487 ffserver_daemon = 1;
4489 parse_options(argc, argv, options, NULL);
4491 unsetenv("http_proxy"); /* Kill the http_proxy */
4493 av_random_init(&random_state, av_gettime() + (getpid() << 16));
4495 memset(&sigact, 0, sizeof(sigact));
4496 sigact.sa_handler = handle_child_exit;
4497 sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
4498 sigaction(SIGCHLD, &sigact, 0);
4500 if (parse_ffconfig(config_filename) < 0) {
4501 fprintf(stderr, "Incorrect config file - exiting.\n");
4502 exit(1);
4505 /* open log file if needed */
4506 if (logfilename[0] != '\0') {
4507 if (!strcmp(logfilename, "-"))
4508 logfile = stdout;
4509 else
4510 logfile = fopen(logfilename, "a");
4511 av_log_set_callback(http_av_log);
4514 build_file_streams();
4516 build_feed_streams();
4518 compute_bandwidth();
4520 /* put the process in background and detach it from its TTY */
4521 if (ffserver_daemon) {
4522 int pid;
4524 pid = fork();
4525 if (pid < 0) {
4526 perror("fork");
4527 exit(1);
4528 } else if (pid > 0) {
4529 /* parent : exit */
4530 exit(0);
4531 } else {
4532 /* child */
4533 setsid();
4534 close(0);
4535 open("/dev/null", O_RDWR);
4536 if (strcmp(logfilename, "-") != 0) {
4537 close(1);
4538 dup(0);
4540 close(2);
4541 dup(0);
4545 /* signal init */
4546 signal(SIGPIPE, SIG_IGN);
4548 if (ffserver_daemon)
4549 chdir("/");
4551 if (http_server() < 0) {
4552 http_log("Could not start server\n");
4553 exit(1);
4556 return 0;