ffserver.c
85f07f22
 /*
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
 
93613338
 /**
  * @file
  * multiple format streaming server based on the FFmpeg libraries
  */
 
0f4e8165
 #include "config.h"
b250f9c6
 #if !HAVE_CLOSESOCKET
0f4e8165
 #define closesocket close
 #endif
 #include <string.h>
 #include <stdlib.h>
245976da
 #include "libavformat/avformat.h"
9430c232
 // FIXME those are internal headers, ffserver _really_ shouldn't use them
3ee53dab
 #include "libavformat/ffm.h"
245976da
 #include "libavformat/network.h"
 #include "libavformat/os_support.h"
302879cb
 #include "libavformat/rtpdec.h"
245976da
 #include "libavformat/rtsp.h"
403ee835
 #include "libavformat/avio_internal.h"
b263bf66
 #include "libavformat/internal.h"
 #include "libavformat/url.h"
 
959da985
 #include "libavutil/avstring.h"
042819c5
 #include "libavutil/lfg.h"
d2d67e42
 #include "libavutil/dict.h"
0ebcdf5c
 #include "libavutil/mathematics.h"
042819c5
 #include "libavutil/random_seed.h"
737eb597
 #include "libavutil/parseutils.h"
41d0eb1c
 #include "libavutil/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>
 #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
 
89b503b5
 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
 };
 
aff88101
 #define MAX_STREAMS 20
 
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;
19c8c4ec
     int chunked_encoding;
     int chunk_size;               /* 0 if it needs to be read */
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 */
ae628ec1
     AVIOContext *pb;
2effd274
     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) */
50f2dfad
     AVDictionary *in_opts;   /* input parameters */
e240a0bb
     AVInputFormat *ifmt;       /* if non NULL, force input format */
bd7cf6ad
     AVOutputFormat *fmt;
8256c0a3
     IPAddressACL *acl;
58f48adb
     char dynamic_acl[1024];
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 */
861ec13a
     int truncate;        /* True if feeder connection truncate the feed 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
 
6675a5bf
 static const char *config_filename = "/etc/ffserver.conf";
 
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
 
042819c5
 static AVLFG random_state;
1df93ae9
 
85f07f22
 static FILE *logfile = NULL;
 
22f73dcc
 /* FIXME: make ffserver work with IPv6 */
db29da9a
 void av_noreturn exit_program(int ret)
dad09ff9
 {
     exit(ret);
 }
 
22f73dcc
 /* resolve host with also IP address parsing */
 static int resolve_host(struct in_addr *sin_addr, const char *hostname)
 {
 
     if (!ff_inet_aton(hostname, sin_addr)) {
 #if HAVE_GETADDRINFO
         struct addrinfo *ai, *cur;
a92be9b8
         struct addrinfo hints = { 0 };
22f73dcc
         hints.ai_family = AF_INET;
         if (getaddrinfo(hostname, NULL, &hints, &ai))
             return -1;
         /* getaddrinfo returns a linked list of addrinfo structs.
          * Even if we set ai_family = AF_INET above, make sure
          * that the returned one actually is of the correct type. */
         for (cur = ai; cur; cur = cur->ai_next) {
             if (cur->ai_family == AF_INET) {
                 *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr;
                 freeaddrinfo(ai);
                 return 0;
             }
         }
         freeaddrinfo(ai);
         return -1;
 #else
         struct hostent *hp;
         hp = gethostbyname(hostname);
         if (!hp)
             return -1;
         memcpy(sin_addr, hp->h_addr_list[0], sizeof(struct in_addr));
 #endif
     }
     return 0;
 }
 
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
 }
 
efa6ce99
 #ifdef __GNUC__
 __attribute__ ((format (printf, 1, 2)))
 #endif
 static void http_log(const char *fmt, ...)
bcd3ce59
 {
     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;
49ceb58b
     if (level > av_log_get_level())
bcd3ce59
         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");
 
da9cea77
                 http_log("Launch command line: ");
8bf61f5b
                 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 */
fc5a905a
                 if (chdir(my_program_dir) < 0) {
                     http_log("chdir failed\n");
                     exit(1);
                 }
d6562d2c
 
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));
 
f5e717f3
     my_addr->sin_family = AF_INET;
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",
042819c5
                      av_lfg_get(&random_state), av_lfg_get(&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);
28c4741a
             if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
                 ff_neterrno() != AVERROR(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;
     }
 }
 
0bdd8b85
 static void http_send_too_busy_reply(int fd)
 {
     char buffer[300];
     int len = snprintf(buffer, sizeof(buffer),
2a22187f
                        "HTTP/1.0 503 Server too busy\r\n"
0bdd8b85
                        "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"
                        "<p>The number of current connections is %d, and this exceeds the limit of %d.</p>\r\n"
                        "</body></html>\r\n",
                        nb_connections, nb_max_connections);
     send(fd, buffer, len, 0);
 }
 
 
2effd274
 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
 
0bdd8b85
     if (nb_connections >= nb_max_connections) {
         http_send_too_busy_reply(fd);
2effd274
         goto fail;
0bdd8b85
     }
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
         }
cd3716b9
         avformat_close_input(&c->fmt_in);
2effd274
     }
 
     /* 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);
d2d67e42
             av_dict_free(&ctx->metadata);
ea4f8aab
             av_free(ctx->streams[0]);
2effd274
             av_free(ctx);
         }
         h = c->rtp_handles[i];
611c5741
         if (h)
b263bf66
             ffurl_close(h);
2effd274
     }
115329f1
 
b88ba823
     ctx = &c->fmt_ctx;
 
637b638e
     if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
87638494
         if (ctx->oformat) {
             /* prepare header */
b92c5452
             if (avio_open_dyn_buf(&ctx->pb) >= 0) {
87638494
                 av_write_trailer(ctx);
f8b06be9
                 av_freep(&c->pb_buffer);
6dc7d80d
                 avio_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) {
28c4741a
             if (ff_neterrno() != AVERROR(EAGAIN) &&
                 ff_neterrno() != AVERROR(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) {
28c4741a
             if (ff_neterrno() != AVERROR(EAGAIN) &&
                 ff_neterrno() != AVERROR(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) {
28c4741a
             if (ff_neterrno() != AVERROR(EAGAIN) &&
                 ff_neterrno() != AVERROR(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) {
28c4741a
             if (ff_neterrno() != AVERROR(EAGAIN) &&
                 ff_neterrno() != AVERROR(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'; ) {
bb3244de
         if (av_strncasecmp(p, "Pragma:", 7) == 0) {
3120d2a2
             const char *q = p + 7;
 
             while (*q && *q != '\n' && isspace(*q))
                 q++;
 
bb3244de
             if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
3120d2a2
                 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
 
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;
 }
 
c64c0a9b
 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;
 }
 
58f48adb
 static void parse_acl_row(FFStream *stream, FFStream* feed, IPAddressACL *ext_acl,
                          const char *p, const char *filename, int line_num)
 {
     char arg[1024];
     IPAddressACL acl;
     int errors = 0;
 
     get_arg(arg, sizeof(arg), &p);
bb3244de
     if (av_strcasecmp(arg, "allow") == 0)
58f48adb
         acl.action = IP_ALLOW;
bb3244de
     else if (av_strcasecmp(arg, "deny") == 0)
58f48adb
         acl.action = IP_DENY;
     else {
         fprintf(stderr, "%s:%d: ACL action '%s' is not ALLOW or DENY\n",
                 filename, line_num, arg);
         errors++;
     }
 
     get_arg(arg, sizeof(arg), &p);
 
     if (resolve_host(&acl.first, arg) != 0) {
         fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
                 filename, line_num, arg);
         errors++;
     } else
         acl.last = acl.first;
 
     get_arg(arg, sizeof(arg), &p);
 
     if (arg[0]) {
         if (resolve_host(&acl.last, arg) != 0) {
             fprintf(stderr, "%s:%d: ACL refers to invalid host or ip address '%s'\n",
                     filename, line_num, arg);
             errors++;
         }
     }
 
     if (!errors) {
         IPAddressACL *nacl = av_mallocz(sizeof(*nacl));
         IPAddressACL **naclp = 0;
 
         acl.next = 0;
         *nacl = acl;
 
         if (stream)
             naclp = &stream->acl;
         else if (feed)
             naclp = &feed->acl;
         else if (ext_acl)
             naclp = &ext_acl;
         else {
             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;
         }
     }
 }
 
 
 static IPAddressACL* parse_dynamic_acl(FFStream *stream, HTTPContext *c)
 {
     FILE* f;
     char line[1024];
     char  cmd[1024];
     IPAddressACL *acl = NULL;
     int line_num = 0;
     const char *p;
 
     f = fopen(stream->dynamic_acl, "r");
     if (!f) {
         perror(stream->dynamic_acl);
         return NULL;
     }
 
     acl = av_mallocz(sizeof(IPAddressACL));
 
     /* Build ACL */
     for(;;) {
         if (fgets(line, sizeof(line), f) == NULL)
             break;
         line_num++;
         p = line;
         while (isspace(*p))
             p++;
         if (*p == '\0' || *p == '#')
             continue;
         get_arg(cmd, sizeof(cmd), &p);
 
bb3244de
         if (!av_strcasecmp(cmd, "ACL"))
58f48adb
             parse_acl_row(NULL, NULL, acl, p, stream->dynamic_acl, line_num);
     }
     fclose(f);
     return acl;
 }
 
 
 static void free_acl_list(IPAddressACL *in_acl)
 {
     IPAddressACL *pacl,*pacl2;
 
     pacl = in_acl;
     while(pacl) {
         pacl2 = pacl;
         pacl = pacl->next;
         av_freep(pacl2);
     }
 }
 
 static int validate_acl_list(IPAddressACL *in_acl, HTTPContext *c)
8256c0a3
 {
     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
 
58f48adb
     for (acl = in_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;
 }
 
58f48adb
 static int validate_acl(FFStream *stream, HTTPContext *c)
 {
     int ret = 0;
     IPAddressACL *acl;
 
 
     /* if stream->acl is null validate_acl_list will return 1 */
     ret = validate_acl_list(stream->acl, c);
 
     if (stream->dynamic_acl[0]) {
         acl = parse_dynamic_acl(stream, c);
 
         ret = validate_acl_list(acl, c);
 
         free_acl_list(acl);
     }
 
     return ret;
 }
 
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)
77553ae3
         http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), 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'; ) {
bb3244de
         if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
cde25790
             useragent = p + 11;
             if (*useragent && *useragent != '\n' && isspace(*useragent))
                 useragent++;
             break;
         }
         p = strchr(p, '\n');
         if (!p)
             break;
 
         p++;
     }
 
829ac53d
     redir_type = REDIR_NONE;
aa13b573
     if (av_match_ext(filename, "asx")) {
829ac53d
         redir_type = REDIR_ASX;
7434ba6d
         filename[strlen(filename)-1] = 'f';
aa13b573
     } else if (av_match_ext(filename, "asf") &&
bb3244de
         (!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
cde25790
         /* if this isn't WMP or lookalike, return the redirector file */
829ac53d
         redir_type = REDIR_ASF;
aa13b573
     } else if (av_match_ext(filename, "rpm,ram")) {
829ac53d
         redir_type = REDIR_RAM;
42a63c6a
         strcpy(filename + strlen(filename)-2, "m");
aa13b573
     } else if (av_match_ext(filename, "rtsp")) {
829ac53d
         redir_type = REDIR_RTSP;
bae79c04
         compute_real_filename(filename, sizeof(filename) - 1);
aa13b573
     } else if (av_match_ext(filename, "sdp")) {
829ac53d
         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);
77553ae3
         http_log("File '%s' not found\n", 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)
305ca590
                     c->switch_feed_streams[i] = -1;
cde25790
             }
         }
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.");
77553ae3
         http_log("Feed '%s' already being received\n", stream->feed_filename);
d0a5513b
         goto send_error;
     }
 
edfdd798
     if (c->post == 0 && max_bandwidth < current_bandwidth) {
0aee2a57
         c->http_error = 503;
42a63c6a
         q = c->buffer;
a3aa4fed
         q += snprintf(q, c->buffer_size,
0aee2a57
                       "HTTP/1.0 503 Server too busy\r\n"
a3aa4fed
                       "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'; ) {
bb3244de
             if (av_strncasecmp(p, "Host:", 5) == 0) {
7434ba6d
                 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'; ) {
bb3244de
                 if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
7434ba6d
                     logline = p;
                     break;
                 }
bb3244de
                 if (av_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
 
f190f676
 #ifdef DEBUG
cde25790
             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;
     }
 
f190f676
 #ifdef DEBUG
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 */
 
042819c5
         c->wmp_client_id = av_lfg_get(&random_state);
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"
5e567aae
                   "<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;
 }
 
ae628ec1
 static void fmt_bytecount(AVIOContext *pb, int64_t count)
2ac887ba
 {
b0f29db5
     static const char suffix[] = " kMGTP";
2ac887ba
     const char *s;
 
611c5741
     for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
2ac887ba
 
d9d86e00
     avio_printf(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;
ae628ec1
     AVIOContext *pb;
cde25790
 
b92c5452
     if (avio_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
 
d9d86e00
     avio_printf(pb, "HTTP/1.0 200 OK\r\n");
     avio_printf(pb, "Content-type: %s\r\n", "text/html");
     avio_printf(pb, "Pragma: no-cache\r\n");
     avio_printf(pb, "\r\n");
115329f1
 
d9d86e00
     avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
0679719d
     if (c->stream->feed_filename[0])
d9d86e00
         avio_printf(pb, "<link rel=\"shortcut icon\" href=\"%s\">\n", c->stream->feed_filename);
     avio_printf(pb, "</head>\n<body>");
     avio_printf(pb, "<h1>%s Status</h1>\n", program_name);
85f07f22
     /* format status */
d9d86e00
     avio_printf(pb, "<h2>Available Streams</h2>\n");
     avio_printf(pb, "<table cellspacing=0 cellpadding=4>\n");
     avio_printf(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
 
d9d86e00
             avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
a6e14edd
                          sfilename, stream->filename);
d9d86e00
             avio_printf(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) {
72415b2a
                         case AVMEDIA_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;
72415b2a
                         case AVMEDIA_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;
72415b2a
                         case AVMEDIA_TYPE_DATA:
01f4895c
                             video_bit_rate += st->codec->bit_rate;
e240a0bb
                             break;
a6e14edd
                         default:
0f4e8165
                             abort();
79c4ea3c
                         }
85f07f22
                     }
d9d86e00
                     avio_printf(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)
d9d86e00
                         avio_printf(pb, "<td>%s", stream->feed->filename);
611c5741
                     else
d9d86e00
                         avio_printf(pb, "<td>%s", stream->feed_filename);
                     avio_printf(pb, "\n");
85f07f22
                 }
a6e14edd
                 break;
             default:
d9d86e00
                 avio_printf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
a6e14edd
                 break;
85f07f22
             }
         }
         stream = stream->next;
     }
d9d86e00
     avio_printf(pb, "</table>\n");
a6e14edd
 
     stream = first_stream;
     while (stream != NULL) {
         if (stream->feed == stream) {
d9d86e00
             avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
cde25790
             if (stream->pid) {
d9d86e00
                 avio_printf(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) {
d9d86e00
                             avio_printf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
2effd274
                                          cpuperc, cpuused);
                         }
                         fclose(pid_stat);
cde25790
                     }
                 }
 #endif
 
d9d86e00
                 avio_printf(pb, "<p>");
cde25790
             }
d9d86e00
             avio_printf(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) {
72415b2a
                 case AVMEDIA_TYPE_AUDIO:
a6e14edd
                     type = "audio";
acdc8520
                     snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
a6e14edd
                     break;
72415b2a
                 case AVMEDIA_TYPE_VIDEO:
a6e14edd
                     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
                 }
d9d86e00
                 avio_printf(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
             }
d9d86e00
             avio_printf(pb, "</table>\n");
a6e14edd
 
115329f1
         }
a6e14edd
         stream = stream->next;
     }
115329f1
 
85f07f22
     /* connection status */
d9d86e00
     avio_printf(pb, "<h2>Connection Status</h2>\n");
85f07f22
 
d9d86e00
     avio_printf(pb, "Number of connections: %d / %d<br>\n",
85f07f22
                  nb_connections, nb_max_connections);
 
d9d86e00
     avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
6edd6884
                  current_bandwidth, max_bandwidth);
42a63c6a
 
d9d86e00
     avio_printf(pb, "<table>\n");
     avio_printf(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);
d9d86e00
         avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
115329f1
                     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);
d9d86e00
         avio_printf(pb, "<td align=right>");
2effd274
         fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
d9d86e00
         avio_printf(pb, "<td align=right>");
2effd274
         fmt_bytecount(pb, c1->data_count);
d9d86e00
         avio_printf(pb, "\n");
85f07f22
         c1 = c1->next;
     }
d9d86e00
     avio_printf(pb, "</table>\n");
115329f1
 
85f07f22
     /* date */
     ti = time(NULL);
     p = ctime(&ti);
d9d86e00
     avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
     avio_printf(pb, "</body>\n</html>\n");
85f07f22
 
6dc7d80d
     len = avio_close_dyn_buf(pb, &c->pb_buffer);
2effd274
     c->buffer_ptr = c->pb_buffer;
     c->buffer_end = c->pb_buffer + len;
85f07f22
 }
 
 static int open_input_stream(HTTPContext *c, const char *info)
 {
     char buf[128];
     char input_filename[1024];
50f2dfad
     AVFormatContext *s = NULL;
9fcc62ed
     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);
9fcc62ed
         buf_size = FFM_PACKET_SIZE;
85f07f22
         /* compute position (absolute time) */
ab0287fc
         if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
9fcae973
             if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0)
                 return ret;
ab0287fc
         } else if (av_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);
9fcc62ed
         buf_size = 0;
85f07f22
         /* compute position (relative time) */
ab0287fc
         if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
9fcae973
             if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0)
                 return ret;
ace21da3
         } else
85f07f22
             stream_pos = 0;
     }
     if (input_filename[0] == '\0')
         return -1;
 
     /* open stream */
50f2dfad
     if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
c351cc7f
         http_log("could not open %s: %d\n", input_filename, ret);
85f07f22
         return -1;
2effd274
     }
00969376
 
     /* set buffer size */
     if (buf_size > 0) ffio_set_buf_size(s->pb, buf_size);
 
9dc0bc3d
     s->flags |= AVFMT_FLAG_GENPTS;
85f07f22
     c->fmt_in = s;
c960e67a
     if (strcmp(s->iformat->name, "ffm") && avformat_find_stream_info(c->fmt_in, NULL) < 0) {
20f93c3c
         http_log("Could not find stream info '%s'\n", input_filename);
cd3716b9
         avformat_close_input(&s);
20f93c3c
         return -1;
     }
115329f1
 
2effd274
     /* 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 &&
72415b2a
             c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
2effd274
             c->pts_stream_index = i;
         }
     }
85f07f22
 
611c5741
     if (c->fmt_in->iformat->read_seek)
60a04f7f
         av_seek_frame(c->fmt_in, -1, stream_pos, 0);
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));
d2d67e42
         av_dict_set(&c->fmt_ctx.metadata, "author"   , c->stream->author   , 0);
         av_dict_set(&c->fmt_ctx.metadata, "comment"  , c->stream->comment  , 0);
         av_dict_set(&c->fmt_ctx.metadata, "copyright", c->stream->copyright, 0);
         av_dict_set(&c->fmt_ctx.metadata, "title"    , c->stream->title    , 0);
2effd274
 
db3262b7
         c->fmt_ctx.streams = av_mallocz(sizeof(AVStream *) * c->stream->nb_streams);
 
3d9cc27d
         for(i=0;i<c->stream->nb_streams;i++) {
bb270c08
             AVStream *src;
db3262b7
             c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
2effd274
             /* 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]];
 
db3262b7
             *(c->fmt_ctx.streams[i]) = *src;
             c->fmt_ctx.streams[i]->priv_data = 0;
             c->fmt_ctx.streams[i]->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 */
b92c5452
         if (avio_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
2effd274
             /* XXX: potential leak */
             return -1;
85f07f22
         }
8978feda
         c->fmt_ctx.pb->seekable = 0;
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.max_delay = (int)(0.7*AV_TIME_BASE);
 
50f2dfad
         if (avformat_write_header(&c->fmt_ctx, NULL) < 0) {
929a9b75
             http_log("Error writing output header\n");
f75cdda7
             return -1;
929a9b75
         }
d2d67e42
         av_dict_free(&c->fmt_ctx.metadata);
2effd274
 
6dc7d80d
         len = avio_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:
d9aac267
             ret = av_read_frame(c->fmt_in, &pkt);
             if (ret < 0) {
                 if (c->stream->feed) {
3b371676
                     /* 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 */
d9aac267
                 } else if (ret == AVERROR(EAGAIN)) {
                     /* input not ready, come back later */
                     return 0;
2effd274
                 } else {
3b371676
                     if (c->stream->loop) {
cd3716b9
                         avformat_close_input(&c->fmt_in);
3b371676
                         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)
cc947f04
                                 if (pkt.flags & AV_PKT_FLAG_KEY)
305ca590
                                     c->switch_feed_streams[i] = -1;
3b371676
                             if (c->switch_feed_streams[i] >= 0)
                                 c->switch_pending = 1;
cde25790
                         }
3b371676
                     }
                     for(i=0;i<c->stream->nb_streams;i++) {
a5ba4ced
                         if (c->stream->feed_streams[i] == pkt.stream_index) {
78728064
                             AVStream *st = c->fmt_in->streams[source_index];
3b371676
                             pkt.stream_index = i;
cc947f04
                             if (pkt.flags & AV_PKT_FLAG_KEY &&
72415b2a
                                 (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
180b7026
                                  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);
f475f35f
                         c->cur_pts -= c->first_pts;
8f56ccca
                         c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
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
b263bf66
                             max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size;
403ee835
                         ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size);
3b371676
                     } else {
b92c5452
                         ret = avio_open_dyn_buf(&ctx->pb);
3b371676
                     }
                     if (ret < 0) {
                         /* XXX: potential leak */
                         return -1;
                     }
3ab29d8e
                     ost = ctx->streams[pkt.stream_index];
 
8978feda
                     ctx->pb->seekable = 0;
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
 
6dc7d80d
                     len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
3b371676
                     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 */
b92c5452
         if (avio_open_dyn_buf(&ctx->pb) < 0) {
2effd274
             /* XXX: potential leak */
             return -1;
         }
8978feda
         c->fmt_ctx.pb->seekable = 0;
2effd274
         av_write_trailer(ctx);
6dc7d80d
         len = avio_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 */
ae628ec1
                     AVIOContext *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;
b92c5452
                     if (avio_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;
77eb5504
                     avio_write(pb, header, 4);
bc351386
                     /* write RTP packet data */
                     c->buffer_ptr += 4;
77eb5504
                     avio_write(pb, c->buffer_ptr, len);
6dc7d80d
                     size = avio_close_dyn_buf(pb, &c->packet_buffer);
bc351386
                     /* 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;
b263bf66
                     ffurl_write(c->rtp_handles[c->packet_stream_index],
                                 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) {
28c4741a
                     if (ff_neterrno() != AVERROR(EAGAIN) &&
                         ff_neterrno() != AVERROR(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
 
861ec13a
     if (c->stream->truncate) {
         /* truncate feed file */
         ffm_write_write_index(c->feed_fd, FFM_PACKET_SIZE);
         http_log("Truncating feed file '%s'\n", c->stream->feed_filename);
0de1319e
         if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
             http_log("Error truncating feed file: %s\n", strerror(errno));
             return -1;
         }
861ec13a
     } else {
1f611549
         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;
         }
861ec13a
     }
 
7e24aa0c
     c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
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;
fd7bec5e
     c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
85f07f22
     return 0;
 }
115329f1
 
85f07f22
 static int http_receive_data(HTTPContext *c)
 {
     HTTPContext *c1;
19c8c4ec
     int len, loop_run = 0;
85f07f22
 
19c8c4ec
     while (c->chunked_encoding && !c->chunk_size &&
            c->buffer_end > c->buffer_ptr) {
         /* read chunk header, if present */
         len = recv(c->fd, c->buffer_ptr, 1, 0);
 
         if (len < 0) {
28c4741a
             if (ff_neterrno() != AVERROR(EAGAIN) &&
                 ff_neterrno() != AVERROR(EINTR))
19c8c4ec
                 /* error : close connection */
                 goto fail;
686d6f40
             return 0;
19c8c4ec
         } else if (len == 0) {
             /* end of connection : close it */
             goto fail;
         } else if (c->buffer_ptr - c->buffer >= 2 &&
                    !memcmp(c->buffer_ptr - 1, "\r\n", 2)) {
             c->chunk_size = strtol(c->buffer, 0, 16);
             if (c->chunk_size == 0) // end of stream
                 goto fail;
             c->buffer_ptr = c->buffer;
             break;
         } else if (++loop_run > 10) {
             /* no chunk header, abort */
             goto fail;
         } else {
             c->buffer_ptr++;
         }
     }
a6e14edd
 
19c8c4ec
     if (c->buffer_end > c->buffer_ptr) {
         len = recv(c->fd, c->buffer_ptr,
                    FFMIN(c->chunk_size, c->buffer_end - c->buffer_ptr), 0);
a6e14edd
         if (len < 0) {
28c4741a
             if (ff_neterrno() != AVERROR(EAGAIN) &&
                 ff_neterrno() != AVERROR(EINTR))
a6e14edd
                 /* error : close connection */
                 goto fail;
611c5741
         } else if (len == 0)
a6e14edd
             /* end of connection : close it */
             goto fail;
611c5741
         else {
19c8c4ec
             c->chunk_size -= len;
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 */
50f2dfad
             AVFormatContext *s = avformat_alloc_context();
ae628ec1
             AVIOContext *pb;
bd7cf6ad
             AVInputFormat *fmt_in;
f747e6d3
             int i;
 
50f2dfad
             if (!s)
                 goto fail;
 
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;
 
83fddaeb
             pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
                                     0, NULL, NULL, NULL, NULL);
8978feda
             pb->seekable = 0;
697efa36
 
50f2dfad
             s->pb = pb;
             if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
e6f0deab
                 av_free(pb);
                 goto fail;
             }
f747e6d3
 
             /* Now we have the actual streams */
f2972c8c
             if (s->nb_streams != feed->nb_streams) {
cd3716b9
                 avformat_close_input(&s);
86771c68
                 av_free(pb);
77553ae3
                 http_log("Feed '%s' stream number does not match registered feed\n",
                          c->stream->feed_filename);
f747e6d3
                 goto fail;
             }
f2972c8c
 
cb51aef1
             for (i = 0; i < s->nb_streams; i++) {
                 AVStream *fst = feed->streams[i];
                 AVStream *st = s->streams[i];
5634f30c
                 avcodec_copy_context(fst->codec, st->codec);
cb51aef1
             }
f2972c8c
 
cd3716b9
             avformat_close_input(&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;
0156fcf2
     struct tm *tm;
2effd274
     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
 
d9d86e00
     avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
     avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
2effd274
 
     /* output GMT time */
     ti = time(NULL);
0156fcf2
     tm = gmtime(&ti);
     strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
d9d86e00
     avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
2effd274
 }
 
 static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
 {
     rtsp_reply_header(c, error_number);
d9d86e00
     avio_printf(c->pb, "\r\n");
2effd274
 }
 
 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;
a92be9b8
     RTSPMessageHeader header1 = { 0 }, *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
 
b92c5452
     if (avio_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 */
     /* skip to next line */
     while (*p != '\n' && *p != '\0')
         p++;
     if (*p == '\n')
         p++;
     while (*p != '\0') {
c966c912
         p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
2effd274
         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';
77223c53
         ff_rtsp_parse_line(header, line, NULL, NULL);
2effd274
         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:
6dc7d80d
     len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
2effd274
     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;
9389b925
     AVStream *avs = NULL;
dd723472
     int i;
115329f1
 
8e2fd8e1
     avc =  avformat_alloc_context();
dd723472
     if (avc == NULL) {
2effd274
         return -1;
dd723472
     }
d2d67e42
     av_dict_set(&avc->metadata, "title",
                stream->title[0] ? stream->title : "No Title", 0);
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);
43d09faf
     } else {
         snprintf(avc->filename, 1024, "rtp://0.0.0.0");
dd723472
     }
115329f1
 
9389b925
     if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
         !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
         goto sdp_done;
     if (avc->nb_streams >= INT_MAX/sizeof(*avs) ||
         !(avs = av_malloc(avc->nb_streams * sizeof(*avs))))
         goto sdp_done;
 
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);
c3675dfe
     av_sdp_create(&avc, 1, *pbuffer, 2048);
9389b925
 
  sdp_done:
     av_free(avc->streams);
d2d67e42
     av_dict_free(&avc->metadata);
dd723472
     av_free(avc);
9389b925
     av_free(avs);
dd723472
 
     return strlen(*pbuffer);
2effd274
 }
 
0df65975
 static void rtsp_cmd_options(HTTPContext *c, const char *url)
 {
 //    rtsp_reply_header(c, RTSP_STATUS_OK);
d9d86e00
     avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", RTSP_STATUS_OK, "OK");
     avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
     avio_printf(c->pb, "Public: %s\r\n", "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE");
     avio_printf(c->pb, "\r\n");
0df65975
 }
 
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 */
f3bfe388
     av_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);
d9d86e00
     avio_printf(c->pb, "Content-Base: %s/\r\n", url);
     avio_printf(c->pb, "Content-Type: application/sdp\r\n");
     avio_printf(c->pb, "Content-Length: %d\r\n", content_length);
     avio_printf(c->pb, "\r\n");
77eb5504
     avio_write(c->pb, content, content_length);
ea4f8aab
     av_free(content);
2effd274
 }
 
 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;
bacde646
     int stream_index, rtp_port, rtcp_port;
2effd274
     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 */
f3bfe388
     av_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",
042819c5
                  av_lfg_get(&random_state), av_lfg_get(&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 */
d9d86e00
     avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
2effd274
 
     switch(rtp_c->rtp_protocol) {
90abbdba
     case RTSP_LOWER_TRANSPORT_UDP:
bfc6db44
         rtp_port = ff_rtp_get_local_rtp_port(rtp_c->rtp_handles[stream_index]);
         rtcp_port = ff_rtp_get_local_rtcp_port(rtp_c->rtp_handles[stream_index]);
d9d86e00
         avio_printf(c->pb, "Transport: RTP/AVP/UDP;unicast;"
2effd274
                     "client_port=%d-%d;server_port=%d-%d",
bacde646
                     th->client_port_min, th->client_port_max,
                     rtp_port, rtcp_port);
2effd274
         break;
90abbdba
     case RTSP_LOWER_TRANSPORT_TCP:
d9d86e00
         avio_printf(c->pb, "Transport: RTP/AVP/TCP;interleaved=%d-%d",
2effd274
                     stream_index * 2, stream_index * 2 + 1);
         break;
     default:
         break;
     }
611c5741
     if (setup.transport_option[0] != '\0')
d9d86e00
         avio_printf(c->pb, ";%s", setup.transport_option);
     avio_printf(c->pb, "\r\n");
115329f1
 
2effd274
 
d9d86e00
     avio_printf(c->pb, "\r\n");
2effd274
 }
 
 
 /* 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];
c2ca851b
     int s, len;
2effd274
 
     rtp_c = find_rtp_session(session_id);
     if (!rtp_c)
         return NULL;
 
     /* find which url is asked */
f3bfe388
     av_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;
       }
     }
c2ca851b
     len = strlen(path);
     if (len > 0 && path[len - 1] == '/' &&
         !strncmp(path, rtp_c->stream->filename, len - 1))
         return rtp_c;
94d9ad5f
     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;
     }
 
     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 */
d9d86e00
     avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
     avio_printf(c->pb, "\r\n");
2effd274
 }
 
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 */
d9d86e00
     avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
     avio_printf(c->pb, "\r\n");
2effd274
 }
 
a9e534d5
 static void rtsp_cmd_teardown(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
     /* now everything is OK, so we can send the connection parameters */
     rtsp_reply_header(c, RTSP_STATUS_OK);
     /* session ID */
e2d7dc87
     avio_printf(c->pb, "Session: %s\r\n", rtp_c->session_id);
d9d86e00
     avio_printf(c->pb, "\r\n");
e2d7dc87
 
     /* abort the session */
     close_connection(rtp_c);
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)
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;
0f52ef1a
     ctx->oformat = av_guess_format("rtp", NULL, NULL);
2effd274
 
     st = av_mallocz(sizeof(AVStream));
     if (!st)
         goto fail;
     ctx->nb_streams = 1;
db3262b7
     ctx->streams = av_mallocz(sizeof(AVStream *) * ctx->nb_streams);
840238b8
     if (!ctx->streams)
db3262b7
       goto fail;
2effd274
     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
 
b263bf66
         if (ffurl_open(&h, ctx->filename, AVIO_FLAG_WRITE, NULL, NULL) < 0)
2effd274
             goto fail;
         c->rtp_handles[stream_index] = h;
b263bf66
         max_packet_size = h->max_packet_size;
bc351386
         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 */
403ee835
     if (ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size) < 0) {
2effd274
         /* XXX: close stream */
         goto fail;
     }
50f2dfad
     if (avformat_write_header(ctx, NULL) < 0) {
2effd274
     fail:
         if (h)
b263bf66
             ffurl_close(h);
2effd274
         av_free(ctx);
         return -1;
     }
6dc7d80d
     avio_close_dyn_buf(ctx->pb, &dummy_buf);
2effd274
     av_free(dummy_buf);
115329f1
 
2effd274
     c->rtp_ctx[stream_index] = ctx;
     return 0;
 }
 
 /********************************************************************/
 /* ffserver initialization */
 
e175b55e
 static AVStream *add_av_stream1(FFStream *stream, AVCodecContext *codec, int copy)
2effd274
 {
     AVStream *fst;
 
     fst = av_mallocz(sizeof(AVStream));
     if (!fst)
         return NULL;
e175b55e
     if (copy) {
71a861cf
         fst->codec = avcodec_alloc_context3(NULL);
e175b55e
         memcpy(fst->codec, codec, sizeof(AVCodecContext));
         if (codec->extradata_size) {
             fst->codec->extradata = av_malloc(codec->extradata_size);
             memcpy(fst->codec->extradata, codec->extradata,
                 codec->extradata_size);
         }
     } else {
         /* live streams must use the actual feed's codec since it may be
          * updated later to carry extradata needed by the streams.
          */
         fst->codec = codec;
     }
2effd274
     fst->priv_data = av_mallocz(sizeof(FeedData));
d445a7e9
     fst->index = stream->nb_streams;
b263bf66
     avpriv_set_pts_info(fst, 33, 1, 90000);
6741f7c9
     fst->sample_aspect_ratio = codec->sample_aspect_ratio;
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) {
72415b2a
             case AVMEDIA_TYPE_AUDIO:
85f07f22
                 if (av1->channels == av->channels &&
                     av1->sample_rate == av->sample_rate)
71a1d111
                     return i;
85f07f22
                 break;
72415b2a
             case AVMEDIA_TYPE_VIDEO:
85f07f22
                 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)
71a1d111
                     return i;
85f07f22
                 break;
f747e6d3
             default:
0f4e8165
                 abort();
85f07f22
             }
         }
     }
115329f1
 
e175b55e
     fst = add_av_stream1(feed, av, 0);
85f07f22
     if (!fst)
         return -1;
     return feed->nb_streams - 1;
 }
 
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;
f61d45c9
     int i, ret;
2effd274
 
     /* gather all streams */
     for(stream = first_stream; stream != NULL; stream = stream_next) {
50f2dfad
         AVFormatContext *infile = NULL;
2effd274
         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 */
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 */
50f2dfad
                 av_dict_set(&stream->in_opts, "mpeg2ts_compute_pcr", "1", 0);
e240a0bb
             }
115329f1
 
77553ae3
             http_log("Opening file '%s'\n", stream->feed_filename);
50f2dfad
             if ((ret = avformat_open_input(&infile, stream->feed_filename, stream->ifmt, &stream->in_opts)) < 0) {
77553ae3
                 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' */
c960e67a
                 if (avformat_find_stream_info(infile, NULL) < 0) {
2dae1dd0
                     http_log("Could not find codec parameters from '%s'\n",
2effd274
                              stream->feed_filename);
cd3716b9
                     avformat_close_input(&infile);
2effd274
                     goto fail;
                 }
0fa45e19
                 extract_mpeg4_header(infile);
 
611c5741
                 for(i=0;i<infile->nb_streams;i++)
e175b55e
                     add_av_stream1(stream, infile->streams[i]->codec, 1);
611c5741
 
cd3716b9
                 avformat_close_input(&infile);
2effd274
             }
         }
     }
 }
 
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) {
cde25790
             if (stream->is_feed) {
611c5741
                 for(i=0;i<stream->nb_streams;i++)
85f07f22
                     stream->feed_streams[i] = i;
863e2046
             } else {
                 /* we handle a stream coming from a feed */
                 for(i=0;i<stream->nb_streams;i++)
                     stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
85f07f22
             }
         }
     }
 
     /* create feed files if needed */
     for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
         int fd;
 
55815edc
         if (avio_check(feed->feed_filename, AVIO_FLAG_READ) > 0) {
59eb2ed1
             /* See if it matches */
50f2dfad
             AVFormatContext *s = NULL;
59eb2ed1
             int matches = 0;
 
50f2dfad
             if (avformat_open_input(&s, feed->feed_filename, NULL, NULL) >= 0) {
00969376
                 /* set buffer size */
                 ffio_set_buf_size(s->pb, FFM_PACKET_SIZE);
59eb2ed1
                 /* 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)
 
111c9359
                             if (CHECK_CODEC(codec_id) || 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;
72415b2a
                             } else if (ccf->codec_type == AVMEDIA_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;
                                 }
72415b2a
                             } else if (ccf->codec_type == AVMEDIA_TYPE_AUDIO) {
59eb2ed1
                                 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);
 
cd3716b9
                 avformat_close_input(&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
         }
55815edc
         if (avio_check(feed->feed_filename, AVIO_FLAG_WRITE) <= 0) {
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 */
59d96941
             if (avio_open(&s->pb, feed->feed_filename, AVIO_FLAG_WRITE) < 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;
840238b8
             s->streams = feed->streams;
50f2dfad
             if (avformat_write_header(s, NULL) < 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);
22a3212e
             avio_close(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);
         }
 
7e24aa0c
         feed->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
85f07f22
         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) {
72415b2a
             case AVMEDIA_TYPE_AUDIO:
             case AVMEDIA_TYPE_VIDEO:
01f4895c
                 bandwidth += st->codec->bit_rate;
6edd6884
                 break;
             default:
                 break;
             }
         }
         stream->bandwidth = (bandwidth + 999) / 1000;
     }
 }
 
85f07f22
 /* 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) {
72415b2a
     case AVMEDIA_TYPE_AUDIO:
85f07f22
         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;
72415b2a
     case AVMEDIA_TYPE_VIDEO:
85f07f22
         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;
1bf5228e
         if (!av->me_method)
90c92100
             av->me_method = ME_EPZS;
291fe90a
         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;
71a861cf
     st->codec = avcodec_alloc_context3(NULL);
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
 
72415b2a
     if (p == NULL || p->type != AVMEDIA_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
 
72415b2a
     if (p == NULL || p->type != AVMEDIA_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;
dc59ec5e
     const AVOption *o = av_opt_find(avctx, opt, NULL, type, 0);
5c548937
     if(o)
3b3ea346
         ret = av_opt_set(avctx, opt, arg, 0);
f16dd7e6
     return ret;
7ab08864
 }
 
8190f62f
 static int ffserver_opt_preset(const char *arg,
                        AVCodecContext *avctx, int type,
                        enum CodecID *audio_id, enum CodecID *video_id)
 {
     FILE *f=NULL;
     char filename[1000], tmp[1000], tmp2[1000], line[1000];
6e872935
     int ret = 0;
     AVCodec *codec = avcodec_find_encoder(avctx->codec_id);
8190f62f
 
6e872935
     if (!(f = get_preset_file(filename, sizeof(filename), arg, 0,
                               codec ? codec->name : NULL))) {
8190f62f
         fprintf(stderr, "File for preset '%s' not found\n", arg);
         return 1;
     }
 
     while(!feof(f)){
         int e= fscanf(f, "%999[^\n]\n", line) - 1;
         if(line[0] == '#' && !e)
             continue;
         e|= sscanf(line, "%999[^=]=%999[^\n]\n", tmp, tmp2) - 2;
         if(e){
             fprintf(stderr, "%s: Invalid syntax: '%s'\n", filename, line);
             ret = 1;
             break;
         }
         if(!strcmp(tmp, "acodec")){
             *audio_id = opt_audio_codec(tmp2);
         }else if(!strcmp(tmp, "vcodec")){
             *video_id = opt_video_codec(tmp2);
         }else if(!strcmp(tmp, "scodec")){
             /* opt_subtitle_codec(tmp2); */
         }else if(ffserver_opt_default(tmp, tmp2, avctx, type) < 0){
             fprintf(stderr, "%s: Invalid option or argument: '%s', parsed as '%s' = '%s'\n", filename, line, tmp, tmp2);
             ret = 1;
             break;
         }
     }
 
     fclose(f);
 
     return ret;
 }
 
1642ee43
 static AVOutputFormat *ffserver_guess_format(const char *short_name, const char *filename,
                                              const char *mime_type)
 {
0f52ef1a
     AVOutputFormat *fmt = av_guess_format(short_name, filename, mime_type);
1642ee43
 
     if (fmt) {
         AVOutputFormat *stream_fmt;
         char stream_format_name[64];
 
         snprintf(stream_format_name, sizeof(stream_format_name), "%s_stream", fmt->name);
0f52ef1a
         stream_fmt = av_guess_format(stream_format_name, NULL, NULL);
1642ee43
 
         if (stream_fmt)
             fmt = stream_fmt;
     }
 
     return fmt;
 }
 
472e12f5
 static void report_config_error(const char *filename, int line_num, int *errors, const char *fmt, ...)
 {
     va_list vl;
     va_start(vl, fmt);
     fprintf(stderr, "%s:%d: ", filename, line_num);
     vfprintf(stderr, fmt, vl);
     va_end(vl);
 
     (*errors)++;
 }
 
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;
637af098
     FFStream **last_feed, *feed, *s;
85f07f22
     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;
472e12f5
 
 #define ERROR(...) report_config_error(filename, line_num, &errors, __VA_ARGS__)
85f07f22
     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
 
bb3244de
         if (!av_strcasecmp(cmd, "Port")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
aabce533
             val = atoi(arg);
             if (val < 1 || val > 65536) {
472e12f5
                 ERROR("Invalid_port: %s\n", arg);
aabce533
             }
             my_http_addr.sin_port = htons(val);
bb3244de
         } else if (!av_strcasecmp(cmd, "BindAddress")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
ad8b8abc
             if (resolve_host(&my_http_addr.sin_addr, arg) != 0) {
472e12f5
                 ERROR("%s:%d: Invalid host/IP address: %s\n", arg);
2effd274
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "NoDaemon")) {
2effd274
             ffserver_daemon = 0;
bb3244de
         } else if (!av_strcasecmp(cmd, "RTSPPort")) {
2effd274
             get_arg(arg, sizeof(arg), &p);
aabce533
             val = atoi(arg);
             if (val < 1 || val > 65536) {
472e12f5
                 ERROR("%s:%d: Invalid port: %s\n", arg);
aabce533
             }
             my_rtsp_addr.sin_port = htons(atoi(arg));
bb3244de
         } else if (!av_strcasecmp(cmd, "RTSPBindAddress")) {
2effd274
             get_arg(arg, sizeof(arg), &p);
ad8b8abc
             if (resolve_host(&my_rtsp_addr.sin_addr, arg) != 0) {
472e12f5
                 ERROR("Invalid host/IP address: %s\n", arg);
85f07f22
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "MaxHTTPConnections")) {
1c9ff179
             get_arg(arg, sizeof(arg), &p);
             val = atoi(arg);
             if (val < 1 || val > 65536) {
472e12f5
                 ERROR("Invalid MaxHTTPConnections: %s\n", arg);
1c9ff179
             }
             nb_max_http_connections = val;
bb3244de
         } else if (!av_strcasecmp(cmd, "MaxClients")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
             val = atoi(arg);
1c9ff179
             if (val < 1 || val > nb_max_http_connections) {
472e12f5
                 ERROR("Invalid MaxClients: %s\n", arg);
85f07f22
             } else {
                 nb_max_connections = val;
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "MaxBandwidth")) {
0dc17c21
             int64_t llval;
42a63c6a
             get_arg(arg, sizeof(arg), &p);
0dc17c21
             llval = atoll(arg);
             if (llval < 10 || llval > 10000000) {
472e12f5
                 ERROR("Invalid MaxBandwidth: %s\n", arg);
611c5741
             } else
0dc17c21
                 max_bandwidth = llval;
bb3244de
         } else if (!av_strcasecmp(cmd, "CustomLog")) {
4e8f77ab
             if (!ffserver_debug)
                 get_arg(logfilename, sizeof(logfilename), &p);
bb3244de
         } else if (!av_strcasecmp(cmd, "<Feed")) {
85f07f22
             /*********************************************/
             /* Feed related options */
             char *q;
             if (stream || feed) {
472e12f5
                 ERROR("Already in a tag\n");
85f07f22
             } else {
                 feed = av_mallocz(sizeof(FFStream));
                 get_arg(feed->filename, sizeof(feed->filename), &p);
                 q = strrchr(feed->filename, '>');
                 if (*q)
                     *q = '\0';
637af098
 
                 for (s = first_feed; s; s = s->next) {
                     if (!strcmp(feed->filename, s->filename)) {
472e12f5
                         ERROR("Feed '%s' already registered\n", s->filename);
637af098
                     }
                 }
 
0f52ef1a
                 feed->fmt = av_guess_format("ffm", NULL, NULL);
85f07f22
                 /* 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 :-) */
637af098
 
                 /* add in stream list */
                 *last_stream = feed;
                 last_stream = &feed->next;
                 /* add in feed list */
                 *last_feed = feed;
                 last_feed = &feed->next_feed;
85f07f22
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "Launch")) {
cde25790
             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
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "ReadOnlyFile")) {
e322ea48
             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);
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "File")) {
85f07f22
             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);
bb3244de
         } else if (!av_strcasecmp(cmd, "Truncate")) {
861ec13a
             if (feed) {
                 get_arg(arg, sizeof(arg), &p);
                 feed->truncate = strtod(arg, NULL);
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "FileMaxSize")) {
85f07f22
             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;
64159a58
                 if (feed->feed_max_size < FFM_PACKET_SIZE*4) {
472e12f5
                     ERROR("Feed max file size is too small, must be at least %d\n", FFM_PACKET_SIZE*4);
64159a58
                 }
85f07f22
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "</Feed>")) {
85f07f22
             if (!feed) {
472e12f5
                 ERROR("No corresponding <Feed> for </Feed>\n");
85f07f22
             }
             feed = NULL;
bb3244de
         } else if (!av_strcasecmp(cmd, "<Stream")) {
85f07f22
             /*********************************************/
             /* Stream related options */
             char *q;
             if (stream || feed) {
472e12f5
                 ERROR("Already in a tag\n");
85f07f22
             } else {
637af098
                 FFStream *s;
85f07f22
                 stream = av_mallocz(sizeof(FFStream));
                 get_arg(stream->filename, sizeof(stream->filename), &p);
                 q = strrchr(stream->filename, '>');
                 if (*q)
                     *q = '\0';
637af098
 
                 for (s = first_stream; s; s = s->next) {
                     if (!strcmp(stream->filename, s->filename)) {
472e12f5
                         ERROR("Stream '%s' already registered\n", s->filename);
637af098
                     }
                 }
 
1642ee43
                 stream->fmt = ffserver_guess_format(NULL, stream->filename, NULL);
0b717e24
                 avcodec_get_context_defaults3(&video_enc, NULL);
                 avcodec_get_context_defaults3(&audio_enc, NULL);
d0492578
 
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;
                 }
637af098
 
                 *last_stream = stream;
                 last_stream = &stream->next;
85f07f22
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "Feed")) {
85f07f22
             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)
472e12f5
                     ERROR("feed '%s' not defined\n", arg);
611c5741
                 else
85f07f22
                     stream->feed = sfeed;
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "Format")) {
85f07f22
             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");
1642ee43
                     stream->fmt = ffserver_guess_format(arg, NULL, NULL);
65f2f028
                     if (!stream->fmt) {
472e12f5
                         ERROR("Unknown Format: %s\n", arg);
65f2f028
                     }
                 }
                 if (stream->fmt) {
                     audio_id = stream->fmt->audio_codec;
                     video_id = stream->fmt->video_codec;
85f07f22
                 }
5735f6dc
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "InputFormat")) {
d2040a8f
             get_arg(arg, sizeof(arg), &p);
a69b06be
             if (stream) {
f9739144
                 stream->ifmt = av_find_input_format(arg);
                 if (!stream->ifmt) {
472e12f5
                     ERROR("Unknown input format: %s\n", arg);
f9739144
                 }
a69b06be
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "FaviconURL")) {
cde25790
             if (stream && stream->stream_type == STREAM_TYPE_STATUS) {
                 get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
             } else {
472e12f5
                 ERROR("FaviconURL only permitted for status streams\n");
cde25790
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "Author")) {
611c5741
             if (stream)
2ac887ba
                 get_arg(stream->author, sizeof(stream->author), &p);
bb3244de
         } else if (!av_strcasecmp(cmd, "Comment")) {
611c5741
             if (stream)
2ac887ba
                 get_arg(stream->comment, sizeof(stream->comment), &p);
bb3244de
         } else if (!av_strcasecmp(cmd, "Copyright")) {
611c5741
             if (stream)
2ac887ba
                 get_arg(stream->copyright, sizeof(stream->copyright), &p);
bb3244de
         } else if (!av_strcasecmp(cmd, "Title")) {
611c5741
             if (stream)
2ac887ba
                 get_arg(stream->title, sizeof(stream->title), &p);
bb3244de
         } else if (!av_strcasecmp(cmd, "Preroll")) {
42a63c6a
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
8256c0a3
                 stream->prebuffer = atof(arg) * 1000;
bb3244de
         } else if (!av_strcasecmp(cmd, "StartSendOnKey")) {
611c5741
             if (stream)
79c4ea3c
                 stream->send_on_key = 1;
bb3244de
         } else if (!av_strcasecmp(cmd, "AudioCodec")) {
f747e6d3
             get_arg(arg, sizeof(arg), &p);
             audio_id = opt_audio_codec(arg);
             if (audio_id == CODEC_ID_NONE) {
472e12f5
                 ERROR("Unknown AudioCodec: %s\n", arg);
f747e6d3
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoCodec")) {
f747e6d3
             get_arg(arg, sizeof(arg), &p);
             video_id = opt_video_codec(arg);
             if (video_id == CODEC_ID_NONE) {
472e12f5
                 ERROR("Unknown VideoCodec: %s\n", arg);
f747e6d3
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "MaxTime")) {
ec3b2232
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
8256c0a3
                 stream->max_time = atof(arg) * 1000;
bb3244de
         } else if (!av_strcasecmp(cmd, "AudioBitRate")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
3b963552
                 audio_enc.bit_rate = lrintf(atof(arg) * 1000);
bb3244de
         } else if (!av_strcasecmp(cmd, "AudioChannels")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
85f07f22
                 audio_enc.channels = atoi(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "AudioSampleRate")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
85f07f22
                 audio_enc.sample_rate = atoi(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "AudioQuality")) {
bb270c08
             get_arg(arg, sizeof(arg), &p);
81e0d0b4
             if (stream) {
1e491e29
 //                audio_enc.quality = atof(arg) * 1000;
81e0d0b4
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoBitRateRange")) {
d6562d2c
             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 {
472e12f5
                     ERROR("Incorrect format for VideoBitRateRange -- should be <min>-<max>: %s\n", arg);
d6562d2c
                 }
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "Debug")) {
291fe90a
             if (stream) {
                 get_arg(arg, sizeof(arg), &p);
                 video_enc.debug = strtol(arg,0,0);
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "Strict")) {
291fe90a
             if (stream) {
                 get_arg(arg, sizeof(arg), &p);
                 video_enc.strict_std_compliance = atoi(arg);
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoBufferSize")) {
46026f4e
             if (stream) {
                 get_arg(arg, sizeof(arg), &p);
291fe90a
                 video_enc.rc_buffer_size = atoi(arg) * 8*1024;
46026f4e
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoBitRateTolerance")) {
d6562d2c
             if (stream) {
                 get_arg(arg, sizeof(arg), &p);
                 video_enc.bit_rate_tolerance = atoi(arg) * 1000;
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoBitRate")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
                 video_enc.bit_rate = atoi(arg) * 1000;
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoSize")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
126b638e
                 av_parse_video_size(&video_enc.width, &video_enc.height, arg);
85f07f22
                 if ((video_enc.width % 16) != 0 ||
                     (video_enc.height % 16) != 0) {
472e12f5
                     ERROR("Image size must be a multiple of 16\n");
85f07f22
                 }
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoFrameRate")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
36907468
                 AVRational frame_rate;
126b638e
                 if (av_parse_video_rate(&frame_rate, arg) < 0) {
472e12f5
                     ERROR("Incorrect frame rate: %s\n", arg);
36907468
                 } else {
                     video_enc.time_base.num = frame_rate.den;
                     video_enc.time_base.den = frame_rate.num;
                 }
85f07f22
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoGopSize")) {
85f07f22
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
85f07f22
                 video_enc.gop_size = atoi(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoIntraOnly")) {
611c5741
             if (stream)
85f07f22
                 video_enc.gop_size = 1;
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoHighQuality")) {
611c5741
             if (stream)
7d1c3fc1
                 video_enc.mb_decision = FF_MB_DECISION_BITS;
bb3244de
         } else if (!av_strcasecmp(cmd, "Video4MotionVector")) {
e322ea48
             if (stream) {
7d1c3fc1
                 video_enc.mb_decision = FF_MB_DECISION_BITS; //FIXME remove
e322ea48
                 video_enc.flags |= CODEC_FLAG_4MV;
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "AVOptionVideo") ||
                    !av_strcasecmp(cmd, "AVOptionAudio")) {
7ab08864
             char arg2[1024];
             AVCodecContext *avctx;
             int type;
             get_arg(arg, sizeof(arg), &p);
             get_arg(arg2, sizeof(arg2), &p);
bb3244de
             if (!av_strcasecmp(cmd, "AVOptionVideo")) {
7ab08864
                 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)) {
472e12f5
                 ERROR("AVOption error: %s %s\n", arg, arg2);
7ab08864
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "AVPresetVideo") ||
                    !av_strcasecmp(cmd, "AVPresetAudio")) {
8190f62f
             AVCodecContext *avctx;
             int type;
             get_arg(arg, sizeof(arg), &p);
bb3244de
             if (!av_strcasecmp(cmd, "AVPresetVideo")) {
8190f62f
                 avctx = &video_enc;
                 video_enc.codec_id = video_id;
                 type = AV_OPT_FLAG_VIDEO_PARAM;
             } else {
                 avctx = &audio_enc;
                 audio_enc.codec_id = audio_id;
                 type = AV_OPT_FLAG_AUDIO_PARAM;
             }
             if (ffserver_opt_preset(arg, avctx, type|AV_OPT_FLAG_ENCODING_PARAM, &audio_id, &video_id)) {
                 ERROR("AVPreset error: %s\n", arg);
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "VideoTag")) {
038a1243
             get_arg(arg, sizeof(arg), &p);
611c5741
             if ((strlen(arg) == 4) && stream)
acd0026c
                 video_enc.codec_tag = MKTAG(arg[0], arg[1], arg[2], arg[3]);
bb3244de
         } else if (!av_strcasecmp(cmd, "BitExact")) {
611c5741
             if (stream)
267b0e57
                 video_enc.flags |= CODEC_FLAG_BITEXACT;
bb3244de
         } else if (!av_strcasecmp(cmd, "DctFastint")) {
611c5741
             if (stream)
267b0e57
                 video_enc.dct_algo  = FF_DCT_FASTINT;
bb3244de
         } else if (!av_strcasecmp(cmd, "IdctSimple")) {
611c5741
             if (stream)
267b0e57
                 video_enc.idct_algo = FF_IDCT_SIMPLE;
bb3244de
         } else if (!av_strcasecmp(cmd, "Qscale")) {
267b0e57
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
                 video_enc.flags |= CODEC_FLAG_QSCALE;
                 video_enc.global_quality = FF_QP2LAMBDA * atoi(arg);
             }
bb3244de
         } else if (!av_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) {
472e12f5
                     ERROR("VideoQDiff out of range\n");
42a63c6a
                 }
             }
bb3244de
         } else if (!av_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) {
472e12f5
                     ERROR("VideoQMax out of range\n");
42a63c6a
                 }
             }
bb3244de
         } else if (!av_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) {
472e12f5
                     ERROR("VideoQMin out of range\n");
42a63c6a
                 }
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "LumaElim")) {
6b10e6e4
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
6b10e6e4
                 video_enc.luma_elim_threshold = atoi(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "ChromaElim")) {
6b10e6e4
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
6b10e6e4
                 video_enc.chroma_elim_threshold = atoi(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "LumiMask")) {
6b10e6e4
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
6b10e6e4
                 video_enc.lumi_masking = atof(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "DarkMask")) {
6b10e6e4
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
6b10e6e4
                 video_enc.dark_masking = atof(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "NoVideo")) {
85f07f22
             video_id = CODEC_ID_NONE;
bb3244de
         } else if (!av_strcasecmp(cmd, "NoAudio")) {
85f07f22
             audio_id = CODEC_ID_NONE;
bb3244de
         } else if (!av_strcasecmp(cmd, "ACL")) {
58f48adb
             parse_acl_row(stream, feed, NULL, p, filename, line_num);
bb3244de
         } else if (!av_strcasecmp(cmd, "DynamicACL")) {
58f48adb
             if (stream) {
                 get_arg(stream->dynamic_acl, sizeof(stream->dynamic_acl), &p);
8256c0a3
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "RTSPOption")) {
2effd274
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
                 av_freep(&stream->rtsp_option);
65e72261
                 stream->rtsp_option = av_strdup(arg);
2effd274
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "MulticastAddress")) {
829ac53d
             get_arg(arg, sizeof(arg), &p);
             if (stream) {
ad8b8abc
                 if (resolve_host(&stream->multicast_ip, arg) != 0) {
472e12f5
                     ERROR("Invalid host/IP address: %s\n", arg);
829ac53d
                 }
                 stream->is_multicast = 1;
6edd6884
                 stream->loop = 1; /* default is looping */
829ac53d
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "MulticastPort")) {
829ac53d
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
829ac53d
                 stream->multicast_port = atoi(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "MulticastTTL")) {
6edd6884
             get_arg(arg, sizeof(arg), &p);
611c5741
             if (stream)
6edd6884
                 stream->multicast_ttl = atoi(arg);
bb3244de
         } else if (!av_strcasecmp(cmd, "NoLoop")) {
611c5741
             if (stream)
6edd6884
                 stream->loop = 0;
bb3244de
         } else if (!av_strcasecmp(cmd, "</Stream>")) {
85f07f22
             if (!stream) {
472e12f5
                 ERROR("No corresponding <Stream> for </Stream>\n");
ce651af2
             } else {
7e183a93
                 if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
                     if (audio_id != CODEC_ID_NONE) {
72415b2a
                         audio_enc.codec_type = AVMEDIA_TYPE_AUDIO;
7e183a93
                         audio_enc.codec_id = audio_id;
                         add_codec(stream, &audio_enc);
                     }
                     if (video_id != CODEC_ID_NONE) {
72415b2a
                         video_enc.codec_type = AVMEDIA_TYPE_VIDEO;
7e183a93
                         video_enc.codec_id = video_id;
                         add_codec(stream, &video_enc);
                     }
85f07f22
                 }
7e183a93
                 stream = NULL;
ce651af2
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "<Redirect")) {
cde25790
             /*********************************************/
             char *q;
             if (stream || feed || redirect) {
472e12f5
                 ERROR("Already in a tag\n");
cde25790
             } 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;
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "URL")) {
611c5741
             if (redirect)
cde25790
                 get_arg(redirect->feed_filename, sizeof(redirect->feed_filename), &p);
bb3244de
         } else if (!av_strcasecmp(cmd, "</Redirect>")) {
cde25790
             if (!redirect) {
472e12f5
                 ERROR("No corresponding <Redirect> for </Redirect>\n");
a9c1bb71
             } else {
9667a2d2
                 if (!redirect->feed_filename[0]) {
472e12f5
                     ERROR("No URL found for <Redirect>\n");
9667a2d2
                 }
                 redirect = NULL;
a9c1bb71
             }
bb3244de
         } else if (!av_strcasecmp(cmd, "LoadModule")) {
2effd274
             get_arg(arg, sizeof(arg), &p);
b250f9c6
 #if HAVE_DLOPEN
2effd274
             load_module(arg);
6638d424
 #else
472e12f5
             ERROR("Module support not compiled into this version: '%s'\n", arg);
6638d424
 #endif
85f07f22
         } else {
472e12f5
             ERROR("Incorrect keyword: '%s'\n", cmd);
85f07f22
         }
     }
472e12f5
 #undef ERROR
85f07f22
 
     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
 }
 
a0991833
 static int opt_help(const char *opt, const char *arg)
5a635bc7
 {
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);
a0991833
     return 0;
5a635bc7
 }
 
 static const OptionDef options[] = {
992f8eae
 #include "cmdutils_common_opts.h"
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)
 {
a92be9b8
     struct sigaction sigact = { { 0 } };
85f07f22
 
182cbe43
     parse_loglevel(argc, argv, options);
2c4ae653
     av_register_all();
776f2bb9
     avformat_network_init();
85f07f22
 
452406bd
     show_banner(argc, argv, options);
3578e9a0
 
cde25790
     my_program_name = argv[0];
d6562d2c
     my_program_dir = getcwd(0, 0);
2effd274
     ffserver_daemon = 1;
115329f1
 
7cc8d638
     parse_options(NULL, argc, argv, options, NULL);
85f07f22
 
59c2959d
     unsetenv("http_proxy");             /* Kill the http_proxy */
cde25790
 
576fb48e
     av_lfg_init(&random_state, av_get_random_seed());
8256c0a3
 
5eb765ef
     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;
 }