ffserver.c
85f07f22
 /*
  * Multiple format streaming server
773a21b8
  * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
85f07f22
  *
b78e7197
  * This file is part of FFmpeg.
  *
  * FFmpeg is free software; you can redistribute it and/or
773a21b8
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
b78e7197
  * version 2.1 of the License, or (at your option) any later version.
85f07f22
  *
b78e7197
  * FFmpeg is distributed in the hope that it will be useful,
85f07f22
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
773a21b8
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
85f07f22
  *
773a21b8
  * You should have received a copy of the GNU Lesser General Public
b78e7197
  * License along with FFmpeg; if not, write to the Free Software
5509bffa
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
85f07f22
  */
364a9607
 
29d3ed3b
 #define _XOPEN_SOURCE 600
 
0f4e8165
 #include "config.h"
b250f9c6
 #if !HAVE_CLOSESOCKET
0f4e8165
 #define closesocket close
 #endif
 #include <string.h>
ea452b54
 #include <strings.h>
0f4e8165
 #include <stdlib.h>
959da985
 /* avformat.h defines LIBAVFORMAT_BUILD, include it before all the other libav* headers which use it */
245976da
 #include "libavformat/avformat.h"
 #include "libavformat/network.h"
 #include "libavformat/os_support.h"
302879cb
 #include "libavformat/rtpdec.h"
245976da
 #include "libavformat/rtsp.h"
959da985
 #include "libavutil/avstring.h"
 #include "libavutil/random.h"
2bb6eba2
 #include "libavutil/intreadwrite.h"
7ab08864
 #include "libavcodec/opt.h"
85f07f22
 #include <stdarg.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
b250f9c6
 #if HAVE_POLL_H
f8cda19e
 #include <poll.h>
b0c858d8
 #endif
85f07f22
 #include <errno.h>
 #include <sys/time.h>
4568325a
 #undef time //needed because HAVE_AV_CONFIG_H is defined on top
85f07f22
 #include <time.h>
5eb765ef
 #include <sys/wait.h>
85f07f22
 #include <signal.h>
b250f9c6
 #if HAVE_DLFCN_H
2effd274
 #include <dlfcn.h>
6638d424
 #endif
2effd274
 
4ce5df08
 #include "cmdutils.h"
85f07f22
 
c367d067
 #undef exit
 
64555bd9
 const char program_name[] = "FFserver";
ea9c581f
 const int program_birth_year = 2000;
86074ed1
 
5a635bc7
 static const OptionDef options[];
 
85f07f22
 enum HTTPState {
     HTTPSTATE_WAIT_REQUEST,
     HTTPSTATE_SEND_HEADER,
     HTTPSTATE_SEND_DATA_HEADER,
2effd274
     HTTPSTATE_SEND_DATA,          /* sending TCP or UDP data */
85f07f22
     HTTPSTATE_SEND_DATA_TRAILER,
115329f1
     HTTPSTATE_RECEIVE_DATA,
2effd274
     HTTPSTATE_WAIT_FEED,          /* wait for data from the feed */
     HTTPSTATE_READY,
 
     RTSPSTATE_WAIT_REQUEST,
     RTSPSTATE_SEND_REPLY,
bc351386
     RTSPSTATE_SEND_PACKET,
85f07f22
 };
 
9507a12e
 static const char *http_state[] = {
2effd274
     "HTTP_WAIT_REQUEST",
     "HTTP_SEND_HEADER",
 
85f07f22
     "SEND_DATA_HEADER",
     "SEND_DATA",
     "SEND_DATA_TRAILER",
     "RECEIVE_DATA",
     "WAIT_FEED",
2effd274
     "READY",
 
     "RTSP_WAIT_REQUEST",
     "RTSP_SEND_REPLY",
bc351386
     "RTSP_SEND_PACKET",
85f07f22
 };
 
cde25790
 #define IOBUFFER_INIT_SIZE 8192
85f07f22
 
 /* timeouts are in ms */
2effd274
 #define HTTP_REQUEST_TIMEOUT (15 * 1000)
 #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
 
85f07f22
 #define SYNC_TIMEOUT (10 * 1000)
 
b516ecdd
 typedef struct RTSPActionServerSetup {
     uint32_t ipaddr;
     char transport_option[512];
 } RTSPActionServerSetup;
 
5eb765ef
 typedef struct {
0c1a9eda
     int64_t count1, count2;
c3f58185
     int64_t time1, time2;
5eb765ef
 } DataRateData;
 
85f07f22
 /* context associated with one connection */
 typedef struct HTTPContext {
     enum HTTPState state;
     int fd; /* socket file descriptor */
     struct sockaddr_in from_addr; /* origin */
     struct pollfd *poll_entry; /* used when polling */
c3f58185
     int64_t timeout;
0c1a9eda
     uint8_t *buffer_ptr, *buffer_end;
85f07f22
     int http_error;
edfdd798
     int post;
85f07f22
     struct HTTPContext *next;
42a63c6a
     int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
0c1a9eda
     int64_t data_count;
85f07f22
     /* feed input */
     int feed_fd;
     /* input format handling */
     AVFormatContext *fmt_in;
c3f58185
     int64_t start_time;            /* In milliseconds - this wraps fairly often */
0c1a9eda
     int64_t first_pts;            /* initial pts value */
e240a0bb
     int64_t cur_pts;             /* current pts value from the stream in us */
     int64_t cur_frame_duration;  /* duration of the current frame in us */
     int cur_frame_bytes;       /* output frame size, needed to compute
                                   the time at which we send each
                                   packet */
     int pts_stream_index;        /* stream we choose as clock reference */
     int64_t cur_clock;           /* current clock reference value in us */
85f07f22
     /* output format handling */
     struct FFStream *stream;
cde25790
     /* -1 is invalid stream */
     int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
     int switch_feed_streams[MAX_STREAMS]; /* index of streams in the feed */
     int switch_pending;
2effd274
     AVFormatContext fmt_ctx; /* instance of FFStream for one user */
85f07f22
     int last_packet_sent; /* true if last data packet was sent */
7434ba6d
     int suppress_log;
5eb765ef
     DataRateData datarate;
3120d2a2
     int wmp_client_id;
7434ba6d
     char protocol[16];
     char method[16];
     char url[128];
cde25790
     int buffer_size;
0c1a9eda
     uint8_t *buffer;
2effd274
     int is_packetized; /* if true, the stream is packetized */
     int packet_stream_index; /* current stream for output in state machine */
115329f1
 
2effd274
     /* RTSP state specific */
0c1a9eda
     uint8_t *pb_buffer; /* XXX: use that in all the code */
2effd274
     ByteIOContext *pb;
     int seq; /* RTSP sequence number */
115329f1
 
2effd274
     /* RTP state specific */
90abbdba
     enum RTSPLowerTransport rtp_protocol;
2effd274
     char session_id[32]; /* session id */
     AVFormatContext *rtp_ctx[MAX_STREAMS];
e240a0bb
 
bc351386
     /* RTP/UDP specific */
     URLContext *rtp_handles[MAX_STREAMS];
 
     /* RTP/TCP specific */
     struct HTTPContext *rtsp_c;
     uint8_t *packet_buffer, *packet_buffer_ptr, *packet_buffer_end;
85f07f22
 } HTTPContext;
 
 /* each generated stream is described here */
 enum StreamType {
     STREAM_TYPE_LIVE,
     STREAM_TYPE_STATUS,
cde25790
     STREAM_TYPE_REDIRECT,
85f07f22
 };
 
8256c0a3
 enum IPAddressAction {
     IP_ALLOW = 1,
     IP_DENY,
 };
 
 typedef struct IPAddressACL {
     struct IPAddressACL *next;
     enum IPAddressAction action;
efa04ce2
     /* These are in host order */
8256c0a3
     struct in_addr first;
     struct in_addr last;
 } IPAddressACL;
 
85f07f22
 /* description of each stream of the ffserver.conf file */
 typedef struct FFStream {
     enum StreamType stream_type;
     char filename[1024];     /* stream filename */
2effd274
     struct FFStream *feed;   /* feed we are using (can be null if
                                 coming from file) */
e240a0bb
     AVFormatParameters *ap_in; /* input parameters */
     AVInputFormat *ifmt;       /* if non NULL, force input format */
bd7cf6ad
     AVOutputFormat *fmt;
8256c0a3
     IPAddressACL *acl;
85f07f22
     int nb_streams;
42a63c6a
     int prebuffer;      /* Number of millseconds early to start */
c3f58185
     int64_t max_time;      /* Number of milliseconds to run */
79c4ea3c
     int send_on_key;
85f07f22
     AVStream *streams[MAX_STREAMS];
     int feed_streams[MAX_STREAMS]; /* index of streams in the feed */
     char feed_filename[1024]; /* file name of the feed storage, or
                                  input file name for a stream */
2ac887ba
     char author[512];
     char title[512];
     char copyright[512];
     char comment[512];
cde25790
     pid_t pid;  /* Of ffmpeg process */
5eb765ef
     time_t pid_start;  /* Of ffmpeg process */
cde25790
     char **child_argv;
85f07f22
     struct FFStream *next;
177d2564
     unsigned bandwidth; /* bandwidth, in kbits/s */
2effd274
     /* RTSP options */
     char *rtsp_option;
829ac53d
     /* multicast specific */
     int is_multicast;
     struct in_addr multicast_ip;
     int multicast_port; /* first port used for multicast */
6edd6884
     int multicast_ttl;
     int loop; /* if true, send the stream in loops (only meaningful if file) */
829ac53d
 
85f07f22
     /* feed specific */
2effd274
     int feed_opened;     /* true if someone is writing to the feed */
85f07f22
     int is_feed;         /* true if it is a feed */
e322ea48
     int readonly;        /* True if writing is prohibited to the file */
a6e14edd
     int conns_served;
0c1a9eda
     int64_t bytes_served;
6b0bdc75
     int64_t feed_max_size;      /* maximum storage size, zero means unlimited */
8bfb108b
     int64_t feed_write_index;   /* current write position in feed (it wraps around) */
0c1a9eda
     int64_t feed_size;          /* current size of feed */
85f07f22
     struct FFStream *next_feed;
 } FFStream;
 
 typedef struct FeedData {
     long long data_count;
8bfb108b
     float avg_frame_size;   /* frame size averaged over last frames with exponential mean */
85f07f22
 } FeedData;
 
18405874
 static struct sockaddr_in my_http_addr;
 static struct sockaddr_in my_rtsp_addr;
2effd274
 
33f5e2ec
 static char logfilename[1024];
 static HTTPContext *first_http_ctx;
 static FFStream *first_feed;   /* contains only feeds */
 static FFStream *first_stream; /* contains all streams, including feeds */
85f07f22
 
2effd274
 static void new_connection(int server_fd, int is_rtsp);
 static void close_connection(HTTPContext *c);
 
 /* HTTP handling */
 static int handle_connection(HTTPContext *c);
85f07f22
 static int http_parse_request(HTTPContext *c);
5eb765ef
 static int http_send_data(HTTPContext *c);
dca21085
 static void compute_status(HTTPContext *c);
85f07f22
 static int open_input_stream(HTTPContext *c, const char *info);
 static int http_start_receive_data(HTTPContext *c);
 static int http_receive_data(HTTPContext *c);
2effd274
 
 /* RTSP handling */
 static int rtsp_parse_request(HTTPContext *c);
 static void rtsp_cmd_describe(HTTPContext *c, const char *url);
0df65975
 static void rtsp_cmd_options(HTTPContext *c, const char *url);
a9e534d5
 static void rtsp_cmd_setup(HTTPContext *c, const char *url, RTSPMessageHeader *h);
 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h);
 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h);
 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h);
2effd274
 
829ac53d
 /* SDP handling */
115329f1
 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
829ac53d
                                    struct in_addr my_ip);
 
2effd274
 /* RTP handling */
115329f1
 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
bc351386
                                        FFStream *stream, const char *session_id,
90abbdba
                                        enum RTSPLowerTransport rtp_protocol);
115329f1
 static int rtp_new_av_stream(HTTPContext *c,
bc351386
                              int stream_index, struct sockaddr_in *dest_addr,
                              HTTPContext *rtsp_c);
85f07f22
 
cde25790
 static const char *my_program_name;
d6562d2c
 static const char *my_program_dir;
cde25790
 
5a635bc7
 static const char *config_filename;
2ac887ba
 static int ffserver_debug;
2effd274
 static int ffserver_daemon;
2ac887ba
 static int no_launch;
5eb765ef
 static int need_to_start_children;
2ac887ba
 
1c9ff179
 /* maximum number of simultaneous HTTP connections */
 static unsigned int nb_max_http_connections = 2000;
4af92de6
 static unsigned int nb_max_connections = 5;
 static unsigned int nb_connections;
85f07f22
 
f69bb0cc
 static uint64_t max_bandwidth = 1000;
1ad8289e
 static uint64_t current_bandwidth;
42a63c6a
 
c3f58185
 static int64_t cur_time;           // Making this global saves on passing it around everywhere
5eb765ef
 
1df93ae9
 static AVRandomState random_state;
 
85f07f22
 static FILE *logfile = NULL;
 
9fd3442f
 static char *ctime1(char *buf2)
 {
     time_t ti;
     char *p;
 
     ti = time(NULL);
     p = ctime(&ti);
     strcpy(buf2, p);
     p = buf2 + strlen(p) - 1;
     if (*p == '\n')
         *p = '\0';
     return buf2;
 }
 
bcd3ce59
 static void http_vlog(const char *fmt, va_list vargs)
85f07f22
 {
124ed1c0
     static int print_prefix = 1;
7434ba6d
     if (logfile) {
124ed1c0
         if (print_prefix) {
9fd3442f
             char buf[32];
             ctime1(buf);
             fprintf(logfile, "%s ", buf);
124ed1c0
         }
         print_prefix = strstr(fmt, "\n") != NULL;
bcd3ce59
         vfprintf(logfile, fmt, vargs);
7434ba6d
         fflush(logfile);
     }
bcd3ce59
 }
 
 void __attribute__ ((format (printf, 1, 2))) http_log(const char *fmt, ...)
 {
     va_list vargs;
     va_start(vargs, fmt);
     http_vlog(fmt, vargs);
     va_end(vargs);
 }
 
 static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
 {
     static int print_prefix = 1;
     AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
     if (level > av_log_level)
         return;
     if (print_prefix && avc)
59e7894c
         http_log("[%s @ %p]", avc->item_name(ptr), ptr);
bcd3ce59
     print_prefix = strstr(fmt, "\n") != NULL;
     http_vlog(fmt, vargs);
85f07f22
 }
 
6edd6884
 static void log_connection(HTTPContext *c)
 {
115329f1
     if (c->suppress_log)
6edd6884
         return;
 
82e0be62
     http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
              inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
6edd6884
              c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
cde25790
 }
 
0c1a9eda
 static void update_datarate(DataRateData *drd, int64_t count)
5eb765ef
 {
     if (!drd->time1 && !drd->count1) {
         drd->time1 = drd->time2 = cur_time;
         drd->count1 = drd->count2 = count;
eeffbdea
     } else if (cur_time - drd->time2 > 5000) {
33a4ecbe
         drd->time1 = drd->time2;
         drd->count1 = drd->count2;
         drd->time2 = cur_time;
         drd->count2 = count;
5eb765ef
     }
 }
 
 /* In bytes per second */
0c1a9eda
 static int compute_datarate(DataRateData *drd, int64_t count)
5eb765ef
 {
     if (cur_time == drd->time1)
         return 0;
115329f1
 
5eb765ef
     return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
 }
 
a782f209
 
cde25790
 static void start_children(FFStream *feed)
 {
2ac887ba
     if (no_launch)
         return;
 
cde25790
     for (; feed; feed = feed->next) {
5eb765ef
         if (feed->child_argv && !feed->pid) {
             feed->pid_start = time(0);
 
cde25790
             feed->pid = fork();
 
             if (feed->pid < 0) {
b4befb99
                 http_log("Unable to create children\n");
cde25790
                 exit(1);
             }
             if (!feed->pid) {
                 /* In child */
                 char pathname[1024];
                 char *slash;
                 int i;
 
40444a59
                 av_strlcpy(pathname, my_program_name, sizeof(pathname));
 
                 slash = strrchr(pathname, '/');
                 if (!slash)
                     slash = pathname;
                 else
                     slash++;
                 strcpy(slash, "ffmpeg");
 
8bf61f5b
                 http_log("Launch commandline: ");
                 http_log("%s ", pathname);
                 for (i = 1; feed->child_argv[i] && feed->child_argv[i][0]; i++)
                     http_log("%s ", feed->child_argv[i]);
                 http_log("\n");
40444a59
 
611c5741
                 for (i = 3; i < 256; i++)
5eb765ef
                     close(i);
cde25790
 
5eb765ef
                 if (!ffserver_debug) {
2ac887ba
                     i = open("/dev/null", O_RDWR);
3296409d
                     if (i != -1) {
2ac887ba
                         dup2(i, 0);
3296409d
                         dup2(i, 1);
                         dup2(i, 2);
5eb765ef
                         close(i);
3296409d
                     }
2ac887ba
                 }
cde25790
 
d6562d2c
                 /* This is needed to make relative pathnames work */
                 chdir(my_program_dir);
 
a4d70941
                 signal(SIGPIPE, SIG_DFL);
 
cde25790
                 execvp(pathname, feed->child_argv);
 
                 _exit(1);
             }
         }
     }
7434ba6d
 }
 
2effd274
 /* open a listening socket */
 static int socket_open_listen(struct sockaddr_in *my_addr)
85f07f22
 {
2effd274
     int server_fd, tmp;
85f07f22
 
     server_fd = socket(AF_INET,SOCK_STREAM,0);
     if (server_fd < 0) {
         perror ("socket");
         return -1;
     }
115329f1
 
85f07f22
     tmp = 1;
     setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
 
2effd274
     if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
b17d099d
         char bindmsg[32];
         snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
         perror (bindmsg);
d96633bb
         closesocket(server_fd);
85f07f22
         return -1;
     }
115329f1
 
85f07f22
     if (listen (server_fd, 5) < 0) {
         perror ("listen");
d96633bb
         closesocket(server_fd);
85f07f22
         return -1;
     }
ba472aaf
     ff_socket_nonblock(server_fd, 1);
2effd274
 
     return server_fd;
 }
 
6edd6884
 /* start all multicast streams */
 static void start_multicast(void)
 {
     FFStream *stream;
     char session_id[32];
     HTTPContext *rtp_c;
     struct sockaddr_in dest_addr;
     int default_port, stream_index;
 
     default_port = 6000;
     for(stream = first_stream; stream != NULL; stream = stream->next) {
         if (stream->is_multicast) {
             /* open the RTP connection */
1df93ae9
             snprintf(session_id, sizeof(session_id), "%08x%08x",
                      av_random(&random_state), av_random(&random_state));
6edd6884
 
             /* choose a port if none given */
             if (stream->multicast_port == 0) {
                 stream->multicast_port = default_port;
                 default_port += 100;
             }
 
             dest_addr.sin_family = AF_INET;
             dest_addr.sin_addr = stream->multicast_ip;
             dest_addr.sin_port = htons(stream->multicast_port);
 
115329f1
             rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
90abbdba
                                        RTSP_LOWER_TRANSPORT_UDP_MULTICAST);
611c5741
             if (!rtp_c)
6edd6884
                 continue;
611c5741
 
6edd6884
             if (open_input_stream(rtp_c, "") < 0) {
b4befb99
                 http_log("Could not open input stream for stream '%s'\n",
                          stream->filename);
6edd6884
                 continue;
             }
 
             /* open each RTP stream */
115329f1
             for(stream_index = 0; stream_index < stream->nb_streams;
6edd6884
                 stream_index++) {
115329f1
                 dest_addr.sin_port = htons(stream->multicast_port +
6edd6884
                                            2 * stream_index);
bc351386
                 if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
b4befb99
                     http_log("Could not open output stream '%s/streamid=%d'\n",
                              stream->filename, stream_index);
0fa45e19
                     exit(1);
6edd6884
                 }
             }
 
             /* change state to send data */
             rtp_c->state = HTTPSTATE_SEND_DATA;
         }
     }
 }
2effd274
 
 /* main loop of the http server */
 static int http_server(void)
 {
d2a1ea1d
     int server_fd = 0, rtsp_server_fd = 0;
     int ret, delay, delay1;
1c9ff179
     struct pollfd *poll_table, *poll_entry;
2effd274
     HTTPContext *c, *c_next;
85f07f22
 
a7f361eb
     if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
1c9ff179
         http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
         return -1;
     }
 
d2a1ea1d
     if (my_http_addr.sin_port) {
2b9cd1e7
         server_fd = socket_open_listen(&my_http_addr);
         if (server_fd < 0)
             return -1;
d2a1ea1d
     }
2effd274
 
d2a1ea1d
     if (my_rtsp_addr.sin_port) {
2b9cd1e7
         rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
         if (rtsp_server_fd < 0)
             return -1;
d2a1ea1d
     }
 
     if (!rtsp_server_fd && !server_fd) {
         http_log("HTTP and RTSP disabled.\n");
         return -1;
     }
115329f1
 
a3341b9d
     http_log("FFserver started.\n");
85f07f22
 
cde25790
     start_children(first_feed);
 
6edd6884
     start_multicast();
 
85f07f22
     for(;;) {
         poll_entry = poll_table;
d2a1ea1d
         if (server_fd) {
2b9cd1e7
             poll_entry->fd = server_fd;
             poll_entry->events = POLLIN;
             poll_entry++;
d2a1ea1d
         }
         if (rtsp_server_fd) {
2b9cd1e7
             poll_entry->fd = rtsp_server_fd;
             poll_entry->events = POLLIN;
             poll_entry++;
d2a1ea1d
         }
2effd274
 
85f07f22
         /* wait for events on each HTTP handle */
         c = first_http_ctx;
2effd274
         delay = 1000;
85f07f22
         while (c != NULL) {
             int fd;
             fd = c->fd;
             switch(c->state) {
2effd274
             case HTTPSTATE_SEND_HEADER:
             case RTSPSTATE_SEND_REPLY:
bc351386
             case RTSPSTATE_SEND_PACKET:
85f07f22
                 c->poll_entry = poll_entry;
                 poll_entry->fd = fd;
2effd274
                 poll_entry->events = POLLOUT;
85f07f22
                 poll_entry++;
                 break;
             case HTTPSTATE_SEND_DATA_HEADER:
             case HTTPSTATE_SEND_DATA:
             case HTTPSTATE_SEND_DATA_TRAILER:
2effd274
                 if (!c->is_packetized) {
                     /* for TCP, we output as much as we can (may need to put a limit) */
                     c->poll_entry = poll_entry;
                     poll_entry->fd = fd;
                     poll_entry->events = POLLOUT;
                     poll_entry++;
                 } else {
e240a0bb
                     /* when ffserver is doing the timing, we work by
                        looking at which packet need to be sent every
                        10 ms */
                     delay1 = 10; /* one tick wait XXX: 10 ms assumed */
                     if (delay1 < delay)
                         delay = delay1;
2effd274
                 }
85f07f22
                 break;
2effd274
             case HTTPSTATE_WAIT_REQUEST:
85f07f22
             case HTTPSTATE_RECEIVE_DATA:
             case HTTPSTATE_WAIT_FEED:
2effd274
             case RTSPSTATE_WAIT_REQUEST:
85f07f22
                 /* need to catch errors */
                 c->poll_entry = poll_entry;
                 poll_entry->fd = fd;
a6e14edd
                 poll_entry->events = POLLIN;/* Maybe this will work */
85f07f22
                 poll_entry++;
                 break;
             default:
                 c->poll_entry = NULL;
                 break;
             }
             c = c->next;
         }
 
         /* wait for an event on one connection. We poll at least every
            second to handle timeouts */
         do {
2effd274
             ret = poll(poll_table, poll_entry - poll_table, delay);
8da4034f
             if (ret < 0 && ff_neterrno() != FF_NETERROR(EAGAIN) &&
                 ff_neterrno() != FF_NETERROR(EINTR))
53e2f9ca
                 return -1;
e8d658df
         } while (ret < 0);
115329f1
 
c3f58185
         cur_time = av_gettime() / 1000;
85f07f22
 
5eb765ef
         if (need_to_start_children) {
             need_to_start_children = 0;
             start_children(first_feed);
         }
 
85f07f22
         /* now handle the events */
2effd274
         for(c = first_http_ctx; c != NULL; c = c_next) {
             c_next = c->next;
             if (handle_connection(c) < 0) {
85f07f22
                 /* close and free the connection */
7434ba6d
                 log_connection(c);
2effd274
                 close_connection(c);
85f07f22
             }
         }
 
         poll_entry = poll_table;
d2a1ea1d
         if (server_fd) {
2b9cd1e7
             /* new HTTP connection request ? */
             if (poll_entry->revents & POLLIN)
                 new_connection(server_fd, 0);
             poll_entry++;
d2a1ea1d
         }
         if (rtsp_server_fd) {
2b9cd1e7
             /* new RTSP connection request ? */
             if (poll_entry->revents & POLLIN)
                 new_connection(rtsp_server_fd, 1);
d2a1ea1d
         }
85f07f22
     }
 }
 
2effd274
 /* start waiting for a new HTTP/RTSP request */
 static void start_wait_request(HTTPContext *c, int is_rtsp)
85f07f22
 {
2effd274
     c->buffer_ptr = c->buffer;
     c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
 
     if (is_rtsp) {
         c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
         c->state = RTSPSTATE_WAIT_REQUEST;
     } else {
         c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
         c->state = HTTPSTATE_WAIT_REQUEST;
     }
 }
 
 static void new_connection(int server_fd, int is_rtsp)
 {
     struct sockaddr_in from_addr;
     int fd, len;
     HTTPContext *c = NULL;
 
     len = sizeof(from_addr);
115329f1
     fd = accept(server_fd, (struct sockaddr *)&from_addr,
2effd274
                 &len);
050056d0
     if (fd < 0) {
         http_log("error during accept %s\n", strerror(errno));
2effd274
         return;
050056d0
     }
ba472aaf
     ff_socket_nonblock(fd, 1);
2effd274
 
     /* XXX: should output a warning page when coming
        close to the connection limit */
     if (nb_connections >= nb_max_connections)
         goto fail;
115329f1
 
2effd274
     /* add a new connection */
     c = av_mallocz(sizeof(HTTPContext));
     if (!c)
         goto fail;
115329f1
 
2effd274
     c->fd = fd;
     c->poll_entry = NULL;
     c->from_addr = from_addr;
     c->buffer_size = IOBUFFER_INIT_SIZE;
     c->buffer = av_malloc(c->buffer_size);
     if (!c->buffer)
         goto fail;
8bc80f8b
 
     c->next = first_http_ctx;
     first_http_ctx = c;
2effd274
     nb_connections++;
115329f1
 
2effd274
     start_wait_request(c, is_rtsp);
 
     return;
 
  fail:
     if (c) {
         av_free(c->buffer);
         av_free(c);
     }
d96633bb
     closesocket(fd);
2effd274
 }
 
 static void close_connection(HTTPContext *c)
 {
     HTTPContext **cp, *c1;
     int i, nb_streams;
     AVFormatContext *ctx;
     URLContext *h;
     AVStream *st;
 
     /* remove connection from list */
     cp = &first_http_ctx;
     while ((*cp) != NULL) {
         c1 = *cp;
611c5741
         if (c1 == c)
2effd274
             *cp = c->next;
611c5741
         else
2effd274
             cp = &c1->next;
     }
 
bc351386
     /* remove references, if any (XXX: do it faster) */
     for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
         if (c1->rtsp_c == c)
             c1->rtsp_c = NULL;
     }
 
2effd274
     /* remove connection associated resources */
     if (c->fd >= 0)
d96633bb
         closesocket(c->fd);
2effd274
     if (c->fmt_in) {
         /* close each frame parser */
         for(i=0;i<c->fmt_in->nb_streams;i++) {
             st = c->fmt_in->streams[i];
611c5741
             if (st->codec->codec)
01f4895c
                 avcodec_close(st->codec);
2effd274
         }
         av_close_input_file(c->fmt_in);
     }
 
     /* free RTP output streams if any */
     nb_streams = 0;
115329f1
     if (c->stream)
2effd274
         nb_streams = c->stream->nb_streams;
115329f1
 
2effd274
     for(i=0;i<nb_streams;i++) {
         ctx = c->rtp_ctx[i];
         if (ctx) {
             av_write_trailer(ctx);
             av_free(ctx);
         }
         h = c->rtp_handles[i];
611c5741
         if (h)
2effd274
             url_close(h);
     }
115329f1
 
b88ba823
     ctx = &c->fmt_ctx;
 
637b638e
     if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
87638494
         if (ctx->oformat) {
             /* prepare header */
             if (url_open_dyn_buf(&ctx->pb) >= 0) {
                 av_write_trailer(ctx);
f8b06be9
                 av_freep(&c->pb_buffer);
899681cd
                 url_close_dyn_buf(ctx->pb, &c->pb_buffer);
87638494
             }
         }
     }
 
115329f1
     for(i=0; i<ctx->nb_streams; i++)
0bd53967
         av_free(ctx->streams[i]);
f0ef6240
 
edfdd798
     if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
6edd6884
         current_bandwidth -= c->stream->bandwidth;
5400e092
 
     /* signal that there is no feed if we are the feeder socket */
     if (c->state == HTTPSTATE_RECEIVE_DATA && c->stream) {
         c->stream->feed_opened = 0;
         close(c->feed_fd);
     }
 
2effd274
     av_freep(&c->pb_buffer);
bc351386
     av_freep(&c->packet_buffer);
2effd274
     av_free(c->buffer);
     av_free(c);
     nb_connections--;
 }
 
 static int handle_connection(HTTPContext *c)
 {
     int len, ret;
115329f1
 
85f07f22
     switch(c->state) {
     case HTTPSTATE_WAIT_REQUEST:
2effd274
     case RTSPSTATE_WAIT_REQUEST:
85f07f22
         /* timeout ? */
         if ((c->timeout - cur_time) < 0)
             return -1;
         if (c->poll_entry->revents & (POLLERR | POLLHUP))
             return -1;
 
         /* no need to read if no events */
         if (!(c->poll_entry->revents & POLLIN))
             return 0;
         /* read the data */
1bc1cfdd
     read_loop:
c60202df
         len = recv(c->fd, c->buffer_ptr, 1, 0);
85f07f22
         if (len < 0) {
8da4034f
             if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
                 ff_neterrno() != FF_NETERROR(EINTR))
85f07f22
                 return -1;
         } else if (len == 0) {
             return -1;
         } else {
94d9ad5f
             /* search for end of request. */
0c1a9eda
             uint8_t *ptr;
85f07f22
             c->buffer_ptr += len;
             ptr = c->buffer_ptr;
             if ((ptr >= c->buffer + 2 && !memcmp(ptr-2, "\n\n", 2)) ||
                 (ptr >= c->buffer + 4 && !memcmp(ptr-4, "\r\n\r\n", 4))) {
                 /* request found : parse it and reply */
2effd274
                 if (c->state == HTTPSTATE_WAIT_REQUEST) {
                     ret = http_parse_request(c);
                 } else {
                     ret = rtsp_parse_request(c);
                 }
                 if (ret < 0)
85f07f22
                     return -1;
             } else if (ptr >= c->buffer_end) {
                 /* request too long: cannot do anything */
                 return -1;
1bc1cfdd
             } else goto read_loop;
85f07f22
         }
         break;
 
     case HTTPSTATE_SEND_HEADER:
         if (c->poll_entry->revents & (POLLERR | POLLHUP))
             return -1;
 
2effd274
         /* no need to write if no events */
85f07f22
         if (!(c->poll_entry->revents & POLLOUT))
             return 0;
c60202df
         len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
85f07f22
         if (len < 0) {
8da4034f
             if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
                 ff_neterrno() != FF_NETERROR(EINTR)) {
85f07f22
                 /* error : close connection */
2effd274
                 av_freep(&c->pb_buffer);
85f07f22
                 return -1;
             }
         } else {
             c->buffer_ptr += len;
2e04edb3
             if (c->stream)
                 c->stream->bytes_served += len;
a6e14edd
             c->data_count += len;
85f07f22
             if (c->buffer_ptr >= c->buffer_end) {
2effd274
                 av_freep(&c->pb_buffer);
85f07f22
                 /* if error, exit */
611c5741
                 if (c->http_error)
85f07f22
                     return -1;
2effd274
                 /* all the buffer was sent : synchronize to the incoming stream */
85f07f22
                 c->state = HTTPSTATE_SEND_DATA_HEADER;
                 c->buffer_ptr = c->buffer_end = c->buffer;
             }
         }
         break;
 
     case HTTPSTATE_SEND_DATA:
     case HTTPSTATE_SEND_DATA_HEADER:
     case HTTPSTATE_SEND_DATA_TRAILER:
2effd274
         /* for packetized output, we consider we can always write (the
            input streams sets the speed). It may be better to verify
            that we do not rely too much on the kernel queues */
         if (!c->is_packetized) {
             if (c->poll_entry->revents & (POLLERR | POLLHUP))
                 return -1;
115329f1
 
2effd274
             /* no need to read if no events */
             if (!(c->poll_entry->revents & POLLOUT))
                 return 0;
         }
5eb765ef
         if (http_send_data(c) < 0)
85f07f22
             return -1;
638831aa
         /* close connection if trailer sent */
         if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
             return -1;
85f07f22
         break;
     case HTTPSTATE_RECEIVE_DATA:
         /* no need to read if no events */
         if (c->poll_entry->revents & (POLLERR | POLLHUP))
             return -1;
         if (!(c->poll_entry->revents & POLLIN))
             return 0;
         if (http_receive_data(c) < 0)
             return -1;
         break;
     case HTTPSTATE_WAIT_FEED:
         /* no need to read if no events */
a6e14edd
         if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
85f07f22
             return -1;
 
         /* nothing to do, we'll be waken up by incoming feed packets */
         break;
2effd274
 
     case RTSPSTATE_SEND_REPLY:
         if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
             av_freep(&c->pb_buffer);
             return -1;
         }
         /* no need to write if no events */
         if (!(c->poll_entry->revents & POLLOUT))
             return 0;
c60202df
         len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
2effd274
         if (len < 0) {
8da4034f
             if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
                 ff_neterrno() != FF_NETERROR(EINTR)) {
2effd274
                 /* error : close connection */
                 av_freep(&c->pb_buffer);
                 return -1;
             }
         } else {
             c->buffer_ptr += len;
             c->data_count += len;
             if (c->buffer_ptr >= c->buffer_end) {
                 /* all the buffer was sent : wait for a new request */
                 av_freep(&c->pb_buffer);
                 start_wait_request(c, 1);
             }
         }
         break;
bc351386
     case RTSPSTATE_SEND_PACKET:
         if (c->poll_entry->revents & (POLLERR | POLLHUP)) {
             av_freep(&c->packet_buffer);
             return -1;
         }
         /* no need to write if no events */
         if (!(c->poll_entry->revents & POLLOUT))
             return 0;
c60202df
         len = send(c->fd, c->packet_buffer_ptr,
                     c->packet_buffer_end - c->packet_buffer_ptr, 0);
bc351386
         if (len < 0) {
8da4034f
             if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
                 ff_neterrno() != FF_NETERROR(EINTR)) {
bc351386
                 /* error : close connection */
                 av_freep(&c->packet_buffer);
                 return -1;
             }
         } else {
             c->packet_buffer_ptr += len;
             if (c->packet_buffer_ptr >= c->packet_buffer_end) {
                 /* all the buffer was sent : wait for a new request */
                 av_freep(&c->packet_buffer);
                 c->state = RTSPSTATE_WAIT_REQUEST;
             }
         }
         break;
2effd274
     case HTTPSTATE_READY:
         /* nothing to do */
         break;
85f07f22
     default:
         return -1;
     }
     return 0;
 }
 
3120d2a2
 static int extract_rates(char *rates, int ratelen, const char *request)
 {
     const char *p;
 
     for (p = request; *p && *p != '\r' && *p != '\n'; ) {
         if (strncasecmp(p, "Pragma:", 7) == 0) {
             const char *q = p + 7;
 
             while (*q && *q != '\n' && isspace(*q))
                 q++;
 
             if (strncasecmp(q, "stream-switch-entry=", 20) == 0) {
                 int stream_no;
                 int rate_no;
 
                 q += 20;
 
cde25790
                 memset(rates, 0xff, ratelen);
3120d2a2
 
                 while (1) {
                     while (*q && *q != '\n' && *q != ':')
                         q++;
 
611c5741
                     if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
3120d2a2
                         break;
611c5741
 
3120d2a2
                     stream_no--;
611c5741
                     if (stream_no < ratelen && stream_no >= 0)
3120d2a2
                         rates[stream_no] = rate_no;
 
                     while (*q && *q != '\n' && !isspace(*q))
                         q++;
                 }
 
                 return 1;
             }
         }
         p = strchr(p, '\n');
         if (!p)
             break;
 
         p++;
     }
 
     return 0;
 }
 
cde25790
 static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
3120d2a2
 {
     int i;
cde25790
     int best_bitrate = 100000000;
     int best = -1;
 
     for (i = 0; i < feed->nb_streams; i++) {
01f4895c
         AVCodecContext *feed_codec = feed->streams[i]->codec;
cde25790
 
         if (feed_codec->codec_id != codec->codec_id ||
             feed_codec->sample_rate != codec->sample_rate ||
             feed_codec->width != codec->width ||
611c5741
             feed_codec->height != codec->height)
cde25790
             continue;
 
         /* Potential stream */
 
115329f1
         /* We want the fastest stream less than bit_rate, or the slowest
cde25790
          * faster than bit_rate
          */
 
         if (feed_codec->bit_rate <= bit_rate) {
             if (best_bitrate > bit_rate || feed_codec->bit_rate > best_bitrate) {
                 best_bitrate = feed_codec->bit_rate;
                 best = i;
             }
         } else {
             if (feed_codec->bit_rate < best_bitrate) {
                 best_bitrate = feed_codec->bit_rate;
                 best = i;
             }
         }
     }
 
     return best;
 }
 
 static int modify_current_stream(HTTPContext *c, char *rates)
 {
     int i;
     FFStream *req = c->stream;
     int action_required = 0;
3120d2a2
 
001bcd29
     /* Not much we can do for a feed */
     if (!req->feed)
         return 0;
 
3120d2a2
     for (i = 0; i < req->nb_streams; i++) {
01f4895c
         AVCodecContext *codec = req->streams[i]->codec;
3120d2a2
 
         switch(rates[i]) {
             case 0:
cde25790
                 c->switch_feed_streams[i] = req->feed_streams[i];
3120d2a2
                 break;
             case 1:
cde25790
                 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
3120d2a2
                 break;
             case 2:
cde25790
                 /* Wants off or slow */
                 c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 4);
 #ifdef WANTS_OFF
                 /* This doesn't work well when it turns off the only stream! */
                 c->switch_feed_streams[i] = -2;
                 c->feed_streams[i] = -2;
 #endif
3120d2a2
                 break;
         }
 
cde25790
         if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
             action_required = 1;
     }
3120d2a2
 
cde25790
     return action_required;
 }
3120d2a2
 
 
cde25790
 static void do_switch_stream(HTTPContext *c, int i)
 {
     if (c->switch_feed_streams[i] >= 0) {
115329f1
 #ifdef PHILIP
cde25790
         c->feed_streams[i] = c->switch_feed_streams[i];
 #endif
3120d2a2
 
cde25790
         /* Now update the stream */
3120d2a2
     }
cde25790
     c->switch_feed_streams[i] = -1;
3120d2a2
 }
7434ba6d
 
2effd274
 /* XXX: factorize in utils.c ? */
 /* XXX: take care with different space meaning */
 static void skip_spaces(const char **pp)
 {
     const char *p;
     p = *pp;
     while (*p == ' ' || *p == '\t')
         p++;
     *pp = p;
 }
 
 static void get_word(char *buf, int buf_size, const char **pp)
 {
     const char *p;
     char *q;
 
     p = *pp;
     skip_spaces(&p);
     q = buf;
     while (!isspace(*p) && *p != '\0') {
         if ((q - buf) < buf_size - 1)
             *q++ = *p;
         p++;
     }
     if (buf_size > 0)
         *q = '\0';
     *pp = p;
 }
 
8256c0a3
 static int validate_acl(FFStream *stream, HTTPContext *c)
 {
     enum IPAddressAction last_action = IP_DENY;
     IPAddressACL *acl;
     struct in_addr *src = &c->from_addr.sin_addr;
2bd8416e
     unsigned long src_addr = src->s_addr;
8256c0a3
 
     for (acl = stream->acl; acl; acl = acl->next) {
611c5741
         if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
8256c0a3
             return (acl->action == IP_ALLOW) ? 1 : 0;
         last_action = acl->action;
     }
 
     /* Nothing matched, so return not the last action */
     return (last_action == IP_DENY) ? 1 : 0;
 }
 
829ac53d
 /* compute the real filename of a file by matching it without its
    extensions to all the stream filenames */
 static void compute_real_filename(char *filename, int max_size)
 {
     char file1[1024];
     char file2[1024];
     char *p;
     FFStream *stream;
 
     /* compute filename by matching without the file extensions */
f7d78f36
     av_strlcpy(file1, filename, sizeof(file1));
829ac53d
     p = strrchr(file1, '.');
     if (p)
         *p = '\0';
     for(stream = first_stream; stream != NULL; stream = stream->next) {
f7d78f36
         av_strlcpy(file2, stream->filename, sizeof(file2));
829ac53d
         p = strrchr(file2, '.');
         if (p)
             *p = '\0';
         if (!strcmp(file1, file2)) {
f7d78f36
             av_strlcpy(filename, stream->filename, max_size);
829ac53d
             break;
         }
     }
 }
 
 enum RedirType {
     REDIR_NONE,
     REDIR_ASX,
     REDIR_RAM,
     REDIR_ASF,
     REDIR_RTSP,
     REDIR_SDP,
 };
 
85f07f22
 /* parse http request and prepare header */
 static int http_parse_request(HTTPContext *c)
 {
     char *p;
829ac53d
     enum RedirType redir_type;
85f07f22
     char cmd[32];
bae79c04
     char info[1024], filename[1024];
85f07f22
     char url[1024], *q;
     char protocol[32];
     char msg[1024];
     const char *mime_type;
     FFStream *stream;
42a63c6a
     int i;
3120d2a2
     char ratebuf[32];
cde25790
     char *useragent = 0;
85f07f22
 
     p = c->buffer;
2effd274
     get_word(cmd, sizeof(cmd), (const char **)&p);
f7d78f36
     av_strlcpy(c->method, cmd, sizeof(c->method));
7434ba6d
 
85f07f22
     if (!strcmp(cmd, "GET"))
edfdd798
         c->post = 0;
85f07f22
     else if (!strcmp(cmd, "POST"))
edfdd798
         c->post = 1;
85f07f22
     else
         return -1;
 
2effd274
     get_word(url, sizeof(url), (const char **)&p);
f7d78f36
     av_strlcpy(c->url, url, sizeof(c->url));
7434ba6d
 
2effd274
     get_word(protocol, sizeof(protocol), (const char **)&p);
85f07f22
     if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
         return -1;
7434ba6d
 
f7d78f36
     av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
90f9c440
 
     if (ffserver_debug)
bb270c08
         http_log("New connection: %s %s\n", cmd, url);
115329f1
 
85f07f22
     /* find the filename and the optional info string in the request */
bae79c04
     p = strchr(url, '?');
85f07f22
     if (p) {
f7d78f36
         av_strlcpy(info, p, sizeof(info));
85f07f22
         *p = '\0';
611c5741
     } else
85f07f22
         info[0] = '\0';
 
f7d78f36
     av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
bae79c04
 
cde25790
     for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
         if (strncasecmp(p, "User-Agent:", 11) == 0) {
             useragent = p + 11;
             if (*useragent && *useragent != '\n' && isspace(*useragent))
                 useragent++;
             break;
         }
         p = strchr(p, '\n');
         if (!p)
             break;
 
         p++;
     }
 
829ac53d
     redir_type = REDIR_NONE;
     if (match_ext(filename, "asx")) {
         redir_type = REDIR_ASX;
7434ba6d
         filename[strlen(filename)-1] = 'f';
c2ce254c
     } else if (match_ext(filename, "asf") &&
cde25790
         (!useragent || strncasecmp(useragent, "NSPlayer", 8) != 0)) {
         /* if this isn't WMP or lookalike, return the redirector file */
829ac53d
         redir_type = REDIR_ASF;
     } else if (match_ext(filename, "rpm,ram")) {
         redir_type = REDIR_RAM;
42a63c6a
         strcpy(filename + strlen(filename)-2, "m");
829ac53d
     } else if (match_ext(filename, "rtsp")) {
         redir_type = REDIR_RTSP;
bae79c04
         compute_real_filename(filename, sizeof(filename) - 1);
829ac53d
     } else if (match_ext(filename, "sdp")) {
         redir_type = REDIR_SDP;
bae79c04
         compute_real_filename(filename, sizeof(filename) - 1);
42a63c6a
     }
115329f1
 
bae79c04
     // "redirect" / request to index.html
     if (!strlen(filename))
f7d78f36
         av_strlcpy(filename, "index.html", sizeof(filename) - 1);
bae79c04
 
85f07f22
     stream = first_stream;
     while (stream != NULL) {
8256c0a3
         if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
85f07f22
             break;
         stream = stream->next;
     }
     if (stream == NULL) {
d445a7e9
         snprintf(msg, sizeof(msg), "File '%s' not found", url);
85f07f22
         goto send_error;
     }
42a63c6a
 
cde25790
     c->stream = stream;
     memcpy(c->feed_streams, stream->feed_streams, sizeof(c->feed_streams));
     memset(c->switch_feed_streams, -1, sizeof(c->switch_feed_streams));
 
     if (stream->stream_type == STREAM_TYPE_REDIRECT) {
         c->http_error = 301;
         q = c->buffer;
a3aa4fed
         q += snprintf(q, c->buffer_size,
                       "HTTP/1.0 301 Moved\r\n"
                       "Location: %s\r\n"
                       "Content-type: text/html\r\n"
                       "\r\n"
                       "<html><head><title>Moved</title></head><body>\r\n"
                       "You should be <a href=\"%s\">redirected</a>.\r\n"
                       "</body></html>\r\n", stream->feed_filename, stream->feed_filename);
cde25790
         /* prepare output buffer */
         c->buffer_ptr = c->buffer;
         c->buffer_end = q;
         c->state = HTTPSTATE_SEND_HEADER;
         return 0;
     }
 
3120d2a2
     /* If this is WMP, get the rate information */
     if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
cde25790
         if (modify_current_stream(c, ratebuf)) {
37d3e066
             for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
cde25790
                 if (c->switch_feed_streams[i] >= 0)
                     do_switch_stream(c, i);
             }
         }
3120d2a2
     }
 
d8f28a77
     if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
         current_bandwidth += stream->bandwidth;
 
755bfeab
     /* If already streaming this feed, do not let start another feeder. */
d0a5513b
     if (stream->feed_opened) {
         snprintf(msg, sizeof(msg), "This feed is already being received.");
14c43f91
         http_log("feed %s already being received\n", stream->feed_filename);
d0a5513b
         goto send_error;
     }
 
edfdd798
     if (c->post == 0 && max_bandwidth < current_bandwidth) {
42a63c6a
         c->http_error = 200;
         q = c->buffer;
a3aa4fed
         q += snprintf(q, c->buffer_size,
                       "HTTP/1.0 200 Server too busy\r\n"
                       "Content-type: text/html\r\n"
                       "\r\n"
                       "<html><head><title>Too busy</title></head><body>\r\n"
                       "<p>The server is too busy to serve your request at this time.</p>\r\n"
0be4b8d9
                       "<p>The bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
                       "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
a3aa4fed
                       "</body></html>\r\n", current_bandwidth, max_bandwidth);
42a63c6a
         /* prepare output buffer */
         c->buffer_ptr = c->buffer;
         c->buffer_end = q;
         c->state = HTTPSTATE_SEND_HEADER;
         return 0;
     }
115329f1
 
829ac53d
     if (redir_type != REDIR_NONE) {
7434ba6d
         char *hostinfo = 0;
115329f1
 
7434ba6d
         for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
             if (strncasecmp(p, "Host:", 5) == 0) {
                 hostinfo = p + 5;
                 break;
             }
             p = strchr(p, '\n');
             if (!p)
                 break;
 
             p++;
         }
 
         if (hostinfo) {
             char *eoh;
             char hostbuf[260];
 
             while (isspace(*hostinfo))
                 hostinfo++;
 
             eoh = strchr(hostinfo, '\n');
             if (eoh) {
                 if (eoh[-1] == '\r')
                     eoh--;
 
                 if (eoh - hostinfo < sizeof(hostbuf) - 1) {
                     memcpy(hostbuf, hostinfo, eoh - hostinfo);
                     hostbuf[eoh - hostinfo] = 0;
 
                     c->http_error = 200;
                     q = c->buffer;
829ac53d
                     switch(redir_type) {
                     case REDIR_ASX:
a3aa4fed
                         q += snprintf(q, c->buffer_size,
                                       "HTTP/1.0 200 ASX Follows\r\n"
                                       "Content-type: video/x-ms-asf\r\n"
                                       "\r\n"
                                       "<ASX Version=\"3\">\r\n"
                                       //"<!-- Autogenerated by ffserver -->\r\n"
                                       "<ENTRY><REF HREF=\"http://%s/%s%s\"/></ENTRY>\r\n"
                                       "</ASX>\r\n", hostbuf, filename, info);
829ac53d
                         break;
                     case REDIR_RAM:
a3aa4fed
                         q += snprintf(q, c->buffer_size,
                                       "HTTP/1.0 200 RAM Follows\r\n"
                                       "Content-type: audio/x-pn-realaudio\r\n"
                                       "\r\n"
                                       "# Autogenerated by ffserver\r\n"
                                       "http://%s/%s%s\r\n", hostbuf, filename, info);
829ac53d
                         break;
                     case REDIR_ASF:
a3aa4fed
                         q += snprintf(q, c->buffer_size,
                                       "HTTP/1.0 200 ASF Redirect follows\r\n"
                                       "Content-type: video/x-ms-asf\r\n"
                                       "\r\n"
                                       "[Reference]\r\n"
                                       "Ref1=http://%s/%s%s\r\n", hostbuf, filename, info);
829ac53d
                         break;
                     case REDIR_RTSP:
                         {
                             char hostname[256], *p;
                             /* extract only hostname */
f7d78f36
                             av_strlcpy(hostname, hostbuf, sizeof(hostname));
829ac53d
                             p = strrchr(hostname, ':');
                             if (p)
                                 *p = '\0';
a3aa4fed
                             q += snprintf(q, c->buffer_size,
                                           "HTTP/1.0 200 RTSP Redirect follows\r\n"
                                           /* XXX: incorrect mime type ? */
                                           "Content-type: application/x-rtsp\r\n"
                                           "\r\n"
                                           "rtsp://%s:%d/%s\r\n", hostname, ntohs(my_rtsp_addr.sin_port), filename);
829ac53d
                         }
                         break;
                     case REDIR_SDP:
                         {
0c1a9eda
                             uint8_t *sdp_data;
829ac53d
                             int sdp_data_size, len;
                             struct sockaddr_in my_addr;
 
a3aa4fed
                             q += snprintf(q, c->buffer_size,
                                           "HTTP/1.0 200 OK\r\n"
                                           "Content-type: application/sdp\r\n"
                                           "\r\n");
829ac53d
 
                             len = sizeof(my_addr);
                             getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
115329f1
 
829ac53d
                             /* XXX: should use a dynamic buffer */
115329f1
                             sdp_data_size = prepare_sdp_description(stream,
                                                                     &sdp_data,
829ac53d
                                                                     my_addr.sin_addr);
                             if (sdp_data_size > 0) {
                                 memcpy(q, sdp_data, sdp_data_size);
                                 q += sdp_data_size;
                                 *q = '\0';
                                 av_free(sdp_data);
                             }
                         }
                         break;
                     default:
0f4e8165
                         abort();
829ac53d
                         break;
2effd274
                     }
7434ba6d
 
                     /* prepare output buffer */
                     c->buffer_ptr = c->buffer;
                     c->buffer_end = q;
                     c->state = HTTPSTATE_SEND_HEADER;
                     return 0;
                 }
             }
         }
 
d445a7e9
         snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
7434ba6d
         goto send_error;
85f07f22
     }
 
a6e14edd
     stream->conns_served++;
7434ba6d
 
85f07f22
     /* XXX: add there authenticate and IP match */
 
edfdd798
     if (c->post) {
85f07f22
         /* if post, it means a feed is being sent */
         if (!stream->is_feed) {
e16190fa
             /* However it might be a status report from WMP! Let us log the
              * data as it might come in handy one day. */
7434ba6d
             char *logline = 0;
3120d2a2
             int client_id = 0;
115329f1
 
7434ba6d
             for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
                 if (strncasecmp(p, "Pragma: log-line=", 17) == 0) {
                     logline = p;
                     break;
                 }
611c5741
                 if (strncasecmp(p, "Pragma: client-id=", 18) == 0)
3120d2a2
                     client_id = strtol(p + 18, 0, 10);
7434ba6d
                 p = strchr(p, '\n');
                 if (!p)
                     break;
 
                 p++;
             }
 
             if (logline) {
                 char *eol = strchr(logline, '\n');
 
                 logline += 17;
 
                 if (eol) {
                     if (eol[-1] == '\r')
                         eol--;
7906085f
                     http_log("%.*s\n", (int) (eol - logline), logline);
7434ba6d
                     c->suppress_log = 1;
                 }
             }
3120d2a2
 
cde25790
 #ifdef DEBUG_WMP
             http_log("\nGot request:\n%s\n", c->buffer);
3120d2a2
 #endif
 
             if (client_id && extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
                 HTTPContext *wmpc;
 
                 /* Now we have to find the client_id */
                 for (wmpc = first_http_ctx; wmpc; wmpc = wmpc->next) {
                     if (wmpc->wmp_client_id == client_id)
                         break;
                 }
 
2d563d2f
                 if (wmpc && modify_current_stream(wmpc, ratebuf))
                     wmpc->switch_pending = 1;
3120d2a2
             }
115329f1
 
d445a7e9
             snprintf(msg, sizeof(msg), "POST command not handled");
cb275dd9
             c->stream = 0;
85f07f22
             goto send_error;
         }
         if (http_start_receive_data(c) < 0) {
d445a7e9
             snprintf(msg, sizeof(msg), "could not open feed");
85f07f22
             goto send_error;
         }
         c->http_error = 0;
         c->state = HTTPSTATE_RECEIVE_DATA;
         return 0;
     }
 
cde25790
 #ifdef DEBUG_WMP
611c5741
     if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
cde25790
         http_log("\nGot request:\n%s\n", c->buffer);
3120d2a2
 #endif
 
85f07f22
     if (c->stream->stream_type == STREAM_TYPE_STATUS)
dca21085
         goto send_status;
85f07f22
 
     /* open input stream */
     if (open_input_stream(c, info) < 0) {
d445a7e9
         snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
85f07f22
         goto send_error;
     }
 
     /* prepare http header */
     q = c->buffer;
d445a7e9
     q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "HTTP/1.0 200 OK\r\n");
85f07f22
     mime_type = c->stream->fmt->mime_type;
     if (!mime_type)
087fa475
         mime_type = "application/x-octet-stream";
d445a7e9
     q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Pragma: no-cache\r\n");
85f07f22
 
     /* for asf, we need extra headers */
8256c0a3
     if (!strcmp(c->stream->fmt->name,"asf_stream")) {
3120d2a2
         /* Need to allocate a client id */
 
1df93ae9
         c->wmp_client_id = av_random(&random_state) & 0x7fffffff;
3120d2a2
 
d445a7e9
         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);
85f07f22
     }
d445a7e9
     q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "Content-Type: %s\r\n", mime_type);
     q += snprintf(q, q - (char *) c->buffer + c->buffer_size, "\r\n");
115329f1
 
85f07f22
     /* prepare output buffer */
     c->http_error = 0;
     c->buffer_ptr = c->buffer;
     c->buffer_end = q;
     c->state = HTTPSTATE_SEND_HEADER;
     return 0;
  send_error:
     c->http_error = 404;
     q = c->buffer;
a3aa4fed
     q += snprintf(q, c->buffer_size,
                   "HTTP/1.0 404 Not Found\r\n"
                   "Content-type: text/html\r\n"
                   "\r\n"
                   "<HTML>\n"
                   "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n"
                   "<BODY>%s</BODY>\n"
                   "</HTML>\n", msg);
85f07f22
     /* prepare output buffer */
     c->buffer_ptr = c->buffer;
     c->buffer_end = q;
     c->state = HTTPSTATE_SEND_HEADER;
     return 0;
dca21085
  send_status:
     compute_status(c);
85f07f22
     c->http_error = 200; /* horrible : we use this value to avoid
                             going to the send data state */
     c->state = HTTPSTATE_SEND_HEADER;
     return 0;
 }
 
0c1a9eda
 static void fmt_bytecount(ByteIOContext *pb, int64_t count)
2ac887ba
 {
     static const char *suffix = " kMGTP";
     const char *s;
 
611c5741
     for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
2ac887ba
 
4733abcb
     url_fprintf(pb, "%"PRId64"%c", count, *s);
2ac887ba
 }
 
dca21085
 static void compute_status(HTTPContext *c)
85f07f22
 {
     HTTPContext *c1;
     FFStream *stream;
2effd274
     char *p;
85f07f22
     time_t ti;
2effd274
     int i, len;
899681cd
     ByteIOContext *pb;
cde25790
 
899681cd
     if (url_open_dyn_buf(&pb) < 0) {
2effd274
         /* XXX: return an error ? */
cde25790
         c->buffer_ptr = c->buffer;
2effd274
         c->buffer_end = c->buffer;
         return;
cde25790
     }
85f07f22
 
2effd274
     url_fprintf(pb, "HTTP/1.0 200 OK\r\n");
     url_fprintf(pb, "Content-type: %s\r\n", "text/html");
     url_fprintf(pb, "Pragma: no-cache\r\n");
     url_fprintf(pb, "\r\n");
115329f1
 
31296f6e
     url_fprintf(pb, "<HTML><HEAD><TITLE>%s Status</TITLE>\n", program_name);
0679719d
     if (c->stream->feed_filename[0])
2effd274
         url_fprintf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
     url_fprintf(pb, "</HEAD>\n<BODY>");
17b01199
     url_fprintf(pb, "<H1>%s Status</H1>\n", program_name);
85f07f22
     /* format status */
2effd274
     url_fprintf(pb, "<H2>Available Streams</H2>\n");
     url_fprintf(pb, "<TABLE cellspacing=0 cellpadding=4>\n");
     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");
85f07f22
     stream = first_stream;
     while (stream != NULL) {
42a63c6a
         char sfilename[1024];
         char *eosf;
 
a6e14edd
         if (stream->feed != stream) {
f7d78f36
             av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
a6e14edd
             eosf = sfilename + strlen(sfilename);
             if (eosf - sfilename >= 4) {
611c5741
                 if (strcmp(eosf - 4, ".asf") == 0)
a6e14edd
                     strcpy(eosf - 4, ".asx");
611c5741
                 else if (strcmp(eosf - 3, ".rm") == 0)
a6e14edd
                     strcpy(eosf - 3, ".ram");
25e3e53d
                 else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
829ac53d
                     /* generate a sample RTSP director if
                        unicast. Generate an SDP redirector if
                        multicast */
2effd274
                     eosf = strrchr(sfilename, '.');
                     if (!eosf)
                         eosf = sfilename + strlen(sfilename);
829ac53d
                     if (stream->is_multicast)
                         strcpy(eosf, ".sdp");
                     else
                         strcpy(eosf, ".rtsp");
a6e14edd
                 }
42a63c6a
             }
115329f1
 
             url_fprintf(pb, "<TR><TD><A HREF=\"/%s\">%s</A> ",
a6e14edd
                          sfilename, stream->filename);
2effd274
             url_fprintf(pb, "<td align=right> %d <td align=right> ",
2ac887ba
                         stream->conns_served);
2effd274
             fmt_bytecount(pb, stream->bytes_served);
a6e14edd
             switch(stream->stream_type) {
ace21da3
             case STREAM_TYPE_LIVE: {
a6e14edd
                     int audio_bit_rate = 0;
                     int video_bit_rate = 0;
58445440
                     const char *audio_codec_name = "";
                     const char *video_codec_name = "";
                     const char *audio_codec_name_extra = "";
                     const char *video_codec_name_extra = "";
a6e14edd
 
                     for(i=0;i<stream->nb_streams;i++) {
                         AVStream *st = stream->streams[i];
01f4895c
                         AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
                         switch(st->codec->codec_type) {
a6e14edd
                         case CODEC_TYPE_AUDIO:
01f4895c
                             audio_bit_rate += st->codec->bit_rate;
a6e14edd
                             if (codec) {
                                 if (*audio_codec_name)
                                     audio_codec_name_extra = "...";
                                 audio_codec_name = codec->name;
                             }
                             break;
                         case CODEC_TYPE_VIDEO:
01f4895c
                             video_bit_rate += st->codec->bit_rate;
a6e14edd
                             if (codec) {
                                 if (*video_codec_name)
                                     video_codec_name_extra = "...";
                                 video_codec_name = codec->name;
                             }
                             break;
e240a0bb
                         case CODEC_TYPE_DATA:
01f4895c
                             video_bit_rate += st->codec->bit_rate;
e240a0bb
                             break;
a6e14edd
                         default:
0f4e8165
                             abort();
79c4ea3c
                         }
85f07f22
                     }
115329f1
                     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",
a6e14edd
                                  stream->fmt->name,
6edd6884
                                  stream->bandwidth,
a6e14edd
                                  video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
                                  audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
611c5741
                     if (stream->feed)
2effd274
                         url_fprintf(pb, "<TD>%s", stream->feed->filename);
611c5741
                     else
2effd274
                         url_fprintf(pb, "<TD>%s", stream->feed_filename);
                     url_fprintf(pb, "\n");
85f07f22
                 }
a6e14edd
                 break;
             default:
2effd274
                 url_fprintf(pb, "<TD align=center> - <TD align=right> - <TD align=right> - <td><td align=right> - <TD>\n");
a6e14edd
                 break;
85f07f22
             }
         }
         stream = stream->next;
     }
2effd274
     url_fprintf(pb, "</TABLE>\n");
a6e14edd
 
     stream = first_stream;
     while (stream != NULL) {
         if (stream->feed == stream) {
2effd274
             url_fprintf(pb, "<h2>Feed %s</h2>", stream->filename);
cde25790
             if (stream->pid) {
2effd274
                 url_fprintf(pb, "Running as pid %d.\n", stream->pid);
cde25790
 
2effd274
 #if defined(linux) && !defined(CONFIG_NOCUTILS)
                 {
                     FILE *pid_stat;
                     char ps_cmd[64];
 
                     /* This is somewhat linux specific I guess */
115329f1
                     snprintf(ps_cmd, sizeof(ps_cmd),
                              "ps -o \"%%cpu,cputime\" --no-headers %d",
2effd274
                              stream->pid);
115329f1
 
2effd274
                     pid_stat = popen(ps_cmd, "r");
                     if (pid_stat) {
                         char cpuperc[10];
                         char cpuused[64];
115329f1
 
                         if (fscanf(pid_stat, "%10s %64s", cpuperc,
2effd274
                                    cpuused) == 2) {
                             url_fprintf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
                                          cpuperc, cpuused);
                         }
                         fclose(pid_stat);
cde25790
                     }
                 }
 #endif
 
2effd274
                 url_fprintf(pb, "<p>");
cde25790
             }
2effd274
             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");
a6e14edd
 
             for (i = 0; i < stream->nb_streams; i++) {
                 AVStream *st = stream->streams[i];
01f4895c
                 AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
b29f97d1
                 const char *type = "unknown";
b582f314
                 char parameters[64];
 
                 parameters[0] = 0;
a6e14edd
 
01f4895c
                 switch(st->codec->codec_type) {
a6e14edd
                 case CODEC_TYPE_AUDIO:
                     type = "audio";
acdc8520
                     snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
a6e14edd
                     break;
                 case CODEC_TYPE_VIDEO:
                     type = "video";
01f4895c
                     snprintf(parameters, sizeof(parameters), "%dx%d, q=%d-%d, fps=%d", st->codec->width, st->codec->height,
                                 st->codec->qmin, st->codec->qmax, st->codec->time_base.den / st->codec->time_base.num);
a6e14edd
                     break;
                 default:
0f4e8165
                     abort();
a6e14edd
                 }
2effd274
                 url_fprintf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
01f4895c
                         i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
a6e14edd
             }
2effd274
             url_fprintf(pb, "</table>\n");
a6e14edd
 
115329f1
         }
a6e14edd
         stream = stream->next;
     }
115329f1
 
85f07f22
 #if 0
     {
         float avg;
         AVCodecContext *enc;
         char buf[1024];
115329f1
 
85f07f22
         /* feed status */
         stream = first_feed;
         while (stream != NULL) {
2effd274
             url_fprintf(pb, "<H1>Feed '%s'</H1>\n", stream->filename);
             url_fprintf(pb, "<TABLE>\n");
             url_fprintf(pb, "<TR><TD>Parameters<TD>Frame count<TD>Size<TD>Avg bitrate (kbits/s)\n");
85f07f22
             for(i=0;i<stream->nb_streams;i++) {
                 AVStream *st = stream->streams[i];
                 FeedData *fdata = st->priv_data;
01f4895c
                 enc = st->codec;
115329f1
 
85f07f22
                 avcodec_string(buf, sizeof(buf), enc);
                 avg = fdata->avg_frame_size * (float)enc->rate * 8.0;
                 if (enc->codec->type == CODEC_TYPE_AUDIO && enc->frame_size > 0)
                     avg /= enc->frame_size;
949b1a13
                 url_fprintf(pb, "<TR><TD>%s <TD> %d <TD> %"PRId64" <TD> %0.1f\n",
85f07f22
                              buf, enc->frame_number, fdata->data_count, avg / 1000.0);
             }
2effd274
             url_fprintf(pb, "</TABLE>\n");
85f07f22
             stream = stream->next_feed;
         }
     }
 #endif
 
     /* connection status */
2effd274
     url_fprintf(pb, "<H2>Connection Status</H2>\n");
85f07f22
 
2effd274
     url_fprintf(pb, "Number of connections: %d / %d<BR>\n",
85f07f22
                  nb_connections, nb_max_connections);
 
0be4b8d9
     url_fprintf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<BR>\n",
6edd6884
                  current_bandwidth, max_bandwidth);
42a63c6a
 
2effd274
     url_fprintf(pb, "<TABLE>\n");
     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");
85f07f22
     c1 = first_http_ctx;
     i = 0;
2effd274
     while (c1 != NULL) {
cde25790
         int bitrate;
         int j;
 
         bitrate = 0;
2effd274
         if (c1->stream) {
             for (j = 0; j < c1->stream->nb_streams; j++) {
2d563d2f
                 if (!c1->stream->feed)
01f4895c
                     bitrate += c1->stream->streams[j]->codec->bit_rate;
2d563d2f
                 else if (c1->feed_streams[j] >= 0)
                     bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
cde25790
             }
         }
 
85f07f22
         i++;
         p = inet_ntoa(c1->from_addr.sin_addr);
115329f1
         url_fprintf(pb, "<TR><TD><B>%d</B><TD>%s%s<TD>%s<TD>%s<TD>%s<td align=right>",
                     i,
                     c1->stream ? c1->stream->filename : "",
2effd274
                     c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
115329f1
                     p,
2effd274
                     c1->protocol,
                     http_state[c1->state]);
         fmt_bytecount(pb, bitrate);
         url_fprintf(pb, "<td align=right>");
         fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
         url_fprintf(pb, "<td align=right>");
         fmt_bytecount(pb, c1->data_count);
         url_fprintf(pb, "\n");
85f07f22
         c1 = c1->next;
     }
2effd274
     url_fprintf(pb, "</TABLE>\n");
115329f1
 
85f07f22
     /* date */
     ti = time(NULL);
     p = ctime(&ti);
2effd274
     url_fprintf(pb, "<HR size=1 noshade>Generated at %s", p);
     url_fprintf(pb, "</BODY>\n</HTML>\n");
85f07f22
 
2effd274
     len = url_close_dyn_buf(pb, &c->pb_buffer);
     c->buffer_ptr = c->pb_buffer;
     c->buffer_end = c->pb_buffer + len;
85f07f22
 }
 
2effd274
 /* check if the parser needs to be opened for stream i */
 static void open_parser(AVFormatContext *s, int i)
85f07f22
 {
2effd274
     AVStream *st = s->streams[i];
     AVCodec *codec;
31def229
 
01f4895c
     if (!st->codec->codec) {
         codec = avcodec_find_decoder(st->codec->codec_id);
2effd274
         if (codec && (codec->capabilities & CODEC_CAP_PARSE_ONLY)) {
01f4895c
             st->codec->parse_only = 1;
611c5741
             if (avcodec_open(st->codec, codec) < 0)
01f4895c
                 st->codec->parse_only = 0;
cde25790
         }
     }
85f07f22
 }
 
 static int open_input_stream(HTTPContext *c, const char *info)
 {
     char buf[128];
     char input_filename[1024];
     AVFormatContext *s;
c351cc7f
     int buf_size, i, ret;
0c1a9eda
     int64_t stream_pos;
85f07f22
 
     /* find file name */
     if (c->stream->feed) {
         strcpy(input_filename, c->stream->feed->feed_filename);
         buf_size = FFM_PACKET_SIZE;
         /* compute position (absolute time) */
ace21da3
         if (find_info_tag(buf, sizeof(buf), "date", info)) {
85f07f22
             stream_pos = parse_date(buf, 0);
f9436161
             if (stream_pos == INT64_MIN)
                 return -1;
ace21da3
         } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) {
f747e6d3
             int prebuffer = strtol(buf, 0, 10);
0c1a9eda
             stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
611c5741
         } else
0c1a9eda
             stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
85f07f22
     } else {
         strcpy(input_filename, c->stream->feed_filename);
         buf_size = 0;
         /* compute position (relative time) */
ace21da3
         if (find_info_tag(buf, sizeof(buf), "date", info)) {
85f07f22
             stream_pos = parse_date(buf, 1);
f9436161
             if (stream_pos == INT64_MIN)
                 return -1;
ace21da3
         } else
85f07f22
             stream_pos = 0;
     }
     if (input_filename[0] == '\0')
         return -1;
 
8256c0a3
 #if 0
     { time_t when = stream_pos / 1000000;
949b1a13
     http_log("Stream pos = %"PRId64", time=%s", stream_pos, ctime(&when));
8256c0a3
     }
 #endif
 
85f07f22
     /* open stream */
c351cc7f
     if ((ret = av_open_input_file(&s, input_filename, c->stream->ifmt,
                                   buf_size, c->stream->ap_in)) < 0) {
         http_log("could not open %s: %d\n", input_filename, ret);
85f07f22
         return -1;
2effd274
     }
9dc0bc3d
     s->flags |= AVFMT_FLAG_GENPTS;
85f07f22
     c->fmt_in = s;
89da5781
     av_find_stream_info(c->fmt_in);
115329f1
 
2effd274
     /* open each parser */
     for(i=0;i<s->nb_streams;i++)
         open_parser(s, i);
 
     /* choose stream as clock source (we favorize video stream if
        present) for packet sending */
     c->pts_stream_index = 0;
     for(i=0;i<c->stream->nb_streams;i++) {
115329f1
         if (c->pts_stream_index == 0 &&
01f4895c
             c->stream->streams[i]->codec->codec_type == CODEC_TYPE_VIDEO) {
2effd274
             c->pts_stream_index = i;
         }
     }
85f07f22
 
e8d27bc3
 #if 1
611c5741
     if (c->fmt_in->iformat->read_seek)
60a04f7f
         av_seek_frame(c->fmt_in, -1, stream_pos, 0);
e240a0bb
 #endif
2effd274
     /* set the start time (needed for maxtime and RTP packet timing) */
     c->start_time = cur_time;
     c->first_pts = AV_NOPTS_VALUE;
     return 0;
 }
 
e240a0bb
 /* return the server clock (in us) */
 static int64_t get_server_clock(HTTPContext *c)
2effd274
 {
e240a0bb
     /* compute current pts value from system time */
c3f58185
     return (cur_time - c->start_time) * 1000;
2effd274
 }
 
e240a0bb
 /* return the estimated time at which the current packet must be sent
    (in us) */
 static int64_t get_packet_send_clock(HTTPContext *c)
2effd274
 {
e240a0bb
     int bytes_left, bytes_sent, frame_bytes;
115329f1
 
e240a0bb
     frame_bytes = c->cur_frame_bytes;
611c5741
     if (frame_bytes <= 0)
e240a0bb
         return c->cur_pts;
611c5741
     else {
e240a0bb
         bytes_left = c->buffer_end - c->buffer_ptr;
         bytes_sent = frame_bytes - bytes_left;
         return c->cur_pts + (c->cur_frame_duration * bytes_sent) / frame_bytes;
2effd274
     }
 }
 
 
5eb765ef
 static int http_prepare_data(HTTPContext *c)
85f07f22
 {
2effd274
     int i, len, ret;
     AVFormatContext *ctx;
85f07f22
 
bc351386
     av_freep(&c->pb_buffer);
85f07f22
     switch(c->state) {
     case HTTPSTATE_SEND_DATA_HEADER:
         memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
5ee999b8
         av_metadata_set(&c->fmt_ctx.metadata, "author"   ,c->stream->author);
         av_metadata_set(&c->fmt_ctx.metadata, "comment"  ,c->stream->comment);
         av_metadata_set(&c->fmt_ctx.metadata, "copyright",c->stream->copyright);
         av_metadata_set(&c->fmt_ctx.metadata, "title"    ,c->stream->title);
2effd274
 
3d9cc27d
         for(i=0;i<c->stream->nb_streams;i++) {
2effd274
             AVStream *st;
bb270c08
             AVStream *src;
2effd274
             st = av_mallocz(sizeof(AVStream));
             c->fmt_ctx.streams[i] = st;
             /* if file or feed, then just take streams from FFStream struct */
115329f1
             if (!c->stream->feed ||
2effd274
                 c->stream->feed == c->stream)
7c054ea7
                 src = c->stream->streams[i];
2effd274
             else
7c054ea7
                 src = c->stream->feed->streams[c->stream->feed_streams[i]];
 
bb270c08
             *st = *src;
             st->priv_data = 0;
01f4895c
             st->codec->frame_number = 0; /* XXX: should be done in
2effd274
                                            AVStream, not in codec */
         }
3d9cc27d
         /* set output format parameters */
         c->fmt_ctx.oformat = c->stream->fmt;
         c->fmt_ctx.nb_streams = c->stream->nb_streams;
 
2effd274
         c->got_key_frame = 0;
f747e6d3
 
2effd274
         /* prepare header and save header data in a stream */
         if (url_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
             /* XXX: potential leak */
             return -1;
85f07f22
         }
899681cd
         c->fmt_ctx.pb->is_streamed = 1;
2effd274
 
8aae202e
         /*
          * HACK to avoid mpeg ps muxer to spit many underflow errors
          * Default value from FFmpeg
          * Try to set it use configuration option
          */
         c->fmt_ctx.preload   = (int)(0.5*AV_TIME_BASE);
         c->fmt_ctx.max_delay = (int)(0.7*AV_TIME_BASE);
 
3c27199b
         av_set_parameters(&c->fmt_ctx, NULL);
929a9b75
         if (av_write_header(&c->fmt_ctx) < 0) {
             http_log("Error writing output header\n");
f75cdda7
             return -1;
929a9b75
         }
2effd274
 
899681cd
         len = url_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
2effd274
         c->buffer_ptr = c->pb_buffer;
         c->buffer_end = c->pb_buffer + len;
 
85f07f22
         c->state = HTTPSTATE_SEND_DATA;
         c->last_packet_sent = 0;
         break;
     case HTTPSTATE_SEND_DATA:
         /* find a new packet */
3b371676
         /* read a packet from the input stream */
         if (c->stream->feed)
             ffm_set_write_index(c->fmt_in,
                                 c->stream->feed->feed_write_index,
                                 c->stream->feed->feed_size);
 
         if (c->stream->max_time &&
             c->stream->max_time + c->start_time - cur_time < 0)
             /* We have timed out */
             c->state = HTTPSTATE_SEND_DATA_TRAILER;
         else {
             AVPacket pkt;
         redo:
             if (av_read_frame(c->fmt_in, &pkt) < 0) {
                 if (c->stream->feed && c->stream->feed->feed_opened) {
                     /* if coming from feed, it means we reached the end of the
                        ffm file, so must wait for more data */
                     c->state = HTTPSTATE_WAIT_FEED;
                     return 1; /* state changed */
2effd274
                 } else {
3b371676
                     if (c->stream->loop) {
                         av_close_input_file(c->fmt_in);
                         c->fmt_in = NULL;
                         if (open_input_stream(c, "") < 0)
                             goto no_loop;
                         goto redo;
                     } else {
                     no_loop:
                         /* must send trailer now because eof or error */
                         c->state = HTTPSTATE_SEND_DATA_TRAILER;
1bc1cfdd
                     }
3b371676
                 }
             } else {
084a8912
                 int source_index = pkt.stream_index;
3b371676
                 /* update first pts if needed */
                 if (c->first_pts == AV_NOPTS_VALUE) {
                     c->first_pts = av_rescale_q(pkt.dts, c->fmt_in->streams[pkt.stream_index]->time_base, AV_TIME_BASE_Q);
                     c->start_time = cur_time;
                 }
                 /* send it to the appropriate stream */
                 if (c->stream->feed) {
                     /* if coming from a feed, select the right stream */
                     if (c->switch_pending) {
                         c->switch_pending = 0;
cde25790
                         for(i=0;i<c->stream->nb_streams;i++) {
3b371676
                             if (c->switch_feed_streams[i] == pkt.stream_index)
611c5741
                                 if (pkt.flags & PKT_FLAG_KEY)
3b371676
                                     do_switch_stream(c, i);
                             if (c->switch_feed_streams[i] >= 0)
                                 c->switch_pending = 1;
cde25790
                         }
3b371676
                     }
                     for(i=0;i<c->stream->nb_streams;i++) {
                         if (c->feed_streams[i] == pkt.stream_index) {
78728064
                             AVStream *st = c->fmt_in->streams[source_index];
3b371676
                             pkt.stream_index = i;
180b7026
                             if (pkt.flags & PKT_FLAG_KEY &&
                                 (st->codec->codec_type == CODEC_TYPE_VIDEO ||
                                  c->stream->nb_streams == 1))
0332f549
                                 c->got_key_frame = 1;
                             if (!c->stream->send_on_key || c->got_key_frame)
3b371676
                                 goto send_it;
                         }
                     }
                 } else {
                     AVCodecContext *codec;
dc3a6a36
                     AVStream *ist, *ost;
                 send_it:
                     ist = c->fmt_in->streams[source_index];
3b371676
                     /* specific handling for RTP: we use several
                        output stream (one for each RTP
                        connection). XXX: need more abstract handling */
                     if (c->is_packetized) {
                         /* compute send time and duration */
8f56ccca
                         c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
                         if (ist->start_time != AV_NOPTS_VALUE)
                             c->cur_pts -= av_rescale_q(ist->start_time, ist->time_base, AV_TIME_BASE_Q);
                         c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
e240a0bb
 #if 0
3b371676
                         printf("index=%d pts=%0.3f duration=%0.6f\n",
                                pkt.stream_index,
                                (double)c->cur_pts /
                                AV_TIME_BASE,
                                (double)c->cur_frame_duration /
                                AV_TIME_BASE);
e240a0bb
 #endif
3b371676
                         /* find RTP context */
                         c->packet_stream_index = pkt.stream_index;
                         ctx = c->rtp_ctx[c->packet_stream_index];
                         if(!ctx) {
8a0b55ff
                             av_free_packet(&pkt);
3b371676
                             break;
8a0b55ff
                         }
3b371676
                         codec = ctx->streams[0]->codec;
                         /* only one stream per RTP connection */
                         pkt.stream_index = 0;
                     } else {
                         ctx = &c->fmt_ctx;
                         /* Fudge here */
3ab29d8e
                         codec = ctx->streams[pkt.stream_index]->codec;
3b371676
                     }
 
                     if (c->is_packetized) {
                         int max_packet_size;
90abbdba
                         if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
3b371676
                             max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
                         else
                             max_packet_size = url_get_max_packet_size(c->rtp_handles[c->packet_stream_index]);
                         ret = url_open_dyn_packet_buf(&ctx->pb, max_packet_size);
                     } else {
                         ret = url_open_dyn_buf(&ctx->pb);
                     }
                     if (ret < 0) {
                         /* XXX: potential leak */
                         return -1;
                     }
3ab29d8e
                     ost = ctx->streams[pkt.stream_index];
 
b0675954
                     ctx->pb->is_streamed = 1;
3b371676
                     if (pkt.dts != AV_NOPTS_VALUE)
d80904cc
                         pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
3b371676
                     if (pkt.pts != AV_NOPTS_VALUE)
d80904cc
                         pkt.pts = av_rescale_q(pkt.pts, ist->time_base, ost->time_base);
                     pkt.duration = av_rescale_q(pkt.duration, ist->time_base, ost->time_base);
3766ed72
                     if (av_write_frame(ctx, &pkt) < 0) {
                         http_log("Error writing frame to output\n");
3b371676
                         c->state = HTTPSTATE_SEND_DATA_TRAILER;
3766ed72
                     }
3b371676
 
                     len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
                     c->cur_frame_bytes = len;
                     c->buffer_ptr = c->pb_buffer;
                     c->buffer_end = c->pb_buffer + len;
 
                     codec->frame_number++;
                     if (len == 0) {
                         av_free_packet(&pkt);
                         goto redo;
f747e6d3
                     }
85f07f22
                 }
3b371676
                 av_free_packet(&pkt);
85f07f22
             }
3b371676
         }
85f07f22
         break;
     default:
     case HTTPSTATE_SEND_DATA_TRAILER:
         /* last packet test ? */
2effd274
         if (c->last_packet_sent || c->is_packetized)
85f07f22
             return -1;
2effd274
         ctx = &c->fmt_ctx;
85f07f22
         /* prepare header */
2effd274
         if (url_open_dyn_buf(&ctx->pb) < 0) {
             /* XXX: potential leak */
             return -1;
         }
58bd615f
         c->fmt_ctx.pb->is_streamed = 1;
2effd274
         av_write_trailer(ctx);
899681cd
         len = url_close_dyn_buf(ctx->pb, &c->pb_buffer);
2effd274
         c->buffer_ptr = c->pb_buffer;
         c->buffer_end = c->pb_buffer + len;
 
85f07f22
         c->last_packet_sent = 1;
         break;
     }
     return 0;
 }
 
 /* should convert the format at the same time */
bc351386
 /* send data starting at c->buffer_ptr to the output connection
    (either UDP or TCP connection) */
5eb765ef
 static int http_send_data(HTTPContext *c)
85f07f22
 {
e240a0bb
     int len, ret;
85f07f22
 
bc351386
     for(;;) {
         if (c->buffer_ptr >= c->buffer_end) {
             ret = http_prepare_data(c);
             if (ret < 0)
                 return -1;
611c5741
             else if (ret != 0)
bc351386
                 /* state change requested */
                 break;
2effd274
         } else {
bc351386
             if (c->is_packetized) {
                 /* RTP data output */
                 len = c->buffer_end - c->buffer_ptr;
                 if (len < 4) {
                     /* fail safe - should never happen */
                 fail1:
                     c->buffer_ptr = c->buffer_end;
2effd274
                     return 0;
                 }
bc351386
                 len = (c->buffer_ptr[0] << 24) |
                     (c->buffer_ptr[1] << 16) |
                     (c->buffer_ptr[2] << 8) |
                     (c->buffer_ptr[3]);
                 if (len > (c->buffer_end - c->buffer_ptr))
                     goto fail1;
e240a0bb
                 if ((get_packet_send_clock(c) - get_server_clock(c)) > 0) {
                     /* nothing to send yet: we can wait */
                     return 0;
                 }
 
                 c->data_count += len;
                 update_datarate(&c->datarate, c->data_count);
                 if (c->stream)
                     c->stream->bytes_served += len;
 
90abbdba
                 if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
bc351386
                     /* RTP packets are sent inside the RTSP TCP connection */
899681cd
                     ByteIOContext *pb;
bc351386
                     int interleaved_index, size;
                     uint8_t header[4];
                     HTTPContext *rtsp_c;
115329f1
 
bc351386
                     rtsp_c = c->rtsp_c;
                     /* if no RTSP connection left, error */
                     if (!rtsp_c)
                         return -1;
                     /* if already sending something, then wait. */
611c5741
                     if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
bc351386
                         break;
899681cd
                     if (url_open_dyn_buf(&pb) < 0)
bc351386
                         goto fail1;
                     interleaved_index = c->packet_stream_index * 2;
                     /* RTCP packets are sent at odd indexes */
                     if (c->buffer_ptr[1] == 200)
                         interleaved_index++;
                     /* write RTSP TCP header */
                     header[0] = '$';
                     header[1] = interleaved_index;
                     header[2] = len >> 8;
                     header[3] = len;
                     put_buffer(pb, header, 4);
                     /* write RTP packet data */
                     c->buffer_ptr += 4;
                     put_buffer(pb, c->buffer_ptr, len);
                     size = url_close_dyn_buf(pb, &c->packet_buffer);
                     /* prepare asynchronous TCP sending */
                     rtsp_c->packet_buffer_ptr = c->packet_buffer;
                     rtsp_c->packet_buffer_end = c->packet_buffer + size;
e240a0bb
                     c->buffer_ptr += len;
115329f1
 
e240a0bb
                     /* send everything we can NOW */
c60202df
                     len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
                                 rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
611c5741
                     if (len > 0)
e240a0bb
                         rtsp_c->packet_buffer_ptr += len;
                     if (rtsp_c->packet_buffer_ptr < rtsp_c->packet_buffer_end) {
                         /* if we could not send all the data, we will
                            send it later, so a new state is needed to
                            "lock" the RTSP TCP connection */
                         rtsp_c->state = RTSPSTATE_SEND_PACKET;
                         break;
611c5741
                     } else
e240a0bb
                         /* all data has been sent */
                         av_freep(&c->packet_buffer);
                 } else {
                     /* send RTP packet directly in UDP */
bc351386
                     c->buffer_ptr += 4;
115329f1
                     url_write(c->rtp_handles[c->packet_stream_index],
bc351386
                               c->buffer_ptr, len);
e240a0bb
                     c->buffer_ptr += len;
                     /* here we continue as we can send several packets per 10 ms slot */
bc351386
                 }
             } else {
                 /* TCP data output */
c60202df
                 len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
bc351386
                 if (len < 0) {
8da4034f
                     if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
611c5741
                         ff_neterrno() != FF_NETERROR(EINTR))
bc351386
                         /* error : close connection */
                         return -1;
611c5741
                     else
bc351386
                         return 0;
611c5741
                 } else
bc351386
                     c->buffer_ptr += len;
611c5741
 
e240a0bb
                 c->data_count += len;
                 update_datarate(&c->datarate, c->data_count);
                 if (c->stream)
                     c->stream->bytes_served += len;
                 break;
2effd274
             }
85f07f22
         }
bc351386
     } /* for(;;) */
85f07f22
     return 0;
 }
 
 static int http_start_receive_data(HTTPContext *c)
 {
     int fd;
 
     if (c->stream->feed_opened)
         return -1;
 
e322ea48
     /* Don't permit writing to this one */
     if (c->stream->readonly)
         return -1;
 
85f07f22
     /* open feed */
     fd = open(c->stream->feed_filename, O_RDWR);
929a9b75
     if (fd < 0) {
         http_log("Error opening feeder file: %s\n", strerror(errno));
85f07f22
         return -1;
929a9b75
     }
85f07f22
     c->feed_fd = fd;
115329f1
 
2779cdad
     if ((c->stream->feed_write_index = ffm_read_write_index(fd)) < 0) {
         http_log("Error reading write index from feed file: %s\n", strerror(errno));
         return -1;
     }
85f07f22
     c->stream->feed_size = lseek(fd, 0, SEEK_END);
     lseek(fd, 0, SEEK_SET);
 
     /* init buffer input */
     c->buffer_ptr = c->buffer;
     c->buffer_end = c->buffer + FFM_PACKET_SIZE;
     c->stream->feed_opened = 1;
     return 0;
 }
115329f1
 
85f07f22
 static int http_receive_data(HTTPContext *c)
 {
     HTTPContext *c1;
 
a6e14edd
     if (c->buffer_end > c->buffer_ptr) {
         int len;
 
c60202df
         len = recv(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
a6e14edd
         if (len < 0) {
8da4034f
             if (ff_neterrno() != FF_NETERROR(EAGAIN) &&
611c5741
                 ff_neterrno() != FF_NETERROR(EINTR))
a6e14edd
                 /* error : close connection */
                 goto fail;
611c5741
         } else if (len == 0)
a6e14edd
             /* end of connection : close it */
             goto fail;
611c5741
         else {
a6e14edd
             c->buffer_ptr += len;
             c->data_count += len;
5eb765ef
             update_datarate(&c->datarate, c->data_count);
a6e14edd
         }
     }
 
d445a7e9
     if (c->buffer_ptr - c->buffer >= 2 && c->data_count > FFM_PACKET_SIZE) {
         if (c->buffer[0] != 'f' ||
             c->buffer[1] != 'm') {
             http_log("Feed stream has become desynchronized -- disconnecting\n");
             goto fail;
         }
     }
 
85f07f22
     if (c->buffer_ptr >= c->buffer_end) {
f747e6d3
         FFStream *feed = c->stream;
85f07f22
         /* a packet has been received : write it in the store, except
            if header */
         if (c->data_count > FFM_PACKET_SIZE) {
115329f1
 
949b1a13
             //            printf("writing pos=0x%"PRIx64" size=0x%"PRIx64"\n", feed->feed_write_index, feed->feed_size);
85f07f22
             /* XXX: use llseek or url_seek */
             lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
929a9b75
             if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
                 http_log("Error writing to feed file: %s\n", strerror(errno));
                 goto fail;
             }
115329f1
 
85f07f22
             feed->feed_write_index += FFM_PACKET_SIZE;
             /* update file size */
             if (feed->feed_write_index > c->stream->feed_size)
                 feed->feed_size = feed->feed_write_index;
 
             /* handle wrap around if max file size reached */
6b0bdc75
             if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
85f07f22
                 feed->feed_write_index = FFM_PACKET_SIZE;
 
             /* write index */
2779cdad
             if (ffm_write_write_index(c->feed_fd, feed->feed_write_index) < 0) {
                 http_log("Error writing index to feed file: %s\n", strerror(errno));
                 goto fail;
             }
85f07f22
 
             /* wake up any waiting connections */
             for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
115329f1
                 if (c1->state == HTTPSTATE_WAIT_FEED &&
611c5741
                     c1->stream->feed == c->stream->feed)
85f07f22
                     c1->state = HTTPSTATE_SEND_DATA;
             }
f747e6d3
         } else {
             /* We have a header in our hands that contains useful data */
f2972c8c
             AVFormatContext *s = NULL;
             ByteIOContext *pb;
bd7cf6ad
             AVInputFormat *fmt_in;
f747e6d3
             int i;
 
bd7cf6ad
             /* use feed output format name to find corresponding input format */
             fmt_in = av_find_input_format(feed->fmt->name);
             if (!fmt_in)
                 goto fail;
 
697efa36
             url_open_buf(&pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
             pb->is_streamed = 1;
 
e6f0deab
             if (av_open_input_stream(&s, pb, c->stream->feed_filename, fmt_in, NULL) < 0) {
                 av_free(pb);
                 goto fail;
             }
f747e6d3
 
             /* Now we have the actual streams */
f2972c8c
             if (s->nb_streams != feed->nb_streams) {
                 av_close_input_stream(s);
86771c68
                 av_free(pb);
f747e6d3
                 goto fail;
             }
f2972c8c
 
cb51aef1
             for (i = 0; i < s->nb_streams; i++) {
                 AVStream *fst = feed->streams[i];
                 AVStream *st = s->streams[i];
                 memcpy(fst->codec, st->codec, sizeof(AVCodecContext));
                 if (fst->codec->extradata_size) {
                     fst->codec->extradata = av_malloc(fst->codec->extradata_size);
                     if (!fst->codec->extradata)
                         goto fail;
                     memcpy(fst->codec->extradata, st->codec->extradata,
                            fst->codec->extradata_size);
                 }
             }
f2972c8c
 
             av_close_input_stream(s);
86771c68
             av_free(pb);
85f07f22
         }
         c->buffer_ptr = c->buffer;
     }
 
     return 0;
  fail:
     c->stream->feed_opened = 0;
     close(c->feed_fd);
c1593d0e
     /* wake up any waiting connections to stop waiting for feed */
     for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
         if (c1->state == HTTPSTATE_WAIT_FEED &&
             c1->stream->feed == c->stream->feed)
             c1->state = HTTPSTATE_SEND_DATA_TRAILER;
     }
85f07f22
     return -1;
 }
 
2effd274
 /********************************************************************/
 /* RTSP handling */
 
 static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
 {
     const char *str;
     time_t ti;
     char *p;
     char buf2[32];
 
     switch(error_number) {
7e665cd3
     case RTSP_STATUS_OK:
         str = "OK";
         break;
     case RTSP_STATUS_METHOD:
         str = "Method Not Allowed";
         break;
     case RTSP_STATUS_BANDWIDTH:
         str = "Not Enough Bandwidth";
         break;
     case RTSP_STATUS_SESSION:
         str = "Session Not Found";
         break;
     case RTSP_STATUS_STATE:
         str = "Method Not Valid in This State";
         break;
     case RTSP_STATUS_AGGREGATE:
         str = "Aggregate operation not allowed";
         break;
     case RTSP_STATUS_ONLY_AGGREGATE:
         str = "Only aggregate operation allowed";
         break;
     case RTSP_STATUS_TRANSPORT:
         str = "Unsupported transport";
         break;
     case RTSP_STATUS_INTERNAL:
         str = "Internal Server Error";
         break;
     case RTSP_STATUS_SERVICE:
         str = "Service Unavailable";
         break;
     case RTSP_STATUS_VERSION:
         str = "RTSP Version not supported";
         break;
2effd274
     default:
         str = "Unknown Error";
         break;
     }
115329f1
 
2effd274
     url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
     url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
 
     /* output GMT time */
     ti = time(NULL);
     p = ctime(&ti);
     strcpy(buf2, p);
     p = buf2 + strlen(p) - 1;
     if (*p == '\n')
         *p = '\0';
     url_fprintf(c->pb, "Date: %s GMT\r\n", buf2);
 }
 
 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
 {
     rtsp_reply_header(c, error_number);
     url_fprintf(c->pb, "\r\n");
 }
 
 static int rtsp_parse_request(HTTPContext *c)
 {
     const char *p, *p1, *p2;
     char cmd[32];
     char url[1024];
     char protocol[32];
     char line[1024];
     int len;
a9e534d5
     RTSPMessageHeader header1, *header = &header1;
115329f1
 
2effd274
     c->buffer_ptr[0] = '\0';
     p = c->buffer;
115329f1
 
2effd274
     get_word(cmd, sizeof(cmd), &p);
     get_word(url, sizeof(url), &p);
     get_word(protocol, sizeof(protocol), &p);
 
f7d78f36
     av_strlcpy(c->method, cmd, sizeof(c->method));
     av_strlcpy(c->url, url, sizeof(c->url));
     av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
2effd274
 
899681cd
     if (url_open_dyn_buf(&c->pb) < 0) {
2effd274
         /* XXX: cannot do more */
         c->pb = NULL; /* safety */
         return -1;
     }
 
     /* check version name */
     if (strcmp(protocol, "RTSP/1.0") != 0) {
         rtsp_reply_error(c, RTSP_STATUS_VERSION);
         goto the_end;
     }
 
     /* parse each header line */
d541a7d2
     memset(header, 0, sizeof(*header));
2effd274
     /* skip to next line */
     while (*p != '\n' && *p != '\0')
         p++;
     if (*p == '\n')
         p++;
     while (*p != '\0') {
         p1 = strchr(p, '\n');
         if (!p1)
             break;
         p2 = p1;
         if (p2 > p && p2[-1] == '\r')
             p2--;
         /* skip empty line */
         if (p2 == p)
             break;
         len = p2 - p;
         if (len > sizeof(line) - 1)
             len = sizeof(line) - 1;
         memcpy(line, p, len);
         line[len] = '\0';
         rtsp_parse_line(header, line);
         p = p1 + 1;
     }
 
     /* handle sequence number */
     c->seq = header->seq;
 
611c5741
     if (!strcmp(cmd, "DESCRIBE"))
2effd274
         rtsp_cmd_describe(c, url);
611c5741
     else if (!strcmp(cmd, "OPTIONS"))
0df65975
         rtsp_cmd_options(c, url);
611c5741
     else if (!strcmp(cmd, "SETUP"))
2effd274
         rtsp_cmd_setup(c, url, header);
611c5741
     else if (!strcmp(cmd, "PLAY"))
2effd274
         rtsp_cmd_play(c, url, header);
611c5741
     else if (!strcmp(cmd, "PAUSE"))
2effd274
         rtsp_cmd_pause(c, url, header);
611c5741
     else if (!strcmp(cmd, "TEARDOWN"))
2effd274
         rtsp_cmd_teardown(c, url, header);
611c5741
     else
2effd274
         rtsp_reply_error(c, RTSP_STATUS_METHOD);
611c5741
 
2effd274
  the_end:
     len = url_close_dyn_buf(c->pb, &c->pb_buffer);
     c->pb = NULL; /* safety */
     if (len < 0) {
         /* XXX: cannot do more */
         return -1;
     }
     c->buffer_ptr = c->pb_buffer;
     c->buffer_end = c->pb_buffer + len;
     c->state = RTSPSTATE_SEND_REPLY;
     return 0;
 }
 
115329f1
 static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
829ac53d
                                    struct in_addr my_ip)
2effd274
 {
dd723472
     AVFormatContext *avc;
     AVStream avs[MAX_STREAMS];
     int i;
115329f1
 
8e2fd8e1
     avc =  avformat_alloc_context();
dd723472
     if (avc == NULL) {
2effd274
         return -1;
dd723472
     }
5ee999b8
     av_metadata_set(&avc->metadata, "title",
                     stream->title[0] ? stream->title : "No Title");
dd723472
     avc->nb_streams = stream->nb_streams;
     if (stream->is_multicast) {
         snprintf(avc->filename, 1024, "rtp://%s:%d?multicast=1?ttl=%d",
                  inet_ntoa(stream->multicast_ip),
                  stream->multicast_port, stream->multicast_ttl);
     }
115329f1
 
2effd274
     for(i = 0; i < stream->nb_streams; i++) {
dd723472
         avc->streams[i] = &avs[i];
         avc->streams[i]->codec = stream->streams[i]->codec;
2effd274
     }
dd723472
     *pbuffer = av_mallocz(2048);
     avf_sdp_create(&avc, 1, *pbuffer, 2048);
     av_free(avc);
 
     return strlen(*pbuffer);
2effd274
 }
 
0df65975
 static void rtsp_cmd_options(HTTPContext *c, const char *url)
 {
 //    rtsp_reply_header(c, RTSP_STATUS_OK);
     url_fprintf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
     url_fprintf(c->pb, "CSeq: %d\r\n", c->seq);
     url_fprintf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
     url_fprintf(c->pb, "\r\n");
 }
 
2effd274
 static void rtsp_cmd_describe(HTTPContext *c, const char *url)
 {
     FFStream *stream;
     char path1[1024];
     const char *path;
0c1a9eda
     uint8_t *content;
829ac53d
     int content_length, len;
     struct sockaddr_in my_addr;
115329f1
 
2effd274
     /* find which url is asked */
6ba5cbc6
     url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2effd274
     path = path1;
     if (*path == '/')
         path++;
 
     for(stream = first_stream; stream != NULL; stream = stream->next) {
25e3e53d
         if (!stream->is_feed &&
             stream->fmt && !strcmp(stream->fmt->name, "rtp") &&
2effd274
             !strcmp(path, stream->filename)) {
             goto found;
         }
     }
     /* no stream found */
     rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
     return;
 
  found:
     /* prepare the media description in sdp format */
829ac53d
 
     /* get the host IP */
     len = sizeof(my_addr);
     getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
     content_length = prepare_sdp_description(stream, &content, my_addr.sin_addr);
2effd274
     if (content_length < 0) {
         rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
         return;
     }
     rtsp_reply_header(c, RTSP_STATUS_OK);
     url_fprintf(c->pb, "Content-Type: application/sdp\r\n");
     url_fprintf(c->pb, "Content-Length: %d\r\n", content_length);
     url_fprintf(c->pb, "\r\n");
     put_buffer(c->pb, content, content_length);
 }
 
 static HTTPContext *find_rtp_session(const char *session_id)
 {
     HTTPContext *c;
 
     if (session_id[0] == '\0')
         return NULL;
 
     for(c = first_http_ctx; c != NULL; c = c->next) {
         if (!strcmp(c->session_id, session_id))
             return c;
     }
     return NULL;
 }
 
a9e534d5
 static RTSPTransportField *find_transport(RTSPMessageHeader *h, enum RTSPLowerTransport lower_transport)
2effd274
 {
     RTSPTransportField *th;
     int i;
 
     for(i=0;i<h->nb_transports;i++) {
         th = &h->transports[i];
90abbdba
         if (th->lower_transport == lower_transport)
2effd274
             return th;
     }
     return NULL;
 }
 
115329f1
 static void rtsp_cmd_setup(HTTPContext *c, const char *url,
a9e534d5
                            RTSPMessageHeader *h)
2effd274
 {
     FFStream *stream;
     int stream_index, port;
     char buf[1024];
     char path1[1024];
     const char *path;
     HTTPContext *rtp_c;
     RTSPTransportField *th;
     struct sockaddr_in dest_addr;
     RTSPActionServerSetup setup;
115329f1
 
2effd274
     /* find which url is asked */
6ba5cbc6
     url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2effd274
     path = path1;
     if (*path == '/')
         path++;
 
     /* now check each stream */
     for(stream = first_stream; stream != NULL; stream = stream->next) {
25e3e53d
         if (!stream->is_feed &&
             stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
2effd274
             /* accept aggregate filenames only if single stream */
             if (!strcmp(path, stream->filename)) {
                 if (stream->nb_streams != 1) {
                     rtsp_reply_error(c, RTSP_STATUS_AGGREGATE);
                     return;
                 }
                 stream_index = 0;
                 goto found;
             }
115329f1
 
2effd274
             for(stream_index = 0; stream_index < stream->nb_streams;
                 stream_index++) {
115329f1
                 snprintf(buf, sizeof(buf), "%s/streamid=%d",
2effd274
                          stream->filename, stream_index);
                 if (!strcmp(path, buf))
                     goto found;
             }
         }
     }
     /* no stream found */
     rtsp_reply_error(c, RTSP_STATUS_SERVICE); /* XXX: right error ? */
     return;
  found:
 
     /* generate session id if needed */
611c5741
     if (h->session_id[0] == '\0')
1df93ae9
         snprintf(h->session_id, sizeof(h->session_id), "%08x%08x",
                  av_random(&random_state), av_random(&random_state));
2effd274
 
     /* find rtp session, and create it if none found */
     rtp_c = find_rtp_session(h->session_id);
     if (!rtp_c) {
bc351386
         /* always prefer UDP */
90abbdba
         th = find_transport(h, RTSP_LOWER_TRANSPORT_UDP);
bc351386
         if (!th) {
90abbdba
             th = find_transport(h, RTSP_LOWER_TRANSPORT_TCP);
bc351386
             if (!th) {
                 rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
                 return;
             }
         }
 
         rtp_c = rtp_new_connection(&c->from_addr, stream, h->session_id,
90abbdba
                                    th->lower_transport);
2effd274
         if (!rtp_c) {
             rtsp_reply_error(c, RTSP_STATUS_BANDWIDTH);
             return;
         }
 
         /* open input stream */
         if (open_input_stream(rtp_c, "") < 0) {
             rtsp_reply_error(c, RTSP_STATUS_INTERNAL);
             return;
         }
     }
115329f1
 
2effd274
     /* test if stream is OK (test needed because several SETUP needs
        to be done for a given file) */
     if (rtp_c->stream != stream) {
         rtsp_reply_error(c, RTSP_STATUS_SERVICE);
         return;
     }
115329f1
 
2effd274
     /* test if stream is already set up */
     if (rtp_c->rtp_ctx[stream_index]) {
         rtsp_reply_error(c, RTSP_STATUS_STATE);
         return;
     }
 
     /* check transport */
     th = find_transport(h, rtp_c->rtp_protocol);
90abbdba
     if (!th || (th->lower_transport == RTSP_LOWER_TRANSPORT_UDP &&
2effd274
                 th->client_port_min <= 0)) {
         rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
         return;
     }
 
     /* setup default options */
     setup.transport_option[0] = '\0';
     dest_addr = rtp_c->from_addr;
     dest_addr.sin_port = htons(th->client_port_min);
115329f1
 
2effd274
     /* setup stream */
bc351386
     if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, c) < 0) {
2effd274
         rtsp_reply_error(c, RTSP_STATUS_TRANSPORT);
         return;
     }
 
     /* now everything is OK, so we can send the connection parameters */
     rtsp_reply_header(c, RTSP_STATUS_OK);
     /* session ID */
     url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
 
     switch(rtp_c->rtp_protocol) {
90abbdba
     case RTSP_LOWER_TRANSPORT_UDP:
2effd274
         port = rtp_get_local_port(rtp_c->rtp_handles[stream_index]);
         url_fprintf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
                     "client_port=%d-%d;server_port=%d-%d",
                     th->client_port_min, th->client_port_min + 1,
                     port, port + 1);
         break;
90abbdba
     case RTSP_LOWER_TRANSPORT_TCP:
2effd274
         url_fprintf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
                     stream_index * 2, stream_index * 2 + 1);
         break;
     default:
         break;
     }
611c5741
     if (setup.transport_option[0] != '\0')
2effd274
         url_fprintf(c->pb, ";%s", setup.transport_option);
     url_fprintf(c->pb, "\r\n");
115329f1
 
2effd274
 
     url_fprintf(c->pb, "\r\n");
 }
 
 
 /* find an rtp connection by using the session ID. Check consistency
    with filename */
115329f1
 static HTTPContext *find_rtp_session_with_url(const char *url,
2effd274
                                               const char *session_id)
 {
     HTTPContext *rtp_c;
     char path1[1024];
     const char *path;
94d9ad5f
     char buf[1024];
     int s;
2effd274
 
     rtp_c = find_rtp_session(session_id);
     if (!rtp_c)
         return NULL;
 
     /* find which url is asked */
6ba5cbc6
     url_split(NULL, 0, NULL, 0, NULL, 0, NULL, path1, sizeof(path1), url);
2effd274
     path = path1;
     if (*path == '/')
         path++;
94d9ad5f
     if(!strcmp(path, rtp_c->stream->filename)) return rtp_c;
     for(s=0; s<rtp_c->stream->nb_streams; ++s) {
       snprintf(buf, sizeof(buf), "%s/streamid=%d",
         rtp_c->stream->filename, s);
       if(!strncmp(path, buf, sizeof(buf))) {
     // XXX: Should we reply with RTSP_STATUS_ONLY_AGGREGATE if nb_streams>1?
         return rtp_c;
       }
     }
     return NULL;
2effd274
 }
 
a9e534d5
 static void rtsp_cmd_play(HTTPContext *c, const char *url, RTSPMessageHeader *h)
2effd274
 {
     HTTPContext *rtp_c;
 
     rtp_c = find_rtp_session_with_url(url, h->session_id);
     if (!rtp_c) {
         rtsp_reply_error(c, RTSP_STATUS_SESSION);
         return;
     }
115329f1
 
2effd274
     if (rtp_c->state != HTTPSTATE_SEND_DATA &&
         rtp_c->state != HTTPSTATE_WAIT_FEED &&
         rtp_c->state != HTTPSTATE_READY) {
         rtsp_reply_error(c, RTSP_STATUS_STATE);
         return;
     }
 
e240a0bb
 #if 0
     /* XXX: seek in stream */
     if (h->range_start != AV_NOPTS_VALUE) {
         printf("range_start=%0.3f\n", (double)h->range_start / AV_TIME_BASE);
         av_seek_frame(rtp_c->fmt_in, -1, h->range_start);
     }
 #endif
 
2effd274
     rtp_c->state = HTTPSTATE_SEND_DATA;
115329f1
 
2effd274
     /* now everything is OK, so we can send the connection parameters */
     rtsp_reply_header(c, RTSP_STATUS_OK);
     /* session ID */
     url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
     url_fprintf(c->pb, "\r\n");
 }
 
a9e534d5
 static void rtsp_cmd_pause(HTTPContext *c, const char *url, RTSPMessageHeader *h)
2effd274
 {
     HTTPContext *rtp_c;
 
     rtp_c = find_rtp_session_with_url(url, h->session_id);
     if (!rtp_c) {
         rtsp_reply_error(c, RTSP_STATUS_SESSION);
         return;
     }
115329f1
 
2effd274
     if (rtp_c->state != HTTPSTATE_SEND_DATA &&
         rtp_c->state != HTTPSTATE_WAIT_FEED) {
         rtsp_reply_error(c, RTSP_STATUS_STATE);
         return;
     }
115329f1
 
2effd274
     rtp_c->state = HTTPSTATE_READY;
1bc1cfdd
     rtp_c->first_pts = AV_NOPTS_VALUE;
2effd274
     /* now everything is OK, so we can send the connection parameters */
     rtsp_reply_header(c, RTSP_STATUS_OK);
     /* session ID */
     url_fprintf(c->pb, "Session: %s\r\n", rtp_c->session_id);
     url_fprintf(c->pb, "\r\n");
 }
 
a9e534d5
 static void rtsp_cmd_teardown(HTTPContext *c, const char *url, RTSPMessageHeader *h)
2effd274
 {
     HTTPContext *rtp_c;
b0b2faa7
     char session_id[32];
2effd274
 
     rtp_c = find_rtp_session_with_url(url, h->session_id);
     if (!rtp_c) {
         rtsp_reply_error(c, RTSP_STATUS_SESSION);
         return;
     }
115329f1
 
f7d78f36
     av_strlcpy(session_id, rtp_c->session_id, sizeof(session_id));
b0b2faa7
 
2effd274
     /* abort the session */
     close_connection(rtp_c);
 
     /* now everything is OK, so we can send the connection parameters */
     rtsp_reply_header(c, RTSP_STATUS_OK);
     /* session ID */
b0b2faa7
     url_fprintf(c->pb, "Session: %s\r\n", session_id);
2effd274
     url_fprintf(c->pb, "\r\n");
 }
 
 
 /********************************************************************/
 /* RTP handling */
 
115329f1
 static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
bc351386
                                        FFStream *stream, const char *session_id,
90abbdba
                                        enum RTSPLowerTransport rtp_protocol)
2effd274
 {
     HTTPContext *c = NULL;
bc351386
     const char *proto_str;
115329f1
 
2effd274
     /* XXX: should output a warning page when coming
        close to the connection limit */
     if (nb_connections >= nb_max_connections)
         goto fail;
115329f1
 
2effd274
     /* add a new connection */
     c = av_mallocz(sizeof(HTTPContext));
     if (!c)
         goto fail;
115329f1
 
2effd274
     c->fd = -1;
     c->poll_entry = NULL;
6edd6884
     c->from_addr = *from_addr;
2effd274
     c->buffer_size = IOBUFFER_INIT_SIZE;
     c->buffer = av_malloc(c->buffer_size);
     if (!c->buffer)
         goto fail;
     nb_connections++;
     c->stream = stream;
f7d78f36
     av_strlcpy(c->session_id, session_id, sizeof(c->session_id));
2effd274
     c->state = HTTPSTATE_READY;
     c->is_packetized = 1;
bc351386
     c->rtp_protocol = rtp_protocol;
 
2effd274
     /* protocol is shown in statistics */
bc351386
     switch(c->rtp_protocol) {
90abbdba
     case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
bc351386
         proto_str = "MCAST";
         break;
90abbdba
     case RTSP_LOWER_TRANSPORT_UDP:
bc351386
         proto_str = "UDP";
         break;
90abbdba
     case RTSP_LOWER_TRANSPORT_TCP:
bc351386
         proto_str = "TCP";
         break;
     default:
         proto_str = "???";
         break;
     }
f7d78f36
     av_strlcpy(c->protocol, "RTP/", sizeof(c->protocol));
     av_strlcat(c->protocol, proto_str, sizeof(c->protocol));
2effd274
 
6edd6884
     current_bandwidth += stream->bandwidth;
 
2effd274
     c->next = first_http_ctx;
     first_http_ctx = c;
     return c;
115329f1
 
2effd274
  fail:
     if (c) {
         av_free(c->buffer);
         av_free(c);
     }
     return NULL;
 }
 
 /* add a new RTP stream in an RTP connection (used in RTSP SETUP
bc351386
    command). If RTP/TCP protocol is used, TCP connection 'rtsp_c' is
2effd274
    used. */
115329f1
 static int rtp_new_av_stream(HTTPContext *c,
bc351386
                              int stream_index, struct sockaddr_in *dest_addr,
                              HTTPContext *rtsp_c)
2effd274
 {
     AVFormatContext *ctx;
     AVStream *st;
     char *ipaddr;
75480e86
     URLContext *h = NULL;
0c1a9eda
     uint8_t *dummy_buf;
bc351386
     int max_packet_size;
115329f1
 
2effd274
     /* now we can open the relevant output stream */
8e2fd8e1
     ctx = avformat_alloc_context();
2effd274
     if (!ctx)
         return -1;
b156b88c
     ctx->oformat = guess_format("rtp", NULL, NULL);
2effd274
 
     st = av_mallocz(sizeof(AVStream));
     if (!st)
         goto fail;
8d931070
     st->codec= avcodec_alloc_context();
2effd274
     ctx->nb_streams = 1;
     ctx->streams[0] = st;
 
115329f1
     if (!c->stream->feed ||
611c5741
         c->stream->feed == c->stream)
2effd274
         memcpy(st, c->stream->streams[stream_index], sizeof(AVStream));
611c5741
     else
115329f1
         memcpy(st,
2effd274
                c->stream->feed->streams[c->stream->feed_streams[stream_index]],
                sizeof(AVStream));
57dbe08b
     st->priv_data = NULL;
115329f1
 
bc351386
     /* build destination RTP address */
     ipaddr = inet_ntoa(dest_addr->sin_addr);
 
     switch(c->rtp_protocol) {
90abbdba
     case RTSP_LOWER_TRANSPORT_UDP:
     case RTSP_LOWER_TRANSPORT_UDP_MULTICAST:
bc351386
         /* RTP/UDP case */
115329f1
 
6edd6884
         /* XXX: also pass as parameter to function ? */
         if (c->stream->is_multicast) {
             int ttl;
             ttl = c->stream->multicast_ttl;
             if (!ttl)
                 ttl = 16;
             snprintf(ctx->filename, sizeof(ctx->filename),
115329f1
                      "rtp://%s:%d?multicast=1&ttl=%d",
6edd6884
                      ipaddr, ntohs(dest_addr->sin_port), ttl);
         } else {
             snprintf(ctx->filename, sizeof(ctx->filename),
                      "rtp://%s:%d", ipaddr, ntohs(dest_addr->sin_port));
         }
2effd274
 
         if (url_open(&h, ctx->filename, URL_WRONLY) < 0)
             goto fail;
         c->rtp_handles[stream_index] = h;
bc351386
         max_packet_size = url_get_max_packet_size(h);
         break;
90abbdba
     case RTSP_LOWER_TRANSPORT_TCP:
bc351386
         /* RTP/TCP case */
         c->rtsp_c = rtsp_c;
         max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
         break;
     default:
2effd274
         goto fail;
     }
 
e21ac209
     http_log("%s:%d - - \"PLAY %s/streamid=%d %s\"\n",
115329f1
              ipaddr, ntohs(dest_addr->sin_port),
bc351386
              c->stream->filename, stream_index, c->protocol);
6edd6884
 
2effd274
     /* normally, no packets should be output here, but the packet size may be checked */
bc351386
     if (url_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
2effd274
         /* XXX: close stream */
         goto fail;
     }
3c27199b
     av_set_parameters(ctx, NULL);
2effd274
     if (av_write_header(ctx) < 0) {
     fail:
         if (h)
             url_close(h);
         av_free(ctx);
         return -1;
     }
899681cd
     url_close_dyn_buf(ctx->pb, &dummy_buf);
2effd274
     av_free(dummy_buf);
115329f1
 
2effd274
     c->rtp_ctx[stream_index] = ctx;
     return 0;
 }
 
 /********************************************************************/
 /* ffserver initialization */
 
b29f97d1
 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec)
2effd274
 {
     AVStream *fst;
 
     fst = av_mallocz(sizeof(AVStream));
     if (!fst)
         return NULL;
8d931070
     fst->codec= avcodec_alloc_context();
2effd274
     fst->priv_data = av_mallocz(sizeof(FeedData));
01f4895c
     memcpy(fst->codec, codec, sizeof(AVCodecContext));
d445a7e9
     fst->index = stream->nb_streams;
7c054ea7
     av_set_pts_info(fst, 33, 1, 90000);
2effd274
     stream->streams[stream->nb_streams++] = fst;
     return fst;
 }
 
85f07f22
 /* return the stream number in the feed */
b29f97d1
 static int add_av_stream(FFStream *feed, AVStream *st)
85f07f22
 {
     AVStream *fst;
     AVCodecContext *av, *av1;
     int i;
 
01f4895c
     av = st->codec;
85f07f22
     for(i=0;i<feed->nb_streams;i++) {
         st = feed->streams[i];
01f4895c
         av1 = st->codec;
f747e6d3
         if (av1->codec_id == av->codec_id &&
             av1->codec_type == av->codec_type &&
85f07f22
             av1->bit_rate == av->bit_rate) {
 
             switch(av->codec_type) {
             case CODEC_TYPE_AUDIO:
                 if (av1->channels == av->channels &&
                     av1->sample_rate == av->sample_rate)
                     goto found;
                 break;
             case CODEC_TYPE_VIDEO:
                 if (av1->width == av->width &&
                     av1->height == av->height &&
c0df9d75
                     av1->time_base.den == av->time_base.den &&
                     av1->time_base.num == av->time_base.num &&
85f07f22
                     av1->gop_size == av->gop_size)
                     goto found;
                 break;
f747e6d3
             default:
0f4e8165
                 abort();
85f07f22
             }
         }
     }
115329f1
 
2effd274
     fst = add_av_stream1(feed, av);
85f07f22
     if (!fst)
         return -1;
     return feed->nb_streams - 1;
  found:
     return i;
 }
 
b29f97d1
 static void remove_stream(FFStream *stream)
2effd274
 {
     FFStream **ps;
     ps = &first_stream;
     while (*ps != NULL) {
611c5741
         if (*ps == stream)
2effd274
             *ps = (*ps)->next;
611c5741
         else
2effd274
             ps = &(*ps)->next;
     }
 }
 
0fa45e19
 /* specific mpeg4 handling : we extract the raw parameters */
b29f97d1
 static void extract_mpeg4_header(AVFormatContext *infile)
0fa45e19
 {
     int mpeg4_count, i, size;
     AVPacket pkt;
     AVStream *st;
0c1a9eda
     const uint8_t *p;
0fa45e19
 
     mpeg4_count = 0;
     for(i=0;i<infile->nb_streams;i++) {
         st = infile->streams[i];
01f4895c
         if (st->codec->codec_id == CODEC_ID_MPEG4 &&
             st->codec->extradata_size == 0) {
0fa45e19
             mpeg4_count++;
         }
     }
     if (!mpeg4_count)
         return;
 
d445a7e9
     printf("MPEG4 without extra data: trying to find header in %s\n", infile->filename);
0fa45e19
     while (mpeg4_count > 0) {
         if (av_read_packet(infile, &pkt) < 0)
             break;
         st = infile->streams[pkt.stream_index];
01f4895c
         if (st->codec->codec_id == CODEC_ID_MPEG4 &&
             st->codec->extradata_size == 0) {
             av_freep(&st->codec->extradata);
0fa45e19
             /* fill extradata with the header */
             /* XXX: we make hard suppositions here ! */
             p = pkt.data;
             while (p < pkt.data + pkt.size - 4) {
                 /* stop when vop header is found */
115329f1
                 if (p[0] == 0x00 && p[1] == 0x00 &&
0fa45e19
                     p[2] == 0x01 && p[3] == 0xb6) {
                     size = p - pkt.data;
750f0e1f
                     //                    av_hex_dump_log(infile, AV_LOG_DEBUG, pkt.data, size);
01f4895c
                     st->codec->extradata = av_malloc(size);
                     st->codec->extradata_size = size;
                     memcpy(st->codec->extradata, pkt.data, size);
0fa45e19
                     break;
                 }
                 p++;
             }
             mpeg4_count--;
         }
         av_free_packet(&pkt);
     }
 }
 
2effd274
 /* compute the needed AVStream for each file */
b29f97d1
 static void build_file_streams(void)
2effd274
 {
     FFStream *stream, *stream_next;
     AVFormatContext *infile;
f61d45c9
     int i, ret;
2effd274
 
     /* gather all streams */
     for(stream = first_stream; stream != NULL; stream = stream_next) {
         stream_next = stream->next;
         if (stream->stream_type == STREAM_TYPE_LIVE &&
             !stream->feed) {
             /* the stream comes from a file */
             /* try to open the file */
             /* open stream */
e240a0bb
             stream->ap_in = av_mallocz(sizeof(AVFormatParameters));
25e3e53d
             if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
e240a0bb
                 /* specific case : if transport stream output to RTP,
                    we use a raw transport stream reader */
                 stream->ap_in->mpeg2ts_raw = 1;
                 stream->ap_in->mpeg2ts_compute_pcr = 1;
             }
115329f1
 
f61d45c9
             if ((ret = av_open_input_file(&infile, stream->feed_filename,
                                           stream->ifmt, 0, stream->ap_in)) < 0) {
                 http_log("could not open %s: %d\n", stream->feed_filename, ret);
2effd274
                 /* remove stream (no need to spend more time on it) */
             fail:
                 remove_stream(stream);
             } else {
                 /* find all the AVStreams inside and reference them in
                    'stream' */
                 if (av_find_stream_info(infile) < 0) {
2dae1dd0
                     http_log("Could not find codec parameters from '%s'\n",
2effd274
                              stream->feed_filename);
                     av_close_input_file(infile);
                     goto fail;
                 }
0fa45e19
                 extract_mpeg4_header(infile);
 
611c5741
                 for(i=0;i<infile->nb_streams;i++)
01f4895c
                     add_av_stream1(stream, infile->streams[i]->codec);
611c5741
 
2effd274
                 av_close_input_file(infile);
             }
         }
     }
 }
 
85f07f22
 /* compute the needed AVStream for each feed */
b29f97d1
 static void build_feed_streams(void)
85f07f22
 {
     FFStream *stream, *feed;
     int i;
 
     /* gather all streams */
     for(stream = first_stream; stream != NULL; stream = stream->next) {
         feed = stream->feed;
         if (feed) {
             if (!stream->is_feed) {
2effd274
                 /* we handle a stream coming from a feed */
611c5741
                 for(i=0;i<stream->nb_streams;i++)
85f07f22
                     stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
cde25790
             }
         }
     }
 
     /* gather all streams */
     for(stream = first_stream; stream != NULL; stream = stream->next) {
         feed = stream->feed;
         if (feed) {
             if (stream->is_feed) {
611c5741
                 for(i=0;i<stream->nb_streams;i++)
85f07f22
                     stream->feed_streams[i] = i;
             }
         }
     }
 
     /* create feed files if needed */
     for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
         int fd;
 
59eb2ed1
         if (url_exist(feed->feed_filename)) {
             /* See if it matches */
             AVFormatContext *s;
             int matches = 0;
 
             if (av_open_input_file(&s, feed->feed_filename, NULL, FFM_PACKET_SIZE, NULL) >= 0) {
                 /* Now see if it matches */
                 if (s->nb_streams == feed->nb_streams) {
                     matches = 1;
                     for(i=0;i<s->nb_streams;i++) {
                         AVStream *sf, *ss;
                         sf = feed->streams[i];
                         ss = s->streams[i];
 
                         if (sf->index != ss->index ||
                             sf->id != ss->id) {
80b616fc
                             http_log("Index & Id do not match for stream %d (%s)\n",
e240a0bb
                                    i, feed->feed_filename);
59eb2ed1
                             matches = 0;
                         } else {
                             AVCodecContext *ccf, *ccs;
 
01f4895c
                             ccf = sf->codec;
                             ccs = ss->codec;
59eb2ed1
 #define CHECK_CODEC(x)  (ccf->x != ccs->x)
 
                             if (CHECK_CODEC(codec) || CHECK_CODEC(codec_type)) {
80b616fc
                                 http_log("Codecs do not match for stream %d\n", i);
59eb2ed1
                                 matches = 0;
                             } else if (CHECK_CODEC(bit_rate) || CHECK_CODEC(flags)) {
80b616fc
                                 http_log("Codec bitrates do not match for stream %d\n", i);
59eb2ed1
                                 matches = 0;
                             } else if (ccf->codec_type == CODEC_TYPE_VIDEO) {
c0df9d75
                                 if (CHECK_CODEC(time_base.den) ||
                                     CHECK_CODEC(time_base.num) ||
59eb2ed1
                                     CHECK_CODEC(width) ||
                                     CHECK_CODEC(height)) {
80b616fc
                                     http_log("Codec width, height and framerate do not match for stream %d\n", i);
59eb2ed1
                                     matches = 0;
                                 }
                             } else if (ccf->codec_type == CODEC_TYPE_AUDIO) {
                                 if (CHECK_CODEC(sample_rate) ||
                                     CHECK_CODEC(channels) ||
                                     CHECK_CODEC(frame_size)) {
80b616fc
                                     http_log("Codec sample_rate, channels, frame_size do not match for stream %d\n", i);
59eb2ed1
                                     matches = 0;
                                 }
                             } else {
80b616fc
                                 http_log("Unknown codec type\n");
59eb2ed1
                                 matches = 0;
                             }
                         }
611c5741
                         if (!matches)
59eb2ed1
                             break;
                     }
611c5741
                 } else
80b616fc
                     http_log("Deleting feed file '%s' as stream counts differ (%d != %d)\n",
59eb2ed1
                         feed->feed_filename, s->nb_streams, feed->nb_streams);
 
                 av_close_input_file(s);
611c5741
             } else
80b616fc
                 http_log("Deleting feed file '%s' as it appears to be corrupt\n",
59eb2ed1
                         feed->feed_filename);
611c5741
 
e322ea48
             if (!matches) {
                 if (feed->readonly) {
80b616fc
                     http_log("Unable to delete feed file '%s' as it is marked readonly\n",
e322ea48
                         feed->feed_filename);
                     exit(1);
                 }
59eb2ed1
                 unlink(feed->feed_filename);
e322ea48
             }
59eb2ed1
         }
85f07f22
         if (!url_exist(feed->feed_filename)) {
1d6eeebe
             AVFormatContext s1 = {0}, *s = &s1;
85f07f22
 
e322ea48
             if (feed->readonly) {
80b616fc
                 http_log("Unable to create feed file '%s' as it is marked readonly\n",
e322ea48
                     feed->feed_filename);
                 exit(1);
             }
 
85f07f22
             /* only write the header of the ffm file */
             if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
b4befb99
                 http_log("Could not open output feed file '%s'\n",
                          feed->feed_filename);
85f07f22
                 exit(1);
             }
bd7cf6ad
             s->oformat = feed->fmt;
85f07f22
             s->nb_streams = feed->nb_streams;
             for(i=0;i<s->nb_streams;i++) {
                 AVStream *st;
                 st = feed->streams[i];
                 s->streams[i] = st;
             }
3c27199b
             av_set_parameters(s, NULL);
f75cdda7
             if (av_write_header(s) < 0) {
b4befb99
                 http_log("Container doesn't supports the required parameters\n");
f75cdda7
                 exit(1);
             }
bd7cf6ad
             /* XXX: need better api */
             av_freep(&s->priv_data);
899681cd
             url_fclose(s->pb);
85f07f22
         }
         /* get feed size and write index */
         fd = open(feed->feed_filename, O_RDONLY);
         if (fd < 0) {
b4befb99
             http_log("Could not open output feed file '%s'\n",
85f07f22
                     feed->feed_filename);
             exit(1);
         }
 
         feed->feed_write_index = ffm_read_write_index(fd);
         feed->feed_size = lseek(fd, 0, SEEK_END);
         /* ensure that we do not wrap before the end of file */
6b0bdc75
         if (feed->feed_max_size && feed->feed_max_size < feed->feed_size)
85f07f22
             feed->feed_max_size = feed->feed_size;
 
         close(fd);
     }
 }
 
6edd6884
 /* compute the bandwidth used by each stream */
 static void compute_bandwidth(void)
 {
177d2564
     unsigned bandwidth;
     int i;
6edd6884
     FFStream *stream;
115329f1
 
6edd6884
     for(stream = first_stream; stream != NULL; stream = stream->next) {
         bandwidth = 0;
         for(i=0;i<stream->nb_streams;i++) {
             AVStream *st = stream->streams[i];
01f4895c
             switch(st->codec->codec_type) {
6edd6884
             case CODEC_TYPE_AUDIO:
             case CODEC_TYPE_VIDEO:
01f4895c
                 bandwidth += st->codec->bit_rate;
6edd6884
                 break;
             default:
                 break;
             }
         }
         stream->bandwidth = (bandwidth + 999) / 1000;
     }
 }
 
85f07f22
 static void get_arg(char *buf, int buf_size, const char **pp)
 {
     const char *p;
     char *q;
     int quote;
 
     p = *pp;
     while (isspace(*p)) p++;
     q = buf;
     quote = 0;
     if (*p == '\"' || *p == '\'')
         quote = *p++;
     for(;;) {
         if (quote) {
             if (*p == quote)
                 break;
         } else {
             if (isspace(*p))
                 break;
         }
         if (*p == '\0')
             break;
         if ((q - buf) < buf_size - 1)
             *q++ = *p;
         p++;
     }
     *q = '\0';
     if (quote && *p == quote)
         p++;
     *pp = p;
 }
 
 /* add a codec and set the default parameters */
b29f97d1
 static void add_codec(FFStream *stream, AVCodecContext *av)
85f07f22
 {
     AVStream *st;
 
     /* compute default parameters */
     switch(av->codec_type) {
     case CODEC_TYPE_AUDIO:
         if (av->bit_rate == 0)
             av->bit_rate = 64000;
         if (av->sample_rate == 0)
             av->sample_rate = 22050;
         if (av->channels == 0)
             av->channels = 1;
         break;
     case CODEC_TYPE_VIDEO:
         if (av->bit_rate == 0)
             av->bit_rate = 64000;
c0df9d75
         if (av->time_base.num == 0){
             av->time_base.den = 5;
             av->time_base.num = 1;
14bea432
         }
85f07f22
         if (av->width == 0 || av->height == 0) {
             av->width = 160;
             av->height = 128;
         }
ba9b374f
         /* Bitrate tolerance is less for streaming */
42a63c6a
         if (av->bit_rate_tolerance == 0)
1692008f
             av->bit_rate_tolerance = FFMAX(av->bit_rate / 4,
                       (int64_t)av->bit_rate*av->time_base.num/av->time_base.den);
42a63c6a
         if (av->qmin == 0)
             av->qmin = 3;
         if (av->qmax == 0)
             av->qmax = 31;
         if (av->max_qdiff == 0)
             av->max_qdiff = 3;
ba9b374f
         av->qcompress = 0.5;
         av->qblur = 0.5;
68d7eef9
 
115329f1
         if (!av->nsse_weight)
291fe90a
             av->nsse_weight = 8;
 
         av->frame_skip_cmp = FF_CMP_DCTMAX;
         av->me_method = ME_EPZS;
         av->rc_buffer_aggressivity = 1.0;
 
a782f209
         if (!av->rc_eq)
             av->rc_eq = "tex^qComp";
         if (!av->i_quant_factor)
b3a391e8
             av->i_quant_factor = -0.8;
a782f209
         if (!av->b_quant_factor)
             av->b_quant_factor = 1.25;
         if (!av->b_quant_offset)
             av->b_quant_offset = 1.25;
d6562d2c
         if (!av->rc_max_rate)
             av->rc_max_rate = av->bit_rate * 2;
a782f209
 
291fe90a
         if (av->rc_max_rate && !av->rc_buffer_size) {
             av->rc_buffer_size = av->rc_max_rate;
         }
 
 
85f07f22
         break;
f747e6d3
     default:
0f4e8165
         abort();
85f07f22
     }
 
     st = av_mallocz(sizeof(AVStream));
     if (!st)
         return;
8d931070
     st->codec = avcodec_alloc_context();
85f07f22
     stream->streams[stream->nb_streams++] = st;
01f4895c
     memcpy(st->codec, av, sizeof(AVCodecContext));
85f07f22
 }
 
aed46465
 static enum CodecID opt_audio_codec(const char *arg)
f747e6d3
 {
562b2163
     AVCodec *p= avcodec_find_encoder_by_name(arg);
f747e6d3
 
562b2163
     if (p == NULL || p->type != CODEC_TYPE_AUDIO)
f747e6d3
         return CODEC_ID_NONE;
 
     return p->id;
 }
 
aed46465
 static enum CodecID opt_video_codec(const char *arg)
f747e6d3
 {
562b2163
     AVCodec *p= avcodec_find_encoder_by_name(arg);
f747e6d3
 
562b2163
     if (p == NULL || p->type != CODEC_TYPE_VIDEO)
f747e6d3
         return CODEC_ID_NONE;
 
     return p->id;
 }
 
2effd274
 /* simplistic plugin support */
 
b250f9c6
 #if HAVE_DLOPEN
7b49ce2e
 static void load_module(const char *filename)
2effd274
 {
     void *dll;
     void (*init_func)(void);
     dll = dlopen(filename, RTLD_NOW);
     if (!dll) {
         fprintf(stderr, "Could not load module '%s' - %s\n",
                 filename, dlerror());
         return;
     }
115329f1
 
2effd274
     init_func = dlsym(dll, "ffserver_module_init");
     if (!init_func) {
115329f1
         fprintf(stderr,
2effd274
                 "%s: init function 'ffserver_module_init()' not found\n",
                 filename);
         dlclose(dll);
     }
 
     init_func();
 }
6638d424
 #endif
2effd274
 
df1a4b11
 static int ffserver_opt_default(const char *opt, const char *arg,
7ab08864
                        AVCodecContext *avctx, int type)
 {
f16dd7e6
     int ret = 0;
5c548937
     const AVOption *o = av_find_opt(avctx, opt, NULL, type, type);
     if(o)
f16dd7e6
         ret = av_set_string3(avctx, opt, arg, 1, NULL);
     return ret;
7ab08864
 }
 
b29f97d1
 static int parse_ffconfig(const char *filename)
85f07f22
 {
     FILE *f;
     char line[1024];
     char cmd[64];
     char arg[1024];
     const char *p;
     int val, errors, line_num;
cde25790
     FFStream **last_stream, *stream, *redirect;
85f07f22
     FFStream **last_feed, *feed;
     AVCodecContext audio_enc, video_enc;
aed46465
     enum CodecID audio_id, video_id;
85f07f22
 
     f = fopen(filename, "r");
     if (!f) {
         perror(filename);
         return -1;
     }
115329f1
 
85f07f22
     errors = 0;
     line_num = 0;
     first_stream = NULL;
     last_stream = &first_stream;
     first_feed = NULL;
     last_feed = &first_feed;
     stream = NULL;
     feed = NULL;
cde25790
     redirect = NULL;
85f07f22
     audio_id = CODEC_ID_NONE;
     video_id = CODEC_ID_NONE;
     for(;;) {
         if (fgets(line, sizeof(line), f) == NULL)
             break;
         line_num++;
         p = line;
115329f1
         while (isspace(*p))
85f07f22
             p++;
         if (*p == '\0' || *p == '#')
             continue;
 
         get_arg(cmd, sizeof(cmd), &p);
115329f1
 
85f07f22
         if (!strcasecmp(cmd, "Port")) {
             get_arg(arg, sizeof(arg), &p);
aabce533
             val = atoi(arg);
             if (val < 1 || val > 65536) {
                 fprintf(stderr, "%s:%d: Invalid port: %s\n",
                         filename, line_num, arg);
                 errors++;
             }
             my_http_addr.sin_port = htons(val);
85f07f22
         } else if (!strcasecmp(cmd, "BindAddress")) {
             get_arg(arg, sizeof(arg), &p);
ad8b8abc
             if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
                 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
2effd274
                         filename, line_num, arg);
                 errors++;
             }
         } else if (!strcasecmp(cmd, "NoDaemon")) {
             ffserver_daemon = 0;
         } else if (!strcasecmp(cmd, "RTSPPort")) {
             get_arg(arg, sizeof(arg), &p);
aabce533
             val = atoi(arg);
             if (val < 1 || val > 65536) {
                 fprintf(stderr, "%s:%d: Invalid port: %s\n",
                         filename, line_num, arg);
                 errors++;
             }
             my_rtsp_addr.sin_port = htons(atoi(arg));
2effd274
         } else if (!strcasecmp(cmd, "RTSPBindAddress")) {
             get_arg(arg, sizeof(arg), &p);
ad8b8abc
             if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
                 fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
85f07f22
                         filename, line_num, arg);
                 errors++;
             }
1c9ff179
         } else if (!strcasecmp(cmd, "MaxHTTPConnections")) {
             get_arg(arg, sizeof(arg), &p);
             val = atoi(arg);
             if (val < 1 || val > 65536) {
                 fprintf(stderr, "%s:%d: Invalid MaxHTTPConnections: %s\n",
                         filename, line_num, arg);
                 errors++;
             }
             nb_max_http_connections = val;
85f07f22
         } else if (!strcasecmp(cmd, "MaxClients")) {
             get_arg(arg, sizeof(arg), &p);
             val = atoi(arg);
1c9ff179
             if (val < 1 || val > nb_max_http_connections) {
115329f1
                 fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n",
85f07f22
                         filename, line_num, arg);
                 errors++;
             } else {
                 nb_max_connections = val;
             }
42a63c6a
         } else if (!strcasecmp(cmd, "MaxBandwidth")) {
0dc17c21
             int64_t llval;
42a63c6a
             get_arg(arg, sizeof(arg), &p);
0dc17c21
             llval = atoll(arg);
             if (llval < 10 || llval > 10000000) {
115329f1
                 fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n",
42a63c6a
                         filename, line_num, arg);
                 errors++;
611c5741
             } else
0dc17c21
                 max_bandwidth = llval;
85f07f22
         } else if (!strcasecmp(cmd, "CustomLog")) {
4e8f77ab
             if (!ffserver_debug)
                 get_arg(logfilename, sizeof(logfilename), &p);
85f07f22
         } else if (!strcasecmp(cmd, "<Feed")) {
             /*********************************************/
             /* Feed related options */
             char *q;
             if (stream || feed) {
                 fprintf(stderr, "%s:%d: Already in a tag\n",
                         filename, line_num);
             } else {
                 feed = av_mallocz(sizeof(FFStream));
                 /* add in stream list */
                 *last_stream = feed;
                 last_stream = &feed->next;
                 /* add in feed list */
                 *last_feed = feed;
                 last_feed = &feed->next_feed;
115329f1
 
85f07f22
                 get_arg(feed->filename, sizeof(feed->filename), &p);
                 q = strrchr(feed->filename, '>');
                 if (*q)
                     *q = '\0';
                 feed->fmt = guess_format("ffm", NULL, NULL);
                 /* defaut feed file */
                 snprintf(feed->feed_filename, sizeof(feed->feed_filename),
                          "/tmp/%s.ffm", feed->filename);
                 feed->feed_max_size = 5 * 1024 * 1024;
                 feed->is_feed = 1;
                 feed->feed = feed; /* self feeding :-) */
             }
cde25790
         } else if (!strcasecmp(cmd, "Launch")) {
             if (feed) {
                 int i;
 
90901860
                 feed->child_argv = av_mallocz(64 * sizeof(char *));
cde25790
 
ac6a655b
                 for (i = 0; i < 62; i++) {
5eb782f0
                     get_arg(arg, sizeof(arg), &p);
                     if (!arg[0])
cde25790
                         break;
 
5eb782f0
                     feed->child_argv[i] = av_strdup(arg);
cde25790
                 }
 
                 feed->child_argv[i] = av_malloc(30 + strlen(feed->filename));
 
bb270c08
                 snprintf(feed->child_argv[i], 30+strlen(feed->filename),
                     "http://%s:%d/%s",
                         (my_http_addr.sin_addr.s_addr == INADDR_ANY) ? "127.0.0.1" :
                     inet_ntoa(my_http_addr.sin_addr),
                     ntohs(my_http_addr.sin_port), feed->filename);
cde25790
             }
e322ea48
         } else if (!strcasecmp(cmd, "ReadOnlyFile")) {
             if (feed) {
                 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
                 feed->readonly = 1;
             } else if (stream) {
                 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
             }
85f07f22
         } else if (!strcasecmp(cmd, "File")) {
             if (feed) {
                 get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
611c5741
             } else if (stream)
85f07f22
                 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
         } else if (!strcasecmp(cmd, "FileMaxSize")) {
             if (feed) {
815f98cc
                 char *p1;
85f07f22
                 double fsize;
 
                 get_arg(arg, sizeof(arg), &p);
                 p1 = arg;
815f98cc
                 fsize = strtod(p1, &p1);
85f07f22
                 switch(toupper(*p1)) {
                 case 'K':
                     fsize *= 1024;
                     break;
                 case 'M':
                     fsize *= 1024 * 1024;
                     break;
                 case 'G':
                     fsize *= 1024 * 1024 * 1024;
                     break;
                 }
0c1a9eda
                 feed->feed_max_size = (int64_t)fsize;
85f07f22
             }
         } else if (!strcasecmp(cmd, "</Feed>")) {
             if (!feed) {
                 fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
                         filename, line_num);
                 errors++;
             }
             feed = NULL;
         } else if (!strcasecmp(cmd, "<Stream")) {
             /*********************************************/
             /* Stream related options */
             char *q;
             if (stream || feed) {
                 fprintf(stderr, "%s:%d: Already in a tag\n",
                         filename, line_num);
             } else {
7ab08864
                 const AVClass *class;
85f07f22
                 stream = av_mallocz(sizeof(FFStream));
                 *last_stream = stream;
                 last_stream = &stream->next;
 
                 get_arg(stream->filename, sizeof(stream->filename), &p);
                 q = strrchr(stream->filename, '>');
                 if (*q)
                     *q = '\0';
8256c0a3
                 stream->fmt = guess_stream_format(NULL, stream->filename, NULL);
7ab08864
                 /* fetch avclass so AVOption works
                  * FIXME try to use avcodec_get_context_defaults2
                  * without changing defaults too much */
                 avcodec_get_context_defaults(&video_enc);
                 class = video_enc.av_class;
85f07f22
                 memset(&audio_enc, 0, sizeof(AVCodecContext));
                 memset(&video_enc, 0, sizeof(AVCodecContext));
7ab08864
                 audio_enc.av_class = class;
                 video_enc.av_class = class;
85f07f22
                 audio_id = CODEC_ID_NONE;
                 video_id = CODEC_ID_NONE;
                 if (stream->fmt) {
                     audio_id = stream->fmt->audio_codec;
                     video_id = stream->fmt->video_codec;
                 }
             }
         } else if (!strcasecmp(cmd, "Feed")) {
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
                 FFStream *sfeed;
115329f1
 
85f07f22
                 sfeed = first_feed;
                 while (sfeed != NULL) {
                     if (!strcmp(sfeed->filename, arg))
                         break;
                     sfeed = sfeed->next_feed;
                 }
611c5741
                 if (!sfeed)
85f07f22
                     fprintf(stderr, "%s:%d: feed '%s' not defined\n",
                             filename, line_num, arg);
611c5741
                 else
85f07f22
                     stream->feed = sfeed;
             }
         } else if (!strcasecmp(cmd, "Format")) {
             get_arg(arg, sizeof(arg), &p);
5735f6dc
             if (stream) {
65f2f028
                 if (!strcmp(arg, "status")) {
                     stream->stream_type = STREAM_TYPE_STATUS;
                     stream->fmt = NULL;
                 } else {
                     stream->stream_type = STREAM_TYPE_LIVE;
                     /* jpeg cannot be used here, so use single frame jpeg */
                     if (!strcmp(arg, "jpeg"))
                         strcpy(arg, "mjpeg");
                     stream->fmt = guess_stream_format(arg, NULL, NULL);
                     if (!stream->fmt) {
                         fprintf(stderr, "%s:%d: Unknown Format: %s\n",
                                 filename, line_num, arg);
                         errors++;
                     }
                 }
                 if (stream->fmt) {
                     audio_id = stream->fmt->audio_codec;
                     video_id = stream->fmt->video_codec;
85f07f22
                 }
5735f6dc
             }
e240a0bb
         } else if (!strcasecmp(cmd, "InputFormat")) {
d2040a8f
             get_arg(arg, sizeof(arg), &p);
a69b06be
             if (stream) {
f9739144
                 stream->ifmt = av_find_input_format(arg);
                 if (!stream->ifmt) {
                     fprintf(stderr, "%s:%d: Unknown input format: %s\n",
                             filename, line_num, arg);
                 }
a69b06be
             }
cde25790
         } else if (!strcasecmp(cmd, "FaviconURL")) {
             if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
                 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
             } else {
115329f1
                 fprintf(stderr, "%s:%d: FaviconURL only permitted for status streams\n",
cde25790
                             filename, line_num);
                 errors++;
             }
2ac887ba
         } else if (!strcasecmp(cmd, "Author")) {
611c5741
             if (stream)
2ac887ba
                 get_arg(stream->author, sizeof(stream->author), &p);
         } else if (!strcasecmp(cmd, "Comment")) {
611c5741
             if (stream)
2ac887ba
                 get_arg(stream->comment, sizeof(stream->comment), &p);
         } else if (!strcasecmp(cmd, "Copyright")) {
611c5741
             if (stream)
2ac887ba
                 get_arg(stream->copyright, sizeof(stream->copyright), &p);
         } else if (!strcasecmp(cmd, "Title")) {
611c5741
             if (stream)
2ac887ba
                 get_arg(stream->title, sizeof(stream->title), &p);
42a63c6a
         } else if (!strcasecmp(cmd, "Preroll")) {
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
8256c0a3
                 stream->prebuffer = atof(arg) * 1000;
79c4ea3c
         } else if (!strcasecmp(cmd, "StartSendOnKey")) {
611c5741
             if (stream)
79c4ea3c
                 stream->send_on_key = 1;
f747e6d3
         } else if (!strcasecmp(cmd, "AudioCodec")) {
             get_arg(arg, sizeof(arg), &p);
             audio_id = opt_audio_codec(arg);
             if (audio_id == CODEC_ID_NONE) {
115329f1
                 fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n",
f747e6d3
                         filename, line_num, arg);
                 errors++;
             }
         } else if (!strcasecmp(cmd, "VideoCodec")) {
             get_arg(arg, sizeof(arg), &p);
             video_id = opt_video_codec(arg);
             if (video_id == CODEC_ID_NONE) {
115329f1
                 fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n",
f747e6d3
                         filename, line_num, arg);
                 errors++;
             }
ec3b2232
         } else if (!strcasecmp(cmd, "MaxTime")) {
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
8256c0a3
                 stream->max_time = atof(arg) * 1000;
85f07f22
         } else if (!strcasecmp(cmd, "AudioBitRate")) {
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
85f07f22
                 audio_enc.bit_rate = atoi(arg) * 1000;
         } else if (!strcasecmp(cmd, "AudioChannels")) {
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
85f07f22
                 audio_enc.channels = atoi(arg);
         } else if (!strcasecmp(cmd, "AudioSampleRate")) {
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
85f07f22
                 audio_enc.sample_rate = atoi(arg);
bb270c08
         } else if (!strcasecmp(cmd, "AudioQuality")) {
             get_arg(arg, sizeof(arg), &p);
81e0d0b4
             if (stream) {
1e491e29
 //                audio_enc.quality = atof(arg) * 1000;
81e0d0b4
             }
d6562d2c
         } else if (!strcasecmp(cmd, "VideoBitRateRange")) {
             if (stream) {
                 int minrate, maxrate;
 
                 get_arg(arg, sizeof(arg), &p);
 
                 if (sscanf(arg, "%d-%d", &minrate, &maxrate) == 2) {
                     video_enc.rc_min_rate = minrate * 1000;
                     video_enc.rc_max_rate = maxrate * 1000;
                 } else {
115329f1
                     fprintf(stderr, "%s:%d: Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n",
d6562d2c
                             filename, line_num, arg);
                     errors++;
                 }
             }
291fe90a
         } else if (!strcasecmp(cmd, "Debug")) {
             if (stream) {
                 get_arg(arg, sizeof(arg), &p);
                 video_enc.debug = strtol(arg,0,0);
             }
         } else if (!strcasecmp(cmd, "Strict")) {
             if (stream) {
                 get_arg(arg, sizeof(arg), &p);
                 video_enc.strict_std_compliance = atoi(arg);
             }
46026f4e
         } else if (!strcasecmp(cmd, "VideoBufferSize")) {
             if (stream) {
                 get_arg(arg, sizeof(arg), &p);
291fe90a
                 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
46026f4e
             }
d6562d2c
         } else if (!strcasecmp(cmd, "VideoBitRateTolerance")) {
             if (stream) {
                 get_arg(arg, sizeof(arg), &p);
                 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
             }
85f07f22
         } else if (!strcasecmp(cmd, "VideoBitRate")) {
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
                 video_enc.bit_rate = atoi(arg) * 1000;
             }
         } else if (!strcasecmp(cmd, "VideoSize")) {
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
b33ece16
                 av_parse_video_frame_size(&video_enc.width, &video_enc.height, arg);
85f07f22
                 if ((video_enc.width % 16) != 0 ||
                     (video_enc.height % 16) != 0) {
                     fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
                             filename, line_num);
                     errors++;
                 }
             }
         } else if (!strcasecmp(cmd, "VideoFrameRate")) {
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
36907468
                 AVRational frame_rate;
                 if (av_parse_video_frame_rate(&frame_rate, arg) < 0) {
                     fprintf(stderr, "Incorrect frame rate\n");
                     errors++;
                 } else {
                     video_enc.time_base.num = frame_rate.den;
                     video_enc.time_base.den = frame_rate.num;
                 }
85f07f22
             }
         } else if (!strcasecmp(cmd, "VideoGopSize")) {
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
85f07f22
                 video_enc.gop_size = atoi(arg);
         } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
611c5741
             if (stream)
85f07f22
                 video_enc.gop_size = 1;
e7f9c674
         } else if (!strcasecmp(cmd, "VideoHighQuality")) {
611c5741
             if (stream)
7d1c3fc1
                 video_enc.mb_decision = FF_MB_DECISION_BITS;
e322ea48
         } else if (!strcasecmp(cmd, "Video4MotionVector")) {
             if (stream) {
7d1c3fc1
                 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
e322ea48
                 video_enc.flags |= CODEC_FLAG_4MV;
             }
7ab08864
         } else if (!strcasecmp(cmd, "AVOptionVideo") ||
                    !strcasecmp(cmd, "AVOptionAudio")) {
             char arg2[1024];
             AVCodecContext *avctx;
             int type;
             get_arg(arg, sizeof(arg), &p);
             get_arg(arg2, sizeof(arg2), &p);
             if (!strcasecmp(cmd, "AVOptionVideo")) {
                 avctx = &video_enc;
                 type = AV_OPT_FLAG_VIDEO_PARAM;
             } else {
                 avctx = &audio_enc;
                 type = AV_OPT_FLAG_AUDIO_PARAM;
             }
df1a4b11
             if (ffserver_opt_default(arg, arg2, avctx, type|AV_OPT_FLAG_ENCODING_PARAM)) {
7ab08864
                 fprintf(stderr, "AVOption error: %s %s\n", arg, arg2);
                 errors++;
             }
038a1243
         } else if (!strcasecmp(cmd, "VideoTag")) {
             get_arg(arg, sizeof(arg), &p);
611c5741
             if ((strlen(arg) == 4) && stream)
2bb6eba2
                 video_enc.codec_tag = AV_RL32(arg);
267b0e57
         } else if (!strcasecmp(cmd, "BitExact")) {
611c5741
             if (stream)
267b0e57
                 video_enc.flags |= CODEC_FLAG_BITEXACT;
         } else if (!strcasecmp(cmd, "DctFastint")) {
611c5741
             if (stream)
267b0e57
                 video_enc.dct_algo  = FF_DCT_FASTINT;
         } else if (!strcasecmp(cmd, "IdctSimple")) {
611c5741
             if (stream)
267b0e57
                 video_enc.idct_algo = FF_IDCT_SIMPLE;
         } else if (!strcasecmp(cmd, "Qscale")) {
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
                 video_enc.flags |= CODEC_FLAG_QSCALE;
                 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
             }
42a63c6a
         } else if (!strcasecmp(cmd, "VideoQDiff")) {
a782f209
             get_arg(arg, sizeof(arg), &p);
42a63c6a
             if (stream) {
                 video_enc.max_qdiff = atoi(arg);
                 if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
                     fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
                             filename, line_num);
                     errors++;
                 }
             }
         } else if (!strcasecmp(cmd, "VideoQMax")) {
a782f209
             get_arg(arg, sizeof(arg), &p);
42a63c6a
             if (stream) {
                 video_enc.qmax = atoi(arg);
                 if (video_enc.qmax < 1 || video_enc.qmax > 31) {
                     fprintf(stderr, "%s:%d: VideoQMax out of range\n",
                             filename, line_num);
                     errors++;
                 }
             }
         } else if (!strcasecmp(cmd, "VideoQMin")) {
a782f209
             get_arg(arg, sizeof(arg), &p);
42a63c6a
             if (stream) {
                 video_enc.qmin = atoi(arg);
                 if (video_enc.qmin < 1 || video_enc.qmin > 31) {
                     fprintf(stderr, "%s:%d: VideoQMin out of range\n",
                             filename, line_num);
                     errors++;
                 }
             }
6b10e6e4
         } else if (!strcasecmp(cmd, "LumaElim")) {
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
6b10e6e4
                 video_enc.luma_elim_threshold = atoi(arg);
         } else if (!strcasecmp(cmd, "ChromaElim")) {
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
6b10e6e4
                 video_enc.chroma_elim_threshold = atoi(arg);
         } else if (!strcasecmp(cmd, "LumiMask")) {
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
6b10e6e4
                 video_enc.lumi_masking = atof(arg);
         } else if (!strcasecmp(cmd, "DarkMask")) {
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
6b10e6e4
                 video_enc.dark_masking = atof(arg);
85f07f22
         } else if (!strcasecmp(cmd, "NoVideo")) {
             video_id = CODEC_ID_NONE;
         } else if (!strcasecmp(cmd, "NoAudio")) {
             audio_id = CODEC_ID_NONE;
8256c0a3
         } else if (!strcasecmp(cmd, "ACL")) {
             IPAddressACL acl;
 
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (strcasecmp(arg, "allow") == 0)
8256c0a3
                 acl.action = IP_ALLOW;
611c5741
             else if (strcasecmp(arg, "deny") == 0)
8256c0a3
                 acl.action = IP_DENY;
611c5741
             else {
8256c0a3
                 fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
                         filename, line_num, arg);
                 errors++;
             }
 
             get_arg(arg, sizeof(arg), &p);
4ee10633
 
2bd8416e
             if (resolve_host(&acl.first, arg) != 0) {
8256c0a3
                 fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
                         filename, line_num, arg);
                 errors++;
611c5741
             } else
8256c0a3
                 acl.last = acl.first;
 
             get_arg(arg, sizeof(arg), &p);
 
             if (arg[0]) {
2bd8416e
                 if (resolve_host(&acl.last, arg) != 0) {
8256c0a3
                     fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
                             filename, line_num, arg);
                     errors++;
                 }
             }
 
             if (!errors) {
90901860
                 IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
8256c0a3
                 IPAddressACL **naclp = 0;
 
6308caca
                 acl.next = 0;
8256c0a3
                 *nacl = acl;
 
611c5741
                 if (stream)
8256c0a3
                     naclp = &stream->acl;
611c5741
                 else if (feed)
8256c0a3
                     naclp = &feed->acl;
611c5741
                 else {
8256c0a3
                     fprintf(stderr, "%s:%d: ACL found not in <stream> or <feed>\n",
                             filename, line_num);
                     errors++;
                 }
 
                 if (naclp) {
                     while (*naclp)
                         naclp = &(*naclp)->next;
 
                     *naclp = nacl;
                 }
             }
2effd274
         } else if (!strcasecmp(cmd, "RTSPOption")) {
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
                 av_freep(&stream->rtsp_option);
65e72261
                 stream->rtsp_option = av_strdup(arg);
2effd274
             }
829ac53d
         } else if (!strcasecmp(cmd, "MulticastAddress")) {
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
ad8b8abc
                 if (resolve_host(&stream->multicast_ip, arg) != 0) {
                     fprintf(stderr, "%s:%d: Invalid host/IP address: %s\n",
829ac53d
                             filename, line_num, arg);
                     errors++;
                 }
                 stream->is_multicast = 1;
6edd6884
                 stream->loop = 1; /* default is looping */
829ac53d
             }
         } else if (!strcasecmp(cmd, "MulticastPort")) {
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
829ac53d
                 stream->multicast_port = atoi(arg);
6edd6884
         } else if (!strcasecmp(cmd, "MulticastTTL")) {
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
6edd6884
                 stream->multicast_ttl = atoi(arg);
         } else if (!strcasecmp(cmd, "NoLoop")) {
611c5741
             if (stream)
6edd6884
                 stream->loop = 0;
85f07f22
         } else if (!strcasecmp(cmd, "</Stream>")) {
             if (!stream) {
                 fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
                         filename, line_num);
                 errors++;
ce651af2
             } else {
7e183a93
                 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
                     if (audio_id != CODEC_ID_NONE) {
                         audio_enc.codec_type = CODEC_TYPE_AUDIO;
                         audio_enc.codec_id = audio_id;
                         add_codec(stream, &audio_enc);
                     }
                     if (video_id != CODEC_ID_NONE) {
                         video_enc.codec_type = CODEC_TYPE_VIDEO;
                         video_enc.codec_id = video_id;
                         add_codec(stream, &video_enc);
                     }
85f07f22
                 }
7e183a93
                 stream = NULL;
ce651af2
             }
cde25790
         } else if (!strcasecmp(cmd, "<Redirect")) {
             /*********************************************/
             char *q;
             if (stream || feed || redirect) {
                 fprintf(stderr, "%s:%d: Already in a tag\n",
                         filename, line_num);
                 errors++;
             } else {
                 redirect = av_mallocz(sizeof(FFStream));
                 *last_stream = redirect;
                 last_stream = &redirect->next;
 
                 get_arg(redirect->filename, sizeof(redirect->filename), &p);
                 q = strrchr(redirect->filename, '>');
                 if (*q)
                     *q = '\0';
                 redirect->stream_type = STREAM_TYPE_REDIRECT;
             }
         } else if (!strcasecmp(cmd, "URL")) {
611c5741
             if (redirect)
cde25790
                 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
         } else if (!strcasecmp(cmd, "</Redirect>")) {
             if (!redirect) {
                 fprintf(stderr, "%s:%d: No corresponding <Redirect> for </Redirect>\n",
                         filename, line_num);
                 errors++;
a9c1bb71
             } else {
9667a2d2
                 if (!redirect->feed_filename[0]) {
                     fprintf(stderr, "%s:%d: No URL found for <Redirect>\n",
                             filename, line_num);
                     errors++;
                 }
                 redirect = NULL;
a9c1bb71
             }
2effd274
         } else if (!strcasecmp(cmd, "LoadModule")) {
             get_arg(arg, sizeof(arg), &p);
b250f9c6
 #if HAVE_DLOPEN
2effd274
             load_module(arg);
6638d424
 #else
115329f1
             fprintf(stderr, "%s:%d: Module support not compiled into this version: '%s'\n",
6638d424
                     filename, line_num, arg);
             errors++;
 #endif
85f07f22
         } else {
115329f1
             fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n",
85f07f22
                     filename, line_num, cmd);
         }
     }
 
     fclose(f);
     if (errors)
         return -1;
     else
         return 0;
 }
 
5eb765ef
 static void handle_child_exit(int sig)
 {
     pid_t pid;
     int status;
 
     while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
         FFStream *feed;
 
         for (feed = first_feed; feed; feed = feed->next) {
             if (feed->pid == pid) {
                 int uptime = time(0) - feed->pid_start;
 
                 feed->pid = 0;
                 fprintf(stderr, "%s: Pid %d exited with status %d after %d seconds\n", feed->filename, pid, status, uptime);
 
611c5741
                 if (uptime < 30)
5eb765ef
                     /* Turn off any more restarts */
                     feed->child_argv = 0;
             }
         }
     }
 
     need_to_start_children = 1;
 }
 
cf2c671f
 static void opt_debug(void)
5a635bc7
 {
     ffserver_debug = 1;
     ffserver_daemon = 0;
4e8f77ab
     logfilename[0] = '-';
5a635bc7
 }
 
 static void opt_show_help(void)
 {
10ba7404
     printf("usage: ffserver [options]\n"
5a635bc7
            "Hyper fast multi format Audio/Video streaming server\n");
     printf("\n");
     show_help_options(options, "Main options:\n", 0, 0);
 }
 
 static const OptionDef options[] = {
20176cbc
     { "h", OPT_EXIT, {(void*)opt_show_help}, "show help" },
8117c97e
     { "version", OPT_EXIT, {(void*)show_version}, "show version" },
20176cbc
     { "L", OPT_EXIT, {(void*)show_license}, "show license" },
f346033e
     { "formats", OPT_EXIT, {(void*)show_formats}, "show available formats, codecs, protocols, ..." },
5a635bc7
     { "n", OPT_BOOL, {(void *)&no_launch }, "enable no-launch mode" },
     { "d", 0, {(void*)opt_debug}, "enable debug mode" },
     { "f", HAS_ARG | OPT_STRING, {(void*)&config_filename }, "use configfile instead of /etc/ffserver.conf", "configfile" },
     { NULL },
 };
 
85f07f22
 int main(int argc, char **argv)
 {
5eb765ef
     struct sigaction sigact;
85f07f22
 
2c4ae653
     av_register_all();
85f07f22
 
ea9c581f
     show_banner();
3578e9a0
 
85f07f22
     config_filename = "/etc/ffserver.conf";
 
cde25790
     my_program_name = argv[0];
d6562d2c
     my_program_dir = getcwd(0, 0);
2effd274
     ffserver_daemon = 1;
115329f1
 
5a635bc7
     parse_options(argc, argv, options, NULL);
85f07f22
 
59c2959d
     unsetenv("http_proxy");             /* Kill the http_proxy */
cde25790
 
9c868219
     av_random_init(&random_state, av_gettime() + (getpid() << 16));
8256c0a3
 
5eb765ef
     memset(&sigact, 0, sizeof(sigact));
     sigact.sa_handler = handle_child_exit;
     sigact.sa_flags = SA_NOCLDSTOP | SA_RESTART;
     sigaction(SIGCHLD, &sigact, 0);
 
85f07f22
     if (parse_ffconfig(config_filename) < 0) {
         fprintf(stderr, "Incorrect config file - exiting.\n");
         exit(1);
     }
 
f10d55ed
     /* open log file if needed */
     if (logfilename[0] != '\0') {
         if (!strcmp(logfilename, "-"))
a9d9aa36
             logfile = stdout;
f10d55ed
         else
             logfile = fopen(logfilename, "a");
         av_log_set_callback(http_av_log);
     }
 
2effd274
     build_file_streams();
 
85f07f22
     build_feed_streams();
 
6edd6884
     compute_bandwidth();
 
2effd274
     /* put the process in background and detach it from its TTY */
     if (ffserver_daemon) {
         int pid;
 
         pid = fork();
         if (pid < 0) {
             perror("fork");
             exit(1);
         } else if (pid > 0) {
             /* parent : exit */
             exit(0);
         } else {
             /* child */
             setsid();
             close(0);
             open("/dev/null", O_RDWR);
f853bb11
             if (strcmp(logfilename, "-") != 0) {
8256c0a3
                 close(1);
                 dup(0);
             }
             close(2);
2effd274
             dup(0);
         }
     }
 
85f07f22
     /* signal init */
     signal(SIGPIPE, SIG_IGN);
 
496a6132
     if (ffserver_daemon)
         chdir("/");
 
2effd274
     if (http_server() < 0) {
acae1492
         http_log("Could not start server\n");
85f07f22
         exit(1);
     }
 
     return 0;
 }