Skip to content
Snippets Groups Projects
ffserver.c 159 KiB
Newer Older
  • Learn to ignore specific revisions
  • Fabrice Bellard's avatar
    Fabrice Bellard committed
    /*
    
     * Copyright (c) 2000, 2001, 2002 Fabrice Bellard
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     *
    
     * This file is part of FFmpeg.
     *
     * FFmpeg is free software; you can redistribute it and/or
    
     * modify it under the terms of the GNU Lesser General Public
     * License as published by the Free Software Foundation; either
    
     * version 2.1 of the License, or (at your option) any later version.
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     *
    
     * FFmpeg is distributed in the hope that it will be useful,
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     * but WITHOUT ANY WARRANTY; without even the implied warranty of
    
     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     * Lesser General Public License for more details.
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     *
    
     * You should have received a copy of the GNU Lesser General Public
    
     * License along with FFmpeg; if not, write to the Free Software
    
     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
     */
    
    /**
     * @file
     * multiple format streaming server based on the FFmpeg libraries
     */
    
    
    #include "config.h"
    
    #if !HAVE_CLOSESOCKET
    
    #define closesocket close
    #endif
    #include <string.h>
    #include <stdlib.h>
    
    #include "libavformat/avformat.h"
    
    // FIXME those are internal headers, ffserver _really_ shouldn't use them
    
    #include "libavformat/ffm.h"
    
    #include "libavformat/network.h"
    #include "libavformat/os_support.h"
    
    #include "libavformat/rtpdec.h"
    
    #include "libavformat/rtsp.h"
    
    #include "libavformat/avio_internal.h"
    
    #include "libavformat/internal.h"
    #include "libavformat/url.h"
    
    
    #include "libavutil/avassert.h"
    
    #include "libavutil/dict.h"
    
    #include "libavutil/intreadwrite.h"
    
    #include "libavutil/mathematics.h"
    
    #include "libavutil/pixdesc.h"
    
    #include "libavutil/random_seed.h"
    
    #include "libavutil/parseutils.h"
    
    #include "libavutil/opt.h"
    
    #include "libavutil/time.h"
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    #include <stdarg.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <sys/ioctl.h>
    
    #include <poll.h>
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    #include <errno.h>
    #include <time.h>
    
    #include <sys/wait.h>
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    #include <signal.h>
    
    #include "cmdutils.h"
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
    const char program_name[] = "ffserver";
    
    const int program_birth_year = 2000;
    
    static const OptionDef options[];
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    enum HTTPState {
        HTTPSTATE_WAIT_REQUEST,
        HTTPSTATE_SEND_HEADER,
        HTTPSTATE_SEND_DATA_HEADER,
    
        HTTPSTATE_SEND_DATA,          /* sending TCP or UDP data */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        HTTPSTATE_SEND_DATA_TRAILER,
    
        HTTPSTATE_RECEIVE_DATA,
    
        HTTPSTATE_WAIT_FEED,          /* wait for data from the feed */
        HTTPSTATE_READY,
    
        RTSPSTATE_WAIT_REQUEST,
        RTSPSTATE_SEND_REPLY,
    
        RTSPSTATE_SEND_PACKET,
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    };
    
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
    static const char *http_state[] = {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        "SEND_DATA_HEADER",
        "SEND_DATA",
        "SEND_DATA_TRAILER",
        "RECEIVE_DATA",
        "WAIT_FEED",
    
        "RTSP_SEND_PACKET",
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    };
    
    
    #define MAX_STREAMS 20
    
    
    #define IOBUFFER_INIT_SIZE 8192
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    /* timeouts are in ms */
    
    #define HTTP_REQUEST_TIMEOUT (15 * 1000)
    #define RTSP_REQUEST_TIMEOUT (3600 * 24 * 1000)
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    #define SYNC_TIMEOUT (10 * 1000)
    
    
    typedef struct RTSPActionServerSetup {
        uint32_t ipaddr;
        char transport_option[512];
    } RTSPActionServerSetup;
    
    
        int64_t count1, count2;
    
        int64_t time1, time2;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    /* 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 */
    
        uint8_t *buffer_ptr, *buffer_end;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int http_error;
    
        int chunked_encoding;
        int chunk_size;               /* 0 if it needs to be read */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        struct HTTPContext *next;
    
        int got_key_frame; /* stream 0 => 1, stream 1 => 2, stream 2=> 4 */
    
        int64_t data_count;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* feed input */
        int feed_fd;
        /* input format handling */
        AVFormatContext *fmt_in;
    
        int64_t start_time;            /* In milliseconds - this wraps fairly often */
    
        int64_t first_pts;            /* initial pts value */
    
        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 */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* output format handling */
        struct FFStream *stream;
    
        /* -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;
    
        AVFormatContext fmt_ctx; /* instance of FFStream for one user */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int last_packet_sent; /* true if last data packet was sent */
    
        DataRateData datarate;
    
        char protocol[16];
        char method[16];
        char url[128];
    
        int buffer_size;
    
        uint8_t *buffer;
    
        int is_packetized; /* if true, the stream is packetized */
        int packet_stream_index; /* current stream for output in state machine */
    
        uint8_t *pb_buffer; /* XXX: use that in all the code */
    
        AVIOContext *pb;
    
        char session_id[32]; /* session id */
        AVFormatContext *rtp_ctx[MAX_STREAMS];
    
        /* 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;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    } HTTPContext;
    
    /* each generated stream is described here */
    enum StreamType {
        STREAM_TYPE_LIVE,
        STREAM_TYPE_STATUS,
    
        STREAM_TYPE_REDIRECT,
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    };
    
    
    enum IPAddressAction {
        IP_ALLOW = 1,
        IP_DENY,
    };
    
    typedef struct IPAddressACL {
        struct IPAddressACL *next;
        enum IPAddressAction action;
    
        struct in_addr first;
        struct in_addr last;
    } IPAddressACL;
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    /* description of each stream of the ffserver.conf file */
    typedef struct FFStream {
        enum StreamType stream_type;
        char filename[1024];     /* stream filename */
    
        struct FFStream *feed;   /* feed we are using (can be null if
                                    coming from file) */
    
        AVDictionary *in_opts;   /* input parameters */
    
        AVDictionary *metadata;  /* metadata to set on the stream */
    
        AVInputFormat *ifmt;       /* if non NULL, force input format */
    
        AVOutputFormat *fmt;
    
        char dynamic_acl[1024];
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int nb_streams;
    
        int prebuffer;      /* Number of millseconds early to start */
    
        int64_t max_time;      /* Number of milliseconds to run */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        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 */
    
        pid_t pid;  /* Of ffmpeg process */
    
        time_t pid_start;  /* Of ffmpeg process */
    
        char **child_argv;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        struct FFStream *next;
    
        unsigned bandwidth; /* bandwidth, in kbits/s */
    
        /* multicast specific */
        int is_multicast;
        struct in_addr multicast_ip;
        int multicast_port; /* first port used for multicast */
    
        int multicast_ttl;
        int loop; /* if true, send the stream in loops (only meaningful if file) */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* feed specific */
    
        int feed_opened;     /* true if someone is writing to the feed */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        int is_feed;         /* true if it is a feed */
    
        int readonly;        /* True if writing is prohibited to the file */
    
        int truncate;        /* True if feeder connection truncate the feed file */
    
        int64_t bytes_served;
    
        int64_t feed_max_size;      /* maximum storage size, zero means unlimited */
    
    Diego Biurrun's avatar
    Diego Biurrun committed
        int64_t feed_write_index;   /* current write position in feed (it wraps around) */
    
        int64_t feed_size;          /* current size of feed */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        struct FFStream *next_feed;
    } FFStream;
    
    typedef struct FeedData {
        long long data_count;
    
    Diego Biurrun's avatar
    Diego Biurrun committed
        float avg_frame_size;   /* frame size averaged over last frames with exponential mean */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    } FeedData;
    
    
    static struct sockaddr_in my_http_addr;
    static struct sockaddr_in my_rtsp_addr;
    
    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 */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
    static void new_connection(int server_fd, int is_rtsp);
    static void close_connection(HTTPContext *c);
    
    /* HTTP handling */
    static int handle_connection(HTTPContext *c);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    static int http_parse_request(HTTPContext *c);
    
    static int http_send_data(HTTPContext *c);
    
    static void compute_status(HTTPContext *c);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    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);
    
    
    /* RTSP handling */
    static int rtsp_parse_request(HTTPContext *c);
    static void rtsp_cmd_describe(HTTPContext *c, const char *url);
    
    static void rtsp_cmd_options(HTTPContext *c, const char *url);
    
    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);
    
    static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
    
    static HTTPContext *rtp_new_connection(struct sockaddr_in *from_addr,
    
                                           FFStream *stream, const char *session_id,
    
    static int rtp_new_av_stream(HTTPContext *c,
    
                                 int stream_index, struct sockaddr_in *dest_addr,
                                 HTTPContext *rtsp_c);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
    static const char *my_program_name;
    
    
    static const char *config_filename;
    
    static int ffserver_debug;
    static int no_launch;
    
    static int need_to_start_children;
    
    /* maximum number of simultaneous HTTP connections */
    static unsigned int nb_max_http_connections = 2000;
    
    static unsigned int nb_max_connections = 5;
    static unsigned int nb_connections;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
    static uint64_t max_bandwidth = 1000;
    
    static uint64_t current_bandwidth;
    
    static int64_t cur_time;           // Making this global saves on passing it around everywhere
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    static FILE *logfile = NULL;
    
    
    static void htmlstrip(char *s) {
        while (s && *s) {
            s += strspn(s, "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ,. ");
            if (*s)
                *s++ = '?';
        }
    }
    
    
    static int64_t ffm_read_write_index(int fd)
    {
        uint8_t buf[8];
    
    
        if (lseek(fd, 8, SEEK_SET) < 0)
            return AVERROR(EIO);
    
        if (read(fd, buf, 8) != 8)
            return AVERROR(EIO);
        return AV_RB64(buf);
    }
    
    static int ffm_write_write_index(int fd, int64_t pos)
    {
        uint8_t buf[8];
        int i;
    
        for(i=0;i<8;i++)
            buf[i] = (pos >> (56 - i * 8)) & 0xff;
    
        if (lseek(fd, 8, SEEK_SET) < 0)
            return AVERROR(EIO);
    
        if (write(fd, buf, 8) != 8)
            return AVERROR(EIO);
        return 8;
    }
    
    static void ffm_set_write_index(AVFormatContext *s, int64_t pos,
                                    int64_t file_size)
    {
        FFMContext *ffm = s->priv_data;
        ffm->write_index = pos;
        ffm->file_size = file_size;
    }
    
    
    /* FIXME: make ffserver work with IPv6 */
    /* 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;
    
            struct addrinfo hints = { 0 };
    
            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;
    }
    
    
    static char *ctime1(char *buf2, int buf_size)
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
    {
        time_t ti;
        char *p;
    
        ti = time(NULL);
        p = ctime(&ti);
    
        av_strlcpy(buf2, p, buf_size);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
        p = buf2 + strlen(p) - 1;
        if (*p == '\n')
            *p = '\0';
        return buf2;
    }
    
    
    static void http_vlog(const char *fmt, va_list vargs)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
    
        static int print_prefix = 1;
    
            if (print_prefix) {
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                char buf[32];
    
                ctime1(buf, sizeof(buf));
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                fprintf(logfile, "%s ", buf);
    
            }
            print_prefix = strstr(fmt, "\n") != NULL;
    
            vfprintf(logfile, fmt, vargs);
    
    #ifdef __GNUC__
    __attribute__ ((format (printf, 1, 2)))
    #endif
    static void http_log(const char *fmt, ...)
    
    {
        va_list vargs;
        va_start(vargs, fmt);
        http_vlog(fmt, vargs);
        va_end(vargs);
    }
    
    static void http_av_log(void *ptr, int level, const char *fmt, va_list vargs)
    {
        static int print_prefix = 1;
        AVClass *avc = ptr ? *(AVClass**)ptr : NULL;
    
        if (level > av_log_get_level())
    
            return;
        if (print_prefix && avc)
    
            http_log("[%s @ %p]", avc->item_name(ptr), ptr);
    
        print_prefix = strstr(fmt, "\n") != NULL;
        http_vlog(fmt, vargs);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    }
    
    
    static void log_connection(HTTPContext *c)
    {
    
        if (c->suppress_log)
    
        http_log("%s - - [%s] \"%s %s\" %d %"PRId64"\n",
                 inet_ntoa(c->from_addr.sin_addr), c->method, c->url,
    
                 c->protocol, (c->http_error ? c->http_error : 200), c->data_count);
    
    static void update_datarate(DataRateData *drd, int64_t count)
    
    {
        if (!drd->time1 && !drd->count1) {
            drd->time1 = drd->time2 = cur_time;
            drd->count1 = drd->count2 = count;
    
        } else if (cur_time - drd->time2 > 5000) {
    
    Alex Beregszaszi's avatar
    Alex Beregszaszi committed
            drd->time1 = drd->time2;
            drd->count1 = drd->count2;
            drd->time2 = cur_time;
            drd->count2 = count;
    
    static int compute_datarate(DataRateData *drd, int64_t count)
    
    {
        if (cur_time == drd->time1)
            return 0;
    
        return ((count - drd->count1) * 1000) / (cur_time - drd->time1);
    }
    
    
    static void start_children(FFStream *feed)
    {
    
        for (; feed; feed = feed->next) {
    
            if (feed->child_argv && !feed->pid) {
                feed->pid_start = time(0);
    
    
                feed->pid = fork();
    
                if (feed->pid < 0) {
    
                    http_log("Unable to create children\n");
    
                    exit(1);
                }
                if (!feed->pid) {
                    /* In child */
                    char pathname[1024];
                    char *slash;
                    int i;
    
    
                    /* replace "ffserver" with "ffmpeg" in the path of current
                     * program. Ignore user provided path */
    
                    av_strlcpy(pathname, my_program_name, sizeof(pathname));
                    slash = strrchr(pathname, '/');
                    if (!slash)
                        slash = pathname;
                    else
                        slash++;
                    strcpy(slash, "ffmpeg");
    
    
                    http_log("Launch command line: ");
    
    Stefano Sabatini's avatar
    Stefano Sabatini committed
                    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");
    
                    for (i = 3; i < 256; i++)
    
                    if (!ffserver_debug) {
    
                        if (!freopen("/dev/null", "r", stdin))
                            http_log("failed to redirect STDIN to /dev/null\n;");
                        if (!freopen("/dev/null", "w", stdout))
                            http_log("failed to redirect STDOUT to /dev/null\n;");
                        if (!freopen("/dev/null", "w", stderr))
                            http_log("failed to redirect STDERR to /dev/null\n;");
    
                    execvp(pathname, feed->child_argv);
    
                    _exit(1);
                }
            }
        }
    
    /* open a listening socket */
    static int socket_open_listen(struct sockaddr_in *my_addr)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        server_fd = socket(AF_INET,SOCK_STREAM,0);
        if (server_fd < 0) {
            perror ("socket");
            return -1;
        }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        tmp = 1;
        setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp));
    
    
        if (bind (server_fd, (struct sockaddr *) my_addr, sizeof (*my_addr)) < 0) {
    
            char bindmsg[32];
            snprintf(bindmsg, sizeof(bindmsg), "bind(port %d)", ntohs(my_addr->sin_port));
            perror (bindmsg);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            return -1;
        }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (listen (server_fd, 5) < 0) {
            perror ("listen");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            return -1;
        }
    
        ff_socket_nonblock(server_fd, 1);
    
    /* start all multicast streams */
    static void start_multicast(void)
    {
        FFStream *stream;
        char session_id[32];
        HTTPContext *rtp_c;
    
        struct sockaddr_in dest_addr = {0};
    
        int default_port, stream_index;
    
        default_port = 6000;
        for(stream = first_stream; stream != NULL; stream = stream->next) {
            if (stream->is_multicast) {
    
                unsigned random0 = av_lfg_get(&random_state);
                unsigned random1 = av_lfg_get(&random_state);
    
                snprintf(session_id, sizeof(session_id), "%08x%08x",
    
                         random0, random1);
    
    
                /* 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);
    
    
                rtp_c = rtp_new_connection(&dest_addr, stream, session_id,
    
                if (open_input_stream(rtp_c, "") < 0) {
    
                    http_log("Could not open input stream for stream '%s'\n",
                             stream->filename);
    
                for(stream_index = 0; stream_index < stream->nb_streams;
    
                    dest_addr.sin_port = htons(stream->multicast_port +
    
                    if (rtp_new_av_stream(rtp_c, stream_index, &dest_addr, NULL) < 0) {
    
                        http_log("Could not open output stream '%s/streamid=%d'\n",
                                 stream->filename, stream_index);
    
    /* main loop of the HTTP server */
    
        int server_fd = 0, rtsp_server_fd = 0;
        int ret, delay, delay1;
    
        struct pollfd *poll_table, *poll_entry;
    
        if(!(poll_table = av_mallocz((nb_max_http_connections + 2)*sizeof(*poll_table)))) {
    
            http_log("Impossible to allocate a poll table handling %d connections.\n", nb_max_http_connections);
            return -1;
        }
    
    
        if (my_http_addr.sin_port) {
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
            server_fd = socket_open_listen(&my_http_addr);
            if (server_fd < 0)
                return -1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
        if (my_rtsp_addr.sin_port) {
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
            rtsp_server_fd = socket_open_listen(&my_rtsp_addr);
            if (rtsp_server_fd < 0)
                return -1;
    
        }
    
        if (!rtsp_server_fd && !server_fd) {
            http_log("HTTP and RTSP disabled.\n");
            return -1;
        }
    
        http_log("FFserver started.\n");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
        start_children(first_feed);
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        for(;;) {
            poll_entry = poll_table;
    
            if (server_fd) {
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                poll_entry->fd = server_fd;
                poll_entry->events = POLLIN;
                poll_entry++;
    
            }
            if (rtsp_server_fd) {
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                poll_entry->fd = rtsp_server_fd;
                poll_entry->events = POLLIN;
                poll_entry++;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* wait for events on each HTTP handle */
            c = first_http_ctx;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            while (c != NULL) {
                int fd;
                fd = c->fd;
                switch(c->state) {
    
                case HTTPSTATE_SEND_HEADER:
                case RTSPSTATE_SEND_REPLY:
    
                case RTSPSTATE_SEND_PACKET:
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    c->poll_entry = poll_entry;
                    poll_entry->fd = fd;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    poll_entry++;
                    break;
                case HTTPSTATE_SEND_DATA_HEADER:
                case HTTPSTATE_SEND_DATA:
                case HTTPSTATE_SEND_DATA_TRAILER:
    
                        /* 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 {
    
                        /* when ffserver is doing the timing, we work by
    
                           looking at which packet needs to be sent every
    
                           10 ms */
                        delay1 = 10; /* one tick wait XXX: 10 ms assumed */
                        if (delay1 < delay)
                            delay = delay1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                case HTTPSTATE_RECEIVE_DATA:
                case HTTPSTATE_WAIT_FEED:
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    /* need to catch errors */
                    c->poll_entry = poll_entry;
                    poll_entry->fd = fd;
    
                    poll_entry->events = POLLIN;/* Maybe this will work */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    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 {
    
                ret = poll(poll_table, poll_entry - poll_table, delay);
    
                if (ret < 0 && ff_neterrno() != AVERROR(EAGAIN) &&
                    ff_neterrno() != AVERROR(EINTR))
    
            cur_time = av_gettime() / 1000;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
            if (need_to_start_children) {
                need_to_start_children = 0;
                start_children(first_feed);
            }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* now handle the events */
    
            for(c = first_http_ctx; c != NULL; c = c_next) {
                c_next = c->next;
                if (handle_connection(c) < 0) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    /* close and free the connection */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                }
            }
    
            poll_entry = poll_table;
    
            if (server_fd) {
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                /* new HTTP connection request ? */
                if (poll_entry->revents & POLLIN)
                    new_connection(server_fd, 0);
                poll_entry++;
    
            }
            if (rtsp_server_fd) {
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                /* new RTSP connection request ? */
                if (poll_entry->revents & POLLIN)
                    new_connection(rtsp_server_fd, 1);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    }
    
    
    /* start waiting for a new HTTP/RTSP request */
    static void start_wait_request(HTTPContext *c, int is_rtsp)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
    
        c->buffer_ptr = c->buffer;
        c->buffer_end = c->buffer + c->buffer_size - 1; /* leave room for '\0' */
    
        if (is_rtsp) {
            c->timeout = cur_time + RTSP_REQUEST_TIMEOUT;
            c->state = RTSPSTATE_WAIT_REQUEST;
        } else {
            c->timeout = cur_time + HTTP_REQUEST_TIMEOUT;
            c->state = HTTPSTATE_WAIT_REQUEST;
        }
    }
    
    
    static void http_send_too_busy_reply(int fd)
    {
    
        int len = snprintf(buffer, sizeof(buffer),
    
                           "HTTP/1.0 503 Server too busy\r\n"
    
                           "Content-type: text/html\r\n"
                           "\r\n"
                           "<html><head><title>Too busy</title></head><body>\r\n"
                           "<p>The server is too busy to serve your request at this time.</p>\r\n"
    
                           "<p>The number of current connections is %u, and this exceeds the limit of %u.</p>\r\n"
    
                           "</body></html>\r\n",
                           nb_connections, nb_max_connections);
    
        av_assert0(len < sizeof(buffer));
    
    static void new_connection(int server_fd, int is_rtsp)
    {
        struct sockaddr_in from_addr;
    
        socklen_t len;
        int fd;
    
        fd = accept(server_fd, (struct sockaddr *)&from_addr,
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
        if (fd < 0) {
            http_log("error during accept %s\n", strerror(errno));
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
        }
    
        if (nb_connections >= nb_max_connections) {
            http_send_too_busy_reply(fd);
    
        /* add a new connection */
        c = av_mallocz(sizeof(HTTPContext));
        if (!c)
            goto fail;
    
        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;
    
    
        c->next = first_http_ctx;
        first_http_ctx = c;
    
        start_wait_request(c, is_rtsp);
    
        return;
    
     fail:
        if (c) {
            av_free(c->buffer);
            av_free(c);
        }
    
    }
    
    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;
    
        /* 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;
        }
    
    
        /* remove connection associated resources */
        if (c->fd >= 0)
    
        if (c->fmt_in) {
            /* close each frame parser */
            for(i=0;i<c->fmt_in->nb_streams;i++) {
                st = c->fmt_in->streams[i];
    
                if (st->codec->codec)
    
            avformat_close_input(&c->fmt_in);
    
        }
    
        /* free RTP output streams if any */
        nb_streams = 0;
    
        for(i=0;i<nb_streams;i++) {
            ctx = c->rtp_ctx[i];
            if (ctx) {
                av_write_trailer(ctx);
    
                av_dict_free(&ctx->metadata);
    
                av_free(ctx->streams[0]);
    
                ffurl_close(h);
    
        if (!c->last_packet_sent && c->state == HTTPSTATE_SEND_DATA_TRAILER) {
    
                if (avio_open_dyn_buf(&ctx->pb) >= 0) {
    
                    avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
    
        for(i=0; i<ctx->nb_streams; i++)
    
    Alex Beregszaszi's avatar
    Alex Beregszaszi committed
            av_free(ctx->streams[i]);
    
        av_freep(&ctx->streams);
        av_freep(&ctx->priv_data);
    
        if (c->stream && !c->post && c->stream->stream_type == STREAM_TYPE_LIVE)
    
            current_bandwidth -= c->stream->bandwidth;
    
    
        /* 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);
        }
    
    
        av_freep(&c->packet_buffer);
    
        av_free(c->buffer);
        av_free(c);
        nb_connections--;
    }
    
    static int handle_connection(HTTPContext *c)
    {
        int len, ret;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        switch(c->state) {
        case HTTPSTATE_WAIT_REQUEST:
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* 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 */
    
            len = recv(c->fd, c->buffer_ptr, 1, 0);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (len < 0) {
    
                if (ff_neterrno() != AVERROR(EAGAIN) &&
                    ff_neterrno() != AVERROR(EINTR))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    return -1;
            } else if (len == 0) {
                return -1;
            } else {
    
                /* search for end of request. */
    
                uint8_t *ptr;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                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 */
    
                    if (c->state == HTTPSTATE_WAIT_REQUEST) {
                        ret = http_parse_request(c);
                    } else {
                        ret = rtsp_parse_request(c);
                    }
                    if (ret < 0)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                        return -1;
                } else if (ptr >= c->buffer_end) {
                    /* request too long: cannot do anything */
                    return -1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            }
            break;
    
        case HTTPSTATE_SEND_HEADER:
            if (c->poll_entry->revents & (POLLERR | POLLHUP))
                return -1;
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (!(c->poll_entry->revents & POLLOUT))