Skip to content
Snippets Groups Projects
ffserver.c 158 KiB
Newer Older
  • Learn to ignore specific revisions
  •             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 = "";
    
    
                        for(i=0;i<stream->nb_streams;i++) {
                            AVStream *st = stream->streams[i];
    
                            AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
                            switch(st->codec->codec_type) {
    
                                audio_bit_rate += st->codec->bit_rate;
    
                                if (codec) {
                                    if (*audio_codec_name)
                                        audio_codec_name_extra = "...";
                                    audio_codec_name = codec->name;
                                }
                                break;
    
                                video_bit_rate += st->codec->bit_rate;
    
                                if (codec) {
                                    if (*video_codec_name)
                                        video_codec_name_extra = "...";
                                    video_codec_name = codec->name;
                                }
                                break;
    
                                video_bit_rate += st->codec->bit_rate;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                        }
    
                        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",
    
                                     video_bit_rate / 1000, video_codec_name, video_codec_name_extra,
                                     audio_bit_rate / 1000, audio_codec_name, audio_codec_name_extra);
    
                            avio_printf(pb, "<td>%s", stream->feed->filename);
    
                            avio_printf(pb, "<td>%s", stream->feed_filename);
                        avio_printf(pb, "\n");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    }
    
                    avio_printf(pb, "<td align=center> - <td align=right> - <td align=right> - <td><td align=right> - <td>\n");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                }
            }
            stream = stream->next;
        }
    
        avio_printf(pb, "</table>\n");
    
    
        stream = first_stream;
        while (stream != NULL) {
            if (stream->feed == stream) {
    
                avio_printf(pb, "<h2>Feed %s</h2>", stream->filename);
    
                if (stream->pid) {
    
                    avio_printf(pb, "Running as pid %d.\n", stream->pid);
    
    #if defined(linux)
    
                    {
                        FILE *pid_stat;
                        char ps_cmd[64];
    
                        /* This is somewhat linux specific I guess */
    
                        snprintf(ps_cmd, sizeof(ps_cmd),
                                 "ps -o \"%%cpu,cputime\" --no-headers %d",
    
                        pid_stat = popen(ps_cmd, "r");
                        if (pid_stat) {
                            char cpuperc[10];
                            char cpuused[64];
    
                            if (fscanf(pid_stat, "%9s %63s", cpuperc,
    
                                avio_printf(pb, "Currently using %s%% of the cpu. Total time used %s.\n",
    
                    avio_printf(pb, "<p>");
    
                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");
    
    
                for (i = 0; i < stream->nb_streams; i++) {
                    AVStream *st = stream->streams[i];
    
                    AVCodec *codec = avcodec_find_encoder(st->codec->codec_id);
    
                    const char *type = "unknown";
    
                        snprintf(parameters, sizeof(parameters), "%d channel(s), %d Hz", st->codec->channels, st->codec->sample_rate);
    
                        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);
    
                    avio_printf(pb, "<tr><td align=right>%d<td>%s<td align=right>%d<td>%s<td>%s\n",
    
                            i, type, st->codec->bit_rate/1000, codec ? codec->name : "", parameters);
    
                avio_printf(pb, "</table>\n");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* connection status */
    
        avio_printf(pb, "<h2>Connection Status</h2>\n");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
        avio_printf(pb, "Number of connections: %d / %d<br>\n",
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                     nb_connections, nb_max_connections);
    
    
        avio_printf(pb, "Bandwidth in use: %"PRIu64"k / %"PRIu64"k<br>\n",
    
        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");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        c1 = first_http_ctx;
        i = 0;
    
            int bitrate;
            int j;
    
            bitrate = 0;
    
            if (c1->stream) {
                for (j = 0; j < c1->stream->nb_streams; j++) {
    
                    if (!c1->stream->feed)
    
                        bitrate += c1->stream->streams[j]->codec->bit_rate;
    
                    else if (c1->feed_streams[j] >= 0)
                        bitrate += c1->stream->feed->streams[c1->feed_streams[j]]->codec->bit_rate;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            i++;
            p = inet_ntoa(c1->from_addr.sin_addr);
    
            avio_printf(pb, "<tr><td><b>%d</b><td>%s%s<td>%s<td>%s<td>%s<td align=right>",
    
                        i,
                        c1->stream ? c1->stream->filename : "",
    
                        c1->state == HTTPSTATE_RECEIVE_DATA ? "(input)" : "",
    
                        c1->protocol,
                        http_state[c1->state]);
            fmt_bytecount(pb, bitrate);
    
            avio_printf(pb, "<td align=right>");
    
            fmt_bytecount(pb, compute_datarate(&c1->datarate, c1->data_count) * 8);
    
            avio_printf(pb, "<td align=right>");
    
            avio_printf(pb, "\n");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            c1 = c1->next;
        }
    
        avio_printf(pb, "</table>\n");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* date */
        ti = time(NULL);
        p = ctime(&ti);
    
        avio_printf(pb, "<hr size=1 noshade>Generated at %s", p);
        avio_printf(pb, "</body>\n</html>\n");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
        len = avio_close_dyn_buf(pb, &c->pb_buffer);
    
        c->buffer_ptr = c->pb_buffer;
        c->buffer_end = c->pb_buffer + len;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    }
    
    static int open_input_stream(HTTPContext *c, const char *info)
    {
        char buf[128];
        char input_filename[1024];
    
        AVFormatContext *s = NULL;
    
        int buf_size, i, ret;
    
        int64_t stream_pos;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        /* find file name */
        if (c->stream->feed) {
            strcpy(input_filename, c->stream->feed->feed_filename);
    
            buf_size = FFM_PACKET_SIZE;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* compute position (absolute time) */
    
            if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
    
                if ((ret = av_parse_time(&stream_pos, buf, 0)) < 0) {
                    http_log("Invalid date specification '%s' for stream\n", buf);
    
            } else if (av_find_info_tag(buf, sizeof(buf), "buffer", info)) {
    
                stream_pos = av_gettime() - prebuffer * (int64_t)1000000;
    
                stream_pos = av_gettime() - c->stream->prebuffer * (int64_t)1000;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        } else {
            strcpy(input_filename, c->stream->feed_filename);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* compute position (relative time) */
    
            if (av_find_info_tag(buf, sizeof(buf), "date", info)) {
    
                if ((ret = av_parse_time(&stream_pos, buf, 1)) < 0) {
                    http_log("Invalid date specification '%s' for stream\n", buf);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                stream_pos = 0;
        }
    
        if (!input_filename[0]) {
            http_log("No filename was specified for stream\n");
            return AVERROR(EINVAL);
        }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
        /* open stream */
    
        if ((ret = avformat_open_input(&s, input_filename, c->stream->ifmt, &c->stream->in_opts)) < 0) {
    
            http_log("Could not open input '%s': %s\n", input_filename, av_err2str(ret));
            return ret;
    
    
        /* set buffer size */
        if (buf_size > 0) ffio_set_buf_size(s->pb, buf_size);
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        c->fmt_in = s;
    
        if (strcmp(s->iformat->name, "ffm") &&
            (ret = avformat_find_stream_info(c->fmt_in, NULL)) < 0) {
            http_log("Could not find stream info for input '%s'\n", input_filename);
    
        /* choose stream as clock source (we favor the video stream if
         * present) for packet sending */
    
        c->pts_stream_index = 0;
        for(i=0;i<c->stream->nb_streams;i++) {
    
            if (c->pts_stream_index == 0 &&
    
                c->stream->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
        if (c->fmt_in->iformat->read_seek)
    
            av_seek_frame(c->fmt_in, -1, stream_pos, 0);
    
        /* set the start time (needed for maxtime and RTP packet timing) */
        c->start_time = cur_time;
        c->first_pts = AV_NOPTS_VALUE;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        return 0;
    }
    
    
    /* return the server clock (in us) */
    static int64_t get_server_clock(HTTPContext *c)
    
        /* compute current pts value from system time */
    
        return (cur_time - c->start_time) * 1000;
    
    /* return the estimated time at which the current packet must be sent
       (in us) */
    static int64_t get_packet_send_clock(HTTPContext *c)
    
        int bytes_left, bytes_sent, frame_bytes;
    
        if (frame_bytes <= 0)
    
            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;
    
        }
    }
    
    
    static int http_prepare_data(HTTPContext *c)
    {
        int i, len, ret;
        AVFormatContext *ctx;
    
    
        av_freep(&c->pb_buffer);
    
        switch(c->state) {
        case HTTPSTATE_SEND_DATA_HEADER:
    
            ctx = avformat_alloc_context();
            c->fmt_ctx = *ctx;
            av_freep(&ctx);
    
            av_dict_copy(&(c->fmt_ctx.metadata), c->stream->metadata, 0);
    
            c->fmt_ctx.streams = av_mallocz_array(c->stream->nb_streams, sizeof(AVStream *));
    
            for(i=0;i<c->stream->nb_streams;i++) {
    
                c->fmt_ctx.streams[i] = av_mallocz(sizeof(AVStream));
    
                /* if file or feed, then just take streams from FFStream struct */
    
                if (!c->stream->feed ||
    
                    src = c->stream->streams[i];
    
                    src = c->stream->feed->streams[c->stream->feed_streams[i]];
    
    
                *(c->fmt_ctx.streams[i]) = *src;
                c->fmt_ctx.streams[i]->priv_data = 0;
    
                /* XXX: should be done in AVStream, not in codec */
                c->fmt_ctx.streams[i]->codec->frame_number = 0;
    
            /* set output format parameters */
            c->fmt_ctx.oformat = c->stream->fmt;
            c->fmt_ctx.nb_streams = c->stream->nb_streams;
    
    
            c->got_key_frame = 0;
    
            /* prepare header and save header data in a stream */
    
            if (avio_open_dyn_buf(&c->fmt_ctx.pb) < 0) {
    
            c->fmt_ctx.pb->seekable = 0;
    
             * HACK to avoid MPEG-PS muxer to spit many underflow errors
    
             * Try to set it using configuration option
    
            if ((ret = avformat_write_header(&c->fmt_ctx, NULL)) < 0) {
                http_log("Error writing output header for stream '%s': %s\n",
                         c->stream->filename, av_err2str(ret));
                return ret;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
            }
    
            av_dict_free(&c->fmt_ctx.metadata);
    
            len = avio_close_dyn_buf(c->fmt_ctx.pb, &c->pb_buffer);
    
            c->buffer_ptr = c->pb_buffer;
            c->buffer_end = c->pb_buffer + len;
    
            c->state = HTTPSTATE_SEND_DATA;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            c->last_packet_sent = 0;
            break;
        case HTTPSTATE_SEND_DATA:
            /* find a new packet */
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
            /* 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:
    
                ret = av_read_frame(c->fmt_in, &pkt);
                if (ret < 0) {
                    if (c->stream->feed) {
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                        /* 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 */
    
                    } else if (ret == AVERROR(EAGAIN)) {
                        /* input not ready, come back later */
                        return 0;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                        if (c->stream->loop) {
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                            if (open_input_stream(c, "") < 0)
                                goto no_loop;
                            goto redo;
                        } else {
                        no_loop:
    
                            /* must send trailer now because EOF or error */
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                            c->state = HTTPSTATE_SEND_DATA_TRAILER;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                    }
                } else {
    
                    int source_index = pkt.stream_index;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                    /* 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;
    
                            for(i=0;i<c->stream->nb_streams;i++) {
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                                if (c->switch_feed_streams[i] == pkt.stream_index)
    
                                    if (pkt.flags & AV_PKT_FLAG_KEY)
    
    Reinhard Tartler's avatar
    Reinhard Tartler committed
                                        c->switch_feed_streams[i] = -1;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                                if (c->switch_feed_streams[i] >= 0)
                                    c->switch_pending = 1;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                        }
                        for(i=0;i<c->stream->nb_streams;i++) {
    
                            if (c->stream->feed_streams[i] == pkt.stream_index) {
    
                                AVStream *st = c->fmt_in->streams[source_index];
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                                pkt.stream_index = i;
    
                                if (pkt.flags & AV_PKT_FLAG_KEY &&
    
                                    (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
    
                                     c->stream->nb_streams == 1))
    
                                    c->got_key_frame = 1;
                                if (!c->stream->send_on_key || c->got_key_frame)
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                                    goto send_it;
                            }
                        }
                    } else {
                        AVCodecContext *codec;
    
                        AVStream *ist, *ost;
                    send_it:
                        ist = c->fmt_in->streams[source_index];
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                        /* specific handling for RTP: we use several
    
                         * output streams (one for each RTP connection).
                         * XXX: need more abstract handling */
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                        if (c->is_packetized) {
                            /* compute send time and duration */
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                            c->cur_pts = av_rescale_q(pkt.dts, ist->time_base, AV_TIME_BASE_Q);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                            c->cur_frame_duration = av_rescale_q(pkt.duration, ist->time_base, AV_TIME_BASE_Q);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                            /* find RTP context */
                            c->packet_stream_index = pkt.stream_index;
                            ctx = c->rtp_ctx[c->packet_stream_index];
                            if(!ctx) {
    
                                av_free_packet(&pkt);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                                break;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                            codec = ctx->streams[0]->codec;
                            /* only one stream per RTP connection */
                            pkt.stream_index = 0;
                        } else {
                            ctx = &c->fmt_ctx;
                            /* Fudge here */
    
                            codec = ctx->streams[pkt.stream_index]->codec;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                        }
    
                        if (c->is_packetized) {
                            int max_packet_size;
    
                            if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP)
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                                max_packet_size = RTSP_TCP_MAX_PACKET_SIZE;
                            else
    
                                max_packet_size = c->rtp_handles[c->packet_stream_index]->max_packet_size;
    
                            ret = ffio_open_dyn_packet_buf(&ctx->pb, max_packet_size);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                        } else {
    
                            ret = avio_open_dyn_buf(&ctx->pb);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                        }
                        if (ret < 0) {
                            /* XXX: potential leak */
                            return -1;
                        }
    
                        ost = ctx->streams[pkt.stream_index];
    
    
                        ctx->pb->seekable = 0;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                        if (pkt.dts != AV_NOPTS_VALUE)
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                            pkt.dts = av_rescale_q(pkt.dts, ist->time_base, ost->time_base);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                        if (pkt.pts != AV_NOPTS_VALUE)
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                            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);
    
                        if ((ret = av_write_frame(ctx, &pkt)) < 0) {
                            http_log("Error writing frame to output for stream '%s': %s\n",
                                     c->stream->filename, av_err2str(ret));
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                            c->state = HTTPSTATE_SEND_DATA_TRAILER;
    
                        len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                        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;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    }
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                    av_free_packet(&pkt);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            break;
        default:
        case HTTPSTATE_SEND_DATA_TRAILER:
            /* last packet test ? */
    
            if (c->last_packet_sent || c->is_packetized)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                return -1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* prepare header */
    
            if (avio_open_dyn_buf(&ctx->pb) < 0) {
    
            c->fmt_ctx.pb->seekable = 0;
    
            len = avio_close_dyn_buf(ctx->pb, &c->pb_buffer);
    
            c->buffer_ptr = c->pb_buffer;
            c->buffer_end = c->pb_buffer + len;
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            c->last_packet_sent = 1;
            break;
        }
        return 0;
    }
    
    /* should convert the format at the same time */
    
    /* send data starting at c->buffer_ptr to the output connection
    
     * (either UDP or TCP) */
    
    static int http_send_data(HTTPContext *c)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
        for(;;) {
            if (c->buffer_ptr >= c->buffer_end) {
                ret = http_prepare_data(c);
                if (ret < 0)
                    return -1;
    
                    /* state change requested */
                    break;
    
                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;
    
                    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;
    
                    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;
    
    
                    if (c->rtp_protocol == RTSP_LOWER_TRANSPORT_TCP) {
    
                        /* RTP packets are sent inside the RTSP TCP connection */
    
                        AVIOContext *pb;
    
                        int interleaved_index, size;
                        uint8_t header[4];
                        HTTPContext *rtsp_c;
    
                        rtsp_c = c->rtsp_c;
                        /* if no RTSP connection left, error */
                        if (!rtsp_c)
                            return -1;
                        /* if already sending something, then wait. */
    
                        if (rtsp_c->state != RTSPSTATE_WAIT_REQUEST)
    
                        if (avio_open_dyn_buf(&pb) < 0)
    
                            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;
    
                        avio_write(pb, header, 4);
    
                        /* write RTP packet data */
                        c->buffer_ptr += 4;
    
                        avio_write(pb, c->buffer_ptr, len);
    
                        size = avio_close_dyn_buf(pb, &c->packet_buffer);
    
                        /* prepare asynchronous TCP sending */
                        rtsp_c->packet_buffer_ptr = c->packet_buffer;
                        rtsp_c->packet_buffer_end = c->packet_buffer + size;
    
                        len = send(rtsp_c->fd, rtsp_c->packet_buffer_ptr,
                                    rtsp_c->packet_buffer_end - rtsp_c->packet_buffer_ptr, 0);
    
                            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;
    
                            /* all data has been sent */
                            av_freep(&c->packet_buffer);
                    } else {
                        /* send RTP packet directly in UDP */
    
                        c->buffer_ptr += 4;
    
                        ffurl_write(c->rtp_handles[c->packet_stream_index],
                                    c->buffer_ptr, len);
    
                        c->buffer_ptr += len;
                        /* here we continue as we can send several packets per 10 ms slot */
    
                    }
                } else {
                    /* TCP data output */
    
                    len = send(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr, 0);
    
                    if (len < 0) {
    
                        if (ff_neterrno() != AVERROR(EAGAIN) &&
                            ff_neterrno() != AVERROR(EINTR))
    
                            /* error : close connection */
                            return -1;
    
                            return 0;
    
                        c->buffer_ptr += len;
    
                    c->data_count += len;
                    update_datarate(&c->datarate, c->data_count);
                    if (c->stream)
                        c->stream->bytes_served += len;
                    break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            }
    
        } /* for(;;) */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        return 0;
    }
    
    static int http_start_receive_data(HTTPContext *c)
    {
        int fd;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
        if (c->stream->feed_opened) {
            http_log("Stream feed '%s' was not opened\n", c->stream->feed_filename);
            return AVERROR(EINVAL);
        }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
        if (c->stream->readonly) {
            http_log("Cannot write to read-only file '%s'\n", c->stream->feed_filename);
            return AVERROR(EINVAL);
        }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        /* open feed */
        fd = open(c->stream->feed_filename, O_RDWR);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
        if (fd < 0) {
    
            http_log("Could not open feed file '%s': %s\n",
    
                     c->stream->feed_filename, strerror(errno));
            return ret;
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
        }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        c->feed_fd = fd;
    
        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);
    
            if (ftruncate(c->feed_fd, FFM_PACKET_SIZE) < 0) {
    
                ret = AVERROR(errno);
                http_log("Error truncating feed file '%s': %s\n",
                         c->stream->feed_filename, strerror(errno));
                return ret;
    
            ret = ffm_read_write_index(fd);
            if (ret < 0) {
                http_log("Error reading write index from feed file '%s': %s\n",
                         c->stream->feed_filename, strerror(errno));
                return ret;
            } else {
                c->stream->feed_write_index = ret;
    
        c->stream->feed_write_index = FFMAX(ffm_read_write_index(fd), FFM_PACKET_SIZE);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        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;
    
        c->chunked_encoding = !!av_stristr(c->buffer, "Transfer-Encoding: chunked");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        return 0;
    }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    static int http_receive_data(HTTPContext *c)
    {
        HTTPContext *c1;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
    
        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) {
    
                if (ff_neterrno() != AVERROR(EAGAIN) &&
                    ff_neterrno() != AVERROR(EINTR))
    
            } 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++;
            }
        }
    
        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);
    
                if (ff_neterrno() != AVERROR(EAGAIN) &&
                    ff_neterrno() != AVERROR(EINTR))
    
                /* end of connection : close it */
                goto fail;
    
                c->buffer_ptr += len;
                c->data_count += len;
    
                update_datarate(&c->datarate, c->data_count);
    
        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;
            }
        }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        if (c->buffer_ptr >= c->buffer_end) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            /* a packet has been received : write it in the store, except
               if header */
            if (c->data_count > FFM_PACKET_SIZE) {
    
                /* XXX: use llseek or url_seek
                 * XXX: Should probably fail? */
                if (lseek(c->feed_fd, feed->feed_write_index, SEEK_SET) == -1)
                    http_log("Seek to %"PRId64" failed\n", feed->feed_write_index);
    
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                if (write(c->feed_fd, c->buffer, FFM_PACKET_SIZE) < 0) {
                    http_log("Error writing to feed file: %s\n", strerror(errno));
                    goto fail;
                }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                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 */
    
                if (c->stream->feed_max_size && feed->feed_write_index >= c->stream->feed_max_size)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    feed->feed_write_index = FFM_PACKET_SIZE;
    
                /* write index */
    
                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;
                }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    
                /* wake up any waiting connections */
                for(c1 = first_http_ctx; c1 != NULL; c1 = c1->next) {
    
                    if (c1->state == HTTPSTATE_WAIT_FEED &&
    
                        c1->stream->feed == c->stream->feed)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                        c1->state = HTTPSTATE_SEND_DATA;
                }
    
            } else {
                /* We have a header in our hands that contains useful data */
    
                AVFormatContext *s = avformat_alloc_context();
    
                AVIOContext *pb;
    
                AVInputFormat *fmt_in;
    
                /* use feed output format name to find corresponding input format */
                fmt_in = av_find_input_format(feed->fmt->name);
                if (!fmt_in)
                    goto fail;
    
    
                pb = avio_alloc_context(c->buffer, c->buffer_end - c->buffer,
                                        0, NULL, NULL, NULL, NULL);
    
                pb->seekable = 0;
    
                s->pb = pb;
                if (avformat_open_input(&s, c->stream->feed_filename, fmt_in, NULL) < 0) {
    
                    av_free(pb);
                    goto fail;
                }
    
                if (s->nb_streams != feed->nb_streams) {
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                    av_free(pb);
    
                    http_log("Feed '%s' stream number does not match registered feed\n",
                             c->stream->feed_filename);
    
                for (i = 0; i < s->nb_streams; i++) {
                    AVStream *fst = feed->streams[i];
                    AVStream *st = s->streams[i];
    
                    avcodec_copy_context(fst->codec, st->codec);
    
    Baptiste Coudurier's avatar
    Baptiste Coudurier committed
                av_free(pb);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            }
            c->buffer_ptr = c->buffer;
        }
    
        return 0;
     fail:
        c->stream->feed_opened = 0;
        close(c->feed_fd);
    
        /* 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;
        }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        return -1;
    }
    
    
    /********************************************************************/
    /* RTSP handling */
    
    static void rtsp_reply_header(HTTPContext *c, enum RTSPStatusCode error_number)
    {
        const char *str;
        time_t ti;
    
        str = RTSP_STATUS_CODE2STRING(error_number);
        if (!str)
    
        avio_printf(c->pb, "RTSP/1.0 %d %s\r\n", error_number, str);
        avio_printf(c->pb, "CSeq: %d\r\n", c->seq);
    
        tm = gmtime(&ti);
        strftime(buf2, sizeof(buf2), "%a, %d %b %Y %H:%M:%S", tm);
    
        avio_printf(c->pb, "Date: %s GMT\r\n", buf2);
    
    }
    
    static void rtsp_reply_error(HTTPContext *c, enum RTSPStatusCode error_number)
    {
        rtsp_reply_header(c, error_number);
    
        avio_printf(c->pb, "\r\n");
    
    }
    
    static int rtsp_parse_request(HTTPContext *c)
    {
        const char *p, *p1, *p2;
        char cmd[32];
        char url[1024];
        char protocol[32];
        char line[1024];
        int len;
    
        RTSPMessageHeader header1 = { 0 }, *header = &header1;
    
        get_word(cmd, sizeof(cmd), &p);
        get_word(url, sizeof(url), &p);
        get_word(protocol, sizeof(protocol), &p);
    
    
        av_strlcpy(c->method, cmd, sizeof(c->method));
        av_strlcpy(c->url, url, sizeof(c->url));
        av_strlcpy(c->protocol, protocol, sizeof(c->protocol));
    
        if (avio_open_dyn_buf(&c->pb) < 0) {
    
            /* 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') {
    
            p1 = memchr(p, '\n', (char *)c->buffer_ptr - p);
    
            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';
    
            ff_rtsp_parse_line(header, line, NULL, NULL);
    
        if (!strcmp(cmd, "DESCRIBE"))
    
        else if (!strcmp(cmd, "OPTIONS"))
    
        else if (!strcmp(cmd, "SETUP"))
    
        else if (!strcmp(cmd, "PLAY"))
    
        else if (!strcmp(cmd, "PAUSE"))
    
            rtsp_cmd_interrupt(c, url, header, 1);
    
        else if (!strcmp(cmd, "TEARDOWN"))
    
            rtsp_cmd_interrupt(c, url, header, 0);
    
        len = avio_close_dyn_buf(c->pb, &c->pb_buffer);
    
        c->pb = NULL; /* safety */
        if (len < 0) {
            /* XXX: cannot do more */
            return -1;
        }
        c->buffer_ptr = c->pb_buffer;
        c->buffer_end = c->pb_buffer + len;
        c->state = RTSPSTATE_SEND_REPLY;
        return 0;
    }
    
    
    static int prepare_sdp_description(FFStream *stream, uint8_t **pbuffer,
    
        AVFormatContext *avc;
    
        AVOutputFormat *rtp_format = av_guess_format("rtp", NULL, NULL);
    
        AVDictionaryEntry *entry = av_dict_get(stream->metadata, "title", NULL, 0);
    
        avc->oformat = rtp_format;
    
        av_dict_set(&avc->metadata, "title",
    
                    entry ? entry->value : "No Title", 0);
    
        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);
    
        } else {
            snprintf(avc->filename, 1024, "rtp://0.0.0.0");
    
        if (avc->nb_streams >= INT_MAX/sizeof(*avc->streams) ||
            !(avc->streams = av_malloc(avc->nb_streams * sizeof(*avc->streams))))
            goto sdp_done;