Skip to content
Snippets Groups Projects
ffserver.c 159 KiB
Newer Older
  • Learn to ignore specific revisions
  • Fabrice Bellard's avatar
    Fabrice Bellard committed
                return 0;
    
            len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 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
                    /* error : close connection */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    return -1;
                }
            } else {
                c->buffer_ptr += len;
    
                if (c->stream)
                    c->stream->bytes_served += len;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                if (c->buffer_ptr >= c->buffer_end) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    /* if error, exit */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                        return -1;
    
                    /* all the buffer was sent : synchronize to the incoming
                     * stream */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    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:
    
            /* for packetized output, we consider we can always write (the
    
               input streams set 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;
    
                /* no need to read if no events */
                if (!(c->poll_entry->revents & POLLOUT))
                    return 0;
            }
    
            if (http_send_data(c) < 0)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                return -1;
    
            /* close connection if trailer sent */
            if (c->state == HTTPSTATE_SEND_DATA_TRAILER)
                return -1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            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 */
    
            if (c->poll_entry->revents & (POLLIN | POLLERR | POLLHUP))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                return -1;
    
            /* nothing to do, we'll be waken up by incoming feed packets */
            break;
    
    
        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;
    
            len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
    
                if (ff_neterrno() != AVERROR(EAGAIN) &&
                    ff_neterrno() != AVERROR(EINTR)) {
    
                    /* 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;
    
        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;
    
            len = send(c->fd, c->packet_buffer_ptr,
                        c->packet_buffer_end - c->packet_buffer_ptr, 0);
    
            if (len < 0) {
    
                if (ff_neterrno() != AVERROR(EAGAIN) &&
                    ff_neterrno() != AVERROR(EINTR)) {
    
                    /* 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;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        default:
            return -1;
        }
        return 0;
    }
    
    
    static int extract_rates(char *rates, int ratelen, const char *request)
    {
        const char *p;
    
        for (p = request; *p && *p != '\r' && *p != '\n'; ) {
    
            if (av_strncasecmp(p, "Pragma:", 7) == 0) {
    
                while (*q && *q != '\n' && av_isspace(*q))
    
                if (av_strncasecmp(q, "stream-switch-entry=", 20) == 0) {
    
                    memset(rates, 0xff, ratelen);
    
                        if (sscanf(q, ":%d:%d", &stream_no, &rate_no) != 2)
    
                        if (stream_no < ratelen && stream_no >= 0)
    
                        while (*q && *q != '\n' && !av_isspace(*q))
    
    static int find_stream_in_feed(FFStream *feed, AVCodecContext *codec, int bit_rate)
    
        int best_bitrate = 100000000;
        int best = -1;
    
        for (i = 0; i < feed->nb_streams; i++) {
    
            AVCodecContext *feed_codec = feed->streams[i]->codec;
    
    
            if (feed_codec->codec_id != codec->codec_id ||
                feed_codec->sample_rate != codec->sample_rate ||
                feed_codec->width != codec->width ||
    
                feed_codec->height != codec->height)
    
                continue;
    
            /* Potential stream */
    
    
            /* We want the fastest stream less than bit_rate, or the slowest
    
             * 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;
    
        /* Not much we can do for a feed */
        if (!req->feed)
            return 0;
    
    
        for (i = 0; i < req->nb_streams; i++) {
    
            AVCodecContext *codec = req->streams[i]->codec;
    
                    c->switch_feed_streams[i] = req->feed_streams[i];
    
                    c->switch_feed_streams[i] = find_stream_in_feed(req->feed, codec, codec->bit_rate / 2);
    
                    /* 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
    
            if (c->switch_feed_streams[i] >= 0 && c->switch_feed_streams[i] != c->feed_streams[i])
                action_required = 1;
        }
    
        return action_required;
    }
    
    /* 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 (!av_isspace(*p) && *p != '\0') {
    
            if ((q - buf) < buf_size - 1)
                *q++ = *p;
            p++;
        }
        if (buf_size > 0)
            *q = '\0';
        *pp = p;
    }
    
    
    static void get_arg(char *buf, int buf_size, const char **pp)
    {
        const char *p;
        char *q;
        int quote;
    
        p = *pp;
    
        while (av_isspace(*p)) p++;
    
        q = buf;
        quote = 0;
        if (*p == '\"' || *p == '\'')
            quote = *p++;
        for(;;) {
            if (quote) {
                if (*p == quote)
                    break;
            } else {
    
                if (av_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;
    }
    
    
    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);
    
        if (av_strcasecmp(arg, "allow") == 0)
    
            acl.action = IP_ALLOW;
    
        else if (av_strcasecmp(arg, "deny") == 0)
    
            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 (av_isspace(*p))
    
                p++;
            if (*p == '\0' || *p == '#')
                continue;
            get_arg(cmd, sizeof(cmd), &p);
    
    
            if (!av_strcasecmp(cmd, "ACL"))
    
                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)
    
    {
        enum IPAddressAction last_action = IP_DENY;
        IPAddressACL *acl;
        struct in_addr *src = &c->from_addr.sin_addr;
    
        unsigned long src_addr = src->s_addr;
    
        for (acl = in_acl; acl; acl = acl->next) {
    
            if (src_addr >= acl->first.s_addr && src_addr <= acl->last.s_addr)
    
                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;
    }
    
    
    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;
    }
    
    
    /* compute the real filename of a file by matching it without its
    
       extensions to all the stream's 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 */
    
        av_strlcpy(file1, filename, sizeof(file1));
    
        p = strrchr(file1, '.');
        if (p)
            *p = '\0';
        for(stream = first_stream; stream != NULL; stream = stream->next) {
    
            av_strlcpy(file2, stream->filename, sizeof(file2));
    
            p = strrchr(file2, '.');
            if (p)
                *p = '\0';
            if (!strcmp(file1, file2)) {
    
                av_strlcpy(filename, stream->filename, max_size);
    
    /* parse HTTP request and prepare header */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    static int http_parse_request(HTTPContext *c)
    {
    
        const char *p;
        char *p1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        char cmd[32];
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        char url[1024], *q;
        char protocol[32];
        char msg[1024];
        const char *mime_type;
        FFStream *stream;
    
        const char *useragent = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        p = c->buffer;
    
        get_word(cmd, sizeof(cmd), &p);
    
        av_strlcpy(c->method, cmd, sizeof(c->method));
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (!strcmp(cmd, "GET"))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        else if (!strcmp(cmd, "POST"))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        else
            return -1;
    
    
        get_word(url, sizeof(url), &p);
    
        av_strlcpy(c->url, url, sizeof(c->url));
    
        get_word(protocol, sizeof(protocol), (const char **)&p);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (strcmp(protocol, "HTTP/1.0") && strcmp(protocol, "HTTP/1.1"))
            return -1;
    
        av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
    
            http_log("%s - - New connection: %s %s\n", inet_ntoa(c->from_addr.sin_addr), cmd, url);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* find the filename and the optional info string in the request */
    
        p1 = strchr(url, '?');
        if (p1) {
            av_strlcpy(info, p1, sizeof(info));
            *p1 = '\0';
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            info[0] = '\0';
    
    
        av_strlcpy(filename, url + ((*url == '/') ? 1 : 0), sizeof(filename)-1);
    
        for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
    
            if (av_strncasecmp(p, "User-Agent:", 11) == 0) {
    
                useragent = p + 11;
    
                if (*useragent && *useragent != '\n' && av_isspace(*useragent))
    
                    useragent++;
                break;
            }
            p = strchr(p, '\n');
            if (!p)
                break;
    
            p++;
        }
    
    
        if (av_match_ext(filename, "asx")) {
    
            filename[strlen(filename)-1] = 'f';
    
        } else if (av_match_ext(filename, "asf") &&
    
            (!useragent || av_strncasecmp(useragent, "NSPlayer", 8) != 0)) {
    
            /* if this isn't WMP or lookalike, return the redirector file */
    
        } else if (av_match_ext(filename, "rpm,ram")) {
    
            strcpy(filename + strlen(filename)-2, "m");
    
        } else if (av_match_ext(filename, "rtsp")) {
    
            compute_real_filename(filename, sizeof(filename) - 1);
    
        } else if (av_match_ext(filename, "sdp")) {
    
            compute_real_filename(filename, sizeof(filename) - 1);
    
        // "redirect" / request to index.html
        if (!strlen(filename))
    
            av_strlcpy(filename, "index.html", sizeof(filename) - 1);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        stream = first_stream;
        while (stream != NULL) {
    
            if (!strcmp(stream->filename, filename) && validate_acl(stream, c))
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                break;
            stream = stream->next;
        }
        if (stream == NULL) {
    
            snprintf(msg, sizeof(msg), "File '%s' not found", url);
    
            http_log("File '%s' not found\n", url);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            goto send_error;
        }
    
        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;
    
            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);
    
            /* prepare output buffer */
            c->buffer_ptr = c->buffer;
            c->buffer_end = q;
            c->state = HTTPSTATE_SEND_HEADER;
            return 0;
        }
    
    
        /* If this is WMP, get the rate information */
        if (extract_rates(ratebuf, sizeof(ratebuf), c->buffer)) {
    
            if (modify_current_stream(c, ratebuf)) {
    
                for (i = 0; i < FF_ARRAY_ELEMS(c->feed_streams); i++) {
    
                    if (c->switch_feed_streams[i] >= 0)
    
    Reinhard Tartler's avatar
    Reinhard Tartler committed
                        c->switch_feed_streams[i] = -1;
    
        if (c->post == 0 && stream->stream_type == STREAM_TYPE_LIVE)
            current_bandwidth += stream->bandwidth;
    
    
    Diego Biurrun's avatar
    Diego Biurrun committed
        /* If already streaming this feed, do not let start another feeder. */
    
        if (stream->feed_opened) {
            snprintf(msg, sizeof(msg), "This feed is already being received.");
    
            http_log("Feed '%s' already being received\n", stream->feed_filename);
    
        if (c->post == 0 && max_bandwidth < current_bandwidth) {
    
            c->http_error = 503;
    
            snprintf(q, c->buffer_size,
    
                          "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 bandwidth being served (including your stream) is %"PRIu64"kbit/sec, "
                          "and this exceeds the limit of %"PRIu64"kbit/sec.</p>\r\n"
    
                          "</body></html>\r\n", current_bandwidth, max_bandwidth);
    
            /* prepare output buffer */
            c->buffer_ptr = c->buffer;
            c->buffer_end = q;
            c->state = HTTPSTATE_SEND_HEADER;
            return 0;
        }
    
            const char *hostinfo = 0;
    
            for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
    
                if (av_strncasecmp(p, "Host:", 5) == 0) {
    
                    hostinfo = p + 5;
                    break;
                }
                p = strchr(p, '\n');
                if (!p)
                    break;
    
                p++;
            }
    
            if (hostinfo) {
                char *eoh;
                char hostbuf[260];
    
    
                while (av_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;
    
                            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);
    
                            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);
    
                            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);
    
                            break;
                        case REDIR_RTSP:
                            {
                                char hostname[256], *p;
                                /* extract only hostname */
    
                                av_strlcpy(hostname, hostbuf, sizeof(hostname));
    
                                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);
    
                                uint8_t *sdp_data;
    
                                int sdp_data_size;
                                socklen_t len;
    
                                snprintf(q, c->buffer_size,
    
                                              "HTTP/1.0 200 OK\r\n"
                                              "Content-type: application/sdp\r\n"
                                              "\r\n");
    
    
                                len = sizeof(my_addr);
                                getsockname(c->fd, (struct sockaddr *)&my_addr, &len);
    
                                sdp_data_size = prepare_sdp_description(stream,
                                                                        &sdp_data,
    
                                                                        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:
    
    
                        /* prepare output buffer */
                        c->buffer_ptr = c->buffer;
                        c->buffer_end = q;
                        c->state = HTTPSTATE_SEND_HEADER;
                        return 0;
                    }
                }
            }
    
    
            snprintf(msg, sizeof(msg), "ASX/RAM file not handled");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* XXX: add there authenticate and IP match */
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* if post, it means a feed is being sent */
            if (!stream->is_feed) {
    
    Diego Biurrun's avatar
    Diego Biurrun committed
                /* However it might be a status report from WMP! Let us log the
                 * data as it might come in handy one day. */
    
                const char *logline = 0;
    
                for (p = c->buffer; *p && *p != '\r' && *p != '\n'; ) {
    
                    if (av_strncasecmp(p, "Pragma: log-line=", 17) == 0) {
    
                    if (av_strncasecmp(p, "Pragma: client-id=", 18) == 0)
    
                    p = strchr(p, '\n');
                    if (!p)
                        break;
    
                    p++;
                }
    
                if (logline) {
                    char *eol = strchr(logline, '\n');
    
                    logline += 17;
    
                    if (eol) {
                        if (eol[-1] == '\r')
                            eol--;
    
    Falk Hüffner's avatar
    Falk Hüffner committed
                        http_log("%.*s\n", (int) (eol - logline), logline);
    
                http_log("\nGot request:\n%s\n", c->buffer);
    
    #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;
                    }
    
    
                    if (wmpc && modify_current_stream(wmpc, ratebuf))
                        wmpc->switch_pending = 1;
    
                snprintf(msg, sizeof(msg), "POST command not handled");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                goto send_error;
            }
            if (http_start_receive_data(c) < 0) {
    
                snprintf(msg, sizeof(msg), "could not open feed");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                goto send_error;
            }
            c->http_error = 0;
            c->state = HTTPSTATE_RECEIVE_DATA;
            return 0;
        }
    
    
        if (strcmp(stream->filename + strlen(stream->filename) - 4, ".asf") == 0)
    
            http_log("\nGot request:\n%s\n", c->buffer);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (c->stream->stream_type == STREAM_TYPE_STATUS)
    
            goto send_status;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        /* open input stream */
        if (open_input_stream(c, info) < 0) {
    
            snprintf(msg, sizeof(msg), "Input stream corresponding to '%s' not found", url);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            goto send_error;
        }
    
    
        /* prepare HTTP header */
    
        c->buffer[0] = 0;
        av_strlcatf(c->buffer, c->buffer_size, "HTTP/1.0 200 OK\r\n");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        mime_type = c->stream->fmt->mime_type;
        if (!mime_type)
    
            mime_type = "application/x-octet-stream";
    
        av_strlcatf(c->buffer, c->buffer_size, "Pragma: no-cache\r\n");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        /* for asf, we need extra headers */
    
        if (!strcmp(c->stream->fmt->name,"asf_stream")) {
    
            c->wmp_client_id = av_lfg_get(&random_state);
    
            av_strlcatf(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);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
        av_strlcatf(c->buffer, c->buffer_size, "Content-Type: %s\r\n", mime_type);
        av_strlcatf(c->buffer, c->buffer_size, "\r\n");
        q = c->buffer + strlen(c->buffer);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* 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;
    
        snprintf(q, c->buffer_size,
    
                      "HTTP/1.0 404 Not Found\r\n"
                      "Content-type: text/html\r\n"
                      "\r\n"
    
                      "<html>\n"
                      "<head><title>404 Not Found</title></head>\n"
                      "<body>%s</body>\n"
                      "</html>\n", msg);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* prepare output buffer */
        c->buffer_ptr = c->buffer;
        c->buffer_end = q;
        c->state = HTTPSTATE_SEND_HEADER;
        return 0;
    
     send_status:
        compute_status(c);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        c->http_error = 200; /* horrible : we use this value to avoid
                                going to the send data state */
        c->state = HTTPSTATE_SEND_HEADER;
        return 0;
    }
    
    
    static void fmt_bytecount(AVIOContext *pb, int64_t count)
    
        static const char suffix[] = " kMGTP";
    
        for (s = suffix; count >= 100000 && s[1]; count /= 1000, s++);
    
        avio_printf(pb, "%"PRId64"%c", count, *s);
    
    static void compute_status(HTTPContext *c)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
        HTTPContext *c1;
        FFStream *stream;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        time_t ti;
    
        AVIOContext *pb;
    
        if (avio_open_dyn_buf(&pb) < 0) {
    
            c->buffer_ptr = c->buffer;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
        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");
    
        avio_printf(pb, "<html><head><title>%s Status</title>\n", program_name);
    
            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);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* format status */
    
        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");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        stream = first_stream;
        while (stream != NULL) {
    
            char sfilename[1024];
            char *eosf;
    
    
                av_strlcpy(sfilename, stream->filename, sizeof(sfilename) - 10);
    
                eosf = sfilename + strlen(sfilename);
                if (eosf - sfilename >= 4) {
    
                    if (strcmp(eosf - 4, ".asf") == 0)
    
                    else if (strcmp(eosf - 3, ".rm") == 0)
    
                    else if (stream->fmt && !strcmp(stream->fmt->name, "rtp")) {
    
                        /* generate a sample RTSP director if
                           unicast. Generate an SDP redirector if
                           multicast */
    
                        eosf = strrchr(sfilename, '.');
                        if (!eosf)
                            eosf = sfilename + strlen(sfilename);
    
                        if (stream->is_multicast)
                            strcpy(eosf, ".sdp");
                        else
                            strcpy(eosf, ".rtsp");
    
                avio_printf(pb, "<tr><td><a href=\"/%s\">%s</a> ",
    
                avio_printf(pb, "<td align=right> %d <td align=right> ",
    
                case STREAM_TYPE_LIVE: {
    
                        int audio_bit_rate = 0;
                        int video_bit_rate = 0;
    
    Zdenek Kabelac's avatar
    Zdenek Kabelac committed
                        const char *audio_codec_name = "";
                        const char *video_codec_name = "";
                        const char *audio_codec_name_extra = "";
                        const char *video_codec_name_extra = "";