Skip to content
Snippets Groups Projects
ffserver.c 66.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Fabrice Bellard's avatar
    Fabrice Bellard committed
            }
        }
        if (input_filename[0] == '\0')
            return -1;
    
        /* open stream */
    
        if (av_open_input_file(&s, input_filename, NULL, buf_size, NULL) < 0)
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            return -1;
        c->fmt_in = s;
    
    
        if (c->fmt_in->iformat->read_seek) {
            c->fmt_in->iformat->read_seek(c->fmt_in, stream_pos);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
        
        //    printf("stream %s opened pos=%0.6f\n", input_filename, stream_pos / 1000000.0);
        return 0;
    }
    
    static int http_prepare_data(HTTPContext *c)
    {
        int i;
    
        switch(c->state) {
        case HTTPSTATE_SEND_DATA_HEADER:
            memset(&c->fmt_ctx, 0, sizeof(c->fmt_ctx));
            if (c->stream->feed) {
                /* open output stream by using specified codecs */
    
                c->fmt_ctx.oformat = c->stream->fmt;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                c->fmt_ctx.nb_streams = c->stream->nb_streams;
                for(i=0;i<c->fmt_ctx.nb_streams;i++) {
                    AVStream *st;
                    st = av_mallocz(sizeof(AVStream));
                    c->fmt_ctx.streams[i] = st;
    
                    if (c->stream->feed == c->stream)
                        memcpy(st, c->stream->streams[i], sizeof(AVStream));
                    else
                        memcpy(st, c->stream->feed->streams[c->stream->feed_streams[i]], sizeof(AVStream));
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    st->codec.frame_number = 0; /* XXX: should be done in
                                                   AVStream, not in codec */
                }
    
                c->got_key_frame = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else {
                /* open output stream by using codecs in specified file */
    
                c->fmt_ctx.oformat = c->stream->fmt;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                c->fmt_ctx.nb_streams = c->fmt_in->nb_streams;
                for(i=0;i<c->fmt_ctx.nb_streams;i++) {
                    AVStream *st;
                    st = av_mallocz(sizeof(AVStream));
                    c->fmt_ctx.streams[i] = st;
                    memcpy(st, c->fmt_in->streams[i], sizeof(AVStream));
                    st->codec.frame_number = 0; /* XXX: should be done in
                                                   AVStream, not in codec */
                }
    
                c->got_key_frame = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            }
    
            init_put_byte(&c->fmt_ctx.pb, c->pbuffer, PACKET_MAX_SIZE,
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                          1, c, NULL, http_write_packet, NULL);
            c->fmt_ctx.pb.is_streamed = 1;
            /* prepare header */
    
            av_write_header(&c->fmt_ctx);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            c->state = HTTPSTATE_SEND_DATA;
            c->last_packet_sent = 0;
            break;
        case HTTPSTATE_SEND_DATA:
            /* find a new packet */
    #if 0
            fifo_total_size = http_fifo_write_count - c->last_http_fifo_write_count;
            if (fifo_total_size >= ((3 * FIFO_MAX_SIZE) / 4)) {
                /* overflow : resync. We suppose that wptr is at this
                   point a pointer to a valid packet */
                c->rptr = http_fifo.wptr;
    
                c->got_key_frame = 0;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            }
            
            start_rptr = c->rptr;
            if (fifo_read(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &c->rptr) < 0)
                return 0;
            payload_size = ntohs(hdr.payload_size);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            payload = av_malloc(payload_size);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            if (fifo_read(&http_fifo, payload, payload_size, &c->rptr) < 0) {
                /* cannot read all the payload */
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                av_free(payload);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                c->rptr = start_rptr;
                return 0;
            }
            
            c->last_http_fifo_write_count = http_fifo_write_count - 
                fifo_size(&http_fifo, c->rptr);
            
            if (c->stream->stream_type != STREAM_TYPE_MASTER) {
                /* test if the packet can be handled by this format */
                ret = 0;
                for(i=0;i<c->fmt_ctx.nb_streams;i++) {
                    AVStream *st = c->fmt_ctx.streams[i];
                    if (test_header(&hdr, &st->codec)) {
                        /* only begin sending when got a key frame */
                        if (st->codec.key_frame)
    
                            c->got_key_frame |= 1 << i;
                        if (c->got_key_frame & (1 << i)) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                            ret = c->fmt_ctx.format->write_packet(&c->fmt_ctx, i,
                                                                       payload, payload_size);
                        }
                        break;
                    }
                }
                if (ret) {
                    /* must send trailer now */
                    c->state = HTTPSTATE_SEND_DATA_TRAILER;
                }
            } else {
                /* master case : send everything */
                char *q;
                q = c->buffer;
                memcpy(q, &hdr, sizeof(hdr));
                q += sizeof(hdr);
                memcpy(q, payload, payload_size);
                q += payload_size;
                c->buffer_ptr = c->buffer;
                c->buffer_end = q;
            }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            av_free(payload);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    #endif
            {
                AVPacket pkt;
    
                /* 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 > time(0)) {
                    /* We have timed out */
                    c->state = HTTPSTATE_SEND_DATA_TRAILER;
                } else if (av_read_packet(c->fmt_in, &pkt) < 0) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    if (c->stream->feed && c->stream->feed->feed_opened) {
                        /* if coming from feed, it means we reached the end of the
                           ffm file, so must wait for more data */
                        c->state = HTTPSTATE_WAIT_FEED;
                        return 1; /* state changed */
                    } else {
                        /* must send trailer now because eof or error */
                        c->state = HTTPSTATE_SEND_DATA_TRAILER;
                    }
                } else {
                    /* send it to the appropriate stream */
                    if (c->stream->feed) {
                        /* if coming from a feed, select the right stream */
                        for(i=0;i<c->stream->nb_streams;i++) {
                            if (c->stream->feed_streams[i] == pkt.stream_index) {
                                pkt.stream_index = i;
    
                                if (pkt.flags & PKT_FLAG_KEY) {
                                    c->got_key_frame |= 1 << i;
                                }
                                /* See if we have all the key frames, then 
                                 * we start to send. This logic is not quite
                                 * right, but it works for the case of a 
                                 * single video stream with one or more
                                 * audio streams (for which every frame is 
                                 * typically a key frame). 
                                 */
    
                                if (!c->stream->send_on_key || ((c->got_key_frame + 1) >> c->stream->nb_streams)) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                            }
                        }
                    } else {
    
                        AVCodecContext *codec;
                    send_it:
                        /* Fudge here */
                        codec = &c->fmt_ctx.streams[pkt.stream_index]->codec;
    
                        codec->key_frame = ((pkt.flags & PKT_FLAG_KEY) != 0);
    
    #ifdef PJSG
                        if (codec->codec_type == CODEC_TYPE_AUDIO) {
                            codec->frame_size = (codec->sample_rate * pkt.duration + 500000) / 1000000;
                            /* printf("Calculated size %d, from sr %d, duration %d\n", codec->frame_size, codec->sample_rate, pkt.duration); */
                        }
    #endif
    
    
                        if (av_write_packet(&c->fmt_ctx, &pkt, 0))
    
                            c->state = HTTPSTATE_SEND_DATA_TRAILER;
    
                        codec->frame_number++;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    av_free_packet(&pkt);
                }
            }
            break;
        default:
        case HTTPSTATE_SEND_DATA_TRAILER:
            /* last packet test ? */
            if (c->last_packet_sent)
                return -1;
            /* prepare header */
    
            av_write_trailer(&c->fmt_ctx);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            c->last_packet_sent = 1;
            break;
        }
        return 0;
    }
    
    /* should convert the format at the same time */
    static int http_send_data(HTTPContext *c)
    {
        int len, ret;
    
        while (c->buffer_ptr >= c->buffer_end) {
            ret = http_prepare_data(c);
            if (ret < 0)
                return -1;
            else if (ret == 0) {
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else {
                /* state change requested */
                return 0;
            }
        }
    
    
        if (c->buffer_end > c->buffer_ptr) {
            len = write(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
            if (len < 0) {
                if (errno != EAGAIN && errno != EINTR) {
                    /* error : close connection */
                    return -1;
                }
            } else {
                c->buffer_ptr += len;
                c->data_count += len;
    
                if (c->stream)
                    c->stream->bytes_served += len;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            }
        }
        return 0;
    }
    
    static int http_start_receive_data(HTTPContext *c)
    {
        int fd;
    
        if (c->stream->feed_opened)
            return -1;
    
        /* open feed */
        fd = open(c->stream->feed_filename, O_RDWR);
        if (fd < 0)
            return -1;
        c->feed_fd = fd;
        
        c->stream->feed_write_index = ffm_read_write_index(fd);
        c->stream->feed_size = lseek(fd, 0, SEEK_END);
        lseek(fd, 0, SEEK_SET);
    
        /* init buffer input */
        c->buffer_ptr = c->buffer;
        c->buffer_end = c->buffer + FFM_PACKET_SIZE;
        c->stream->feed_opened = 1;
        return 0;
    }
        
    static int http_receive_data(HTTPContext *c)
    {
        HTTPContext *c1;
    
    
        if (c->buffer_end > c->buffer_ptr) {
            int len;
    
            len = read(c->fd, c->buffer_ptr, c->buffer_end - c->buffer_ptr);
            if (len < 0) {
                if (errno != EAGAIN && errno != EINTR) {
                    /* error : close connection */
                    goto fail;
                }
            } else if (len == 0) {
                /* end of connection : close it */
                goto fail;
            } else {
                c->buffer_ptr += len;
                c->data_count += len;
            }
        }
    
    
    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) {
                
                //            printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size);
                /* XXX: use llseek or url_seek */
                lseek(c->feed_fd, feed->feed_write_index, SEEK_SET);
                write(c->feed_fd, c->buffer, FFM_PACKET_SIZE);
                
                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 (feed->feed_write_index >= c->stream->feed_max_size)
                    feed->feed_write_index = FFM_PACKET_SIZE;
    
                /* write index */
                ffm_write_write_index(c->feed_fd, feed->feed_write_index);
    
                /* 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) {
                        c1->state = HTTPSTATE_SEND_DATA;
                    }
                }
    
            } else {
                /* We have a header in our hands that contains useful data */
                AVFormatContext s;
    
                AVInputFormat *fmt_in;
    
                ByteIOContext *pb = &s.pb;
                int i;
    
                memset(&s, 0, sizeof(s));
    
                url_open_buf(pb, c->buffer, c->buffer_end - c->buffer, URL_RDONLY);
                pb->buf_end = c->buffer_end;        /* ?? */
                pb->is_streamed = 1;
    
    
                /* use feed output format name to find corresponding input format */
                fmt_in = av_find_input_format(feed->fmt->name);
                if (!fmt_in)
                    goto fail;
    
    
                s.priv_data = av_mallocz(fmt_in->priv_data_size);
                if (!s.priv_data)
                    goto fail;
    
    
                if (fmt_in->read_header(&s, 0) < 0) {
    
                    goto fail;
                }
    
                /* Now we have the actual streams */
                if (s.nb_streams != feed->nb_streams) {
    
                    memcpy(&feed->streams[i]->codec, 
                           &s.streams[i]->codec, sizeof(AVCodecContext));
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            }
            c->buffer_ptr = c->buffer;
        }
    
        return 0;
     fail:
        c->stream->feed_opened = 0;
        close(c->feed_fd);
        return -1;
    }
    
    /* return the stream number in the feed */
    int add_av_stream(FFStream *feed,
                      AVStream *st)
    {
        AVStream *fst;
        AVCodecContext *av, *av1;
        int i;
    
        av = &st->codec;
        for(i=0;i<feed->nb_streams;i++) {
            st = feed->streams[i];
            av1 = &st->codec;
    
            if (av1->codec_id == av->codec_id &&
                av1->codec_type == av->codec_type &&
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                av1->bit_rate == av->bit_rate) {
    
                switch(av->codec_type) {
                case CODEC_TYPE_AUDIO:
                    if (av1->channels == av->channels &&
                        av1->sample_rate == av->sample_rate)
                        goto found;
                    break;
                case CODEC_TYPE_VIDEO:
                    if (av1->width == av->width &&
                        av1->height == av->height &&
                        av1->frame_rate == av->frame_rate &&
                        av1->gop_size == av->gop_size)
                        goto found;
                    break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                }
            }
        }
        
        fst = av_mallocz(sizeof(AVStream));
        if (!fst)
            return -1;
        fst->priv_data = av_mallocz(sizeof(FeedData));
        memcpy(&fst->codec, av, sizeof(AVCodecContext));
        feed->streams[feed->nb_streams++] = fst;
        return feed->nb_streams - 1;
     found:
        return i;
    }
    
    /* compute the needed AVStream for each feed */
    void build_feed_streams(void)
    {
        FFStream *stream, *feed;
        int i;
    
        /* gather all streams */
        for(stream = first_stream; stream != NULL; stream = stream->next) {
            feed = stream->feed;
            if (feed) {
                if (!stream->is_feed) {
                    for(i=0;i<stream->nb_streams;i++) {
                        stream->feed_streams[i] = add_av_stream(feed, stream->streams[i]);
                    }
                } else {
                    for(i=0;i<stream->nb_streams;i++) {
                        stream->feed_streams[i] = i;
                    }
                }
            }
        }
    
        /* create feed files if needed */
        for(feed = first_feed; feed != NULL; feed = feed->next_feed) {
            int fd;
    
            if (!url_exist(feed->feed_filename)) {
                AVFormatContext s1, *s = &s1;
    
                /* only write the header of the ffm file */
                if (url_fopen(&s->pb, feed->feed_filename, URL_WRONLY) < 0) {
                    fprintf(stderr, "Could not open output feed file '%s'\n",
                            feed->feed_filename);
                    exit(1);
                }
    
                s->oformat = feed->fmt;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                s->nb_streams = feed->nb_streams;
                for(i=0;i<s->nb_streams;i++) {
                    AVStream *st;
                    st = feed->streams[i];
                    s->streams[i] = st;
                }
    
                av_write_header(s);
                /* XXX: need better api */
                av_freep(&s->priv_data);
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                url_fclose(&s->pb);
            }
            /* get feed size and write index */
            fd = open(feed->feed_filename, O_RDONLY);
            if (fd < 0) {
                fprintf(stderr, "Could not open output feed file '%s'\n",
                        feed->feed_filename);
                exit(1);
            }
    
            feed->feed_write_index = ffm_read_write_index(fd);
            feed->feed_size = lseek(fd, 0, SEEK_END);
            /* ensure that we do not wrap before the end of file */
            if (feed->feed_max_size < feed->feed_size)
                feed->feed_max_size = feed->feed_size;
    
            close(fd);
        }
    }
    
    static void get_arg(char *buf, int buf_size, const char **pp)
    {
        const char *p;
        char *q;
        int quote;
    
        p = *pp;
        while (isspace(*p)) p++;
        q = buf;
        quote = 0;
        if (*p == '\"' || *p == '\'')
            quote = *p++;
        for(;;) {
            if (quote) {
                if (*p == quote)
                    break;
            } else {
                if (isspace(*p))
                    break;
            }
            if (*p == '\0')
                break;
            if ((q - buf) < buf_size - 1)
                *q++ = *p;
            p++;
        }
        *q = '\0';
        if (quote && *p == quote)
            p++;
        *pp = p;
    }
    
    /* add a codec and set the default parameters */
    void add_codec(FFStream *stream, AVCodecContext *av)
    {
        AVStream *st;
    
        /* compute default parameters */
        switch(av->codec_type) {
        case CODEC_TYPE_AUDIO:
            if (av->bit_rate == 0)
                av->bit_rate = 64000;
            if (av->sample_rate == 0)
                av->sample_rate = 22050;
            if (av->channels == 0)
                av->channels = 1;
            break;
        case CODEC_TYPE_VIDEO:
            if (av->bit_rate == 0)
                av->bit_rate = 64000;
            if (av->frame_rate == 0)
                av->frame_rate = 5 * FRAME_RATE_BASE;
            if (av->width == 0 || av->height == 0) {
                av->width = 160;
                av->height = 128;
            }
    
            /* Bitrate tolerance is less for streaming */
    
            if (av->bit_rate_tolerance == 0)
                av->bit_rate_tolerance = av->bit_rate / 4;
            if (av->qmin == 0)
                av->qmin = 3;
            if (av->qmax == 0)
                av->qmax = 31;
            if (av->max_qdiff == 0)
                av->max_qdiff = 3;
    
            av->qcompress = 0.5;
            av->qblur = 0.5;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            break;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        }
    
        st = av_mallocz(sizeof(AVStream));
        if (!st)
            return;
        stream->streams[stream->nb_streams++] = st;
        memcpy(&st->codec, av, sizeof(AVCodecContext));
    }
    
    
    int opt_audio_codec(const char *arg)
    {
        AVCodec *p;
    
        p = first_avcodec;
        while (p) {
            if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_AUDIO)
                break;
            p = p->next;
        }
        if (p == NULL) {
            return CODEC_ID_NONE;
        }
    
        return p->id;
    }
    
    int opt_video_codec(const char *arg)
    {
        AVCodec *p;
    
        p = first_avcodec;
        while (p) {
            if (!strcmp(p->name, arg) && p->type == CODEC_TYPE_VIDEO)
                break;
            p = p->next;
        }
        if (p == NULL) {
            return CODEC_ID_NONE;
        }
    
        return p->id;
    }
    
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
    int parse_ffconfig(const char *filename)
    {
        FILE *f;
        char line[1024];
        char cmd[64];
        char arg[1024];
        const char *p;
        int val, errors, line_num;
        FFStream **last_stream, *stream;
        FFStream **last_feed, *feed;
        AVCodecContext audio_enc, video_enc;
        int audio_id, video_id;
    
        f = fopen(filename, "r");
        if (!f) {
            perror(filename);
            return -1;
        }
        
        errors = 0;
        line_num = 0;
        first_stream = NULL;
        last_stream = &first_stream;
        first_feed = NULL;
        last_feed = &first_feed;
        stream = NULL;
        feed = NULL;
        audio_id = CODEC_ID_NONE;
        video_id = CODEC_ID_NONE;
        for(;;) {
            if (fgets(line, sizeof(line), f) == NULL)
                break;
            line_num++;
            p = line;
            while (isspace(*p)) 
                p++;
            if (*p == '\0' || *p == '#')
                continue;
    
            get_arg(cmd, sizeof(cmd), &p);
            
            if (!strcasecmp(cmd, "Port")) {
                get_arg(arg, sizeof(arg), &p);
                my_addr.sin_port = htons (atoi(arg));
            } else if (!strcasecmp(cmd, "BindAddress")) {
                get_arg(arg, sizeof(arg), &p);
                if (!inet_aton(arg, &my_addr.sin_addr)) {
                    fprintf(stderr, "%s:%d: Invalid IP address: %s\n", 
                            filename, line_num, arg);
                    errors++;
                }
            } else if (!strcasecmp(cmd, "MaxClients")) {
                get_arg(arg, sizeof(arg), &p);
                val = atoi(arg);
                if (val < 1 || val > HTTP_MAX_CONNECTIONS) {
                    fprintf(stderr, "%s:%d: Invalid MaxClients: %s\n", 
                            filename, line_num, arg);
                    errors++;
                } else {
                    nb_max_connections = val;
                }
    
            } else if (!strcasecmp(cmd, "MaxBandwidth")) {
                get_arg(arg, sizeof(arg), &p);
                val = atoi(arg);
                if (val < 10 || val > 100000) {
                    fprintf(stderr, "%s:%d: Invalid MaxBandwidth: %s\n", 
                            filename, line_num, arg);
                    errors++;
                } else {
                    nb_max_bandwidth = val;
                }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else if (!strcasecmp(cmd, "CustomLog")) {
                get_arg(logfilename, sizeof(logfilename), &p);
            } else if (!strcasecmp(cmd, "<Feed")) {
                /*********************************************/
                /* Feed related options */
                char *q;
                if (stream || feed) {
                    fprintf(stderr, "%s:%d: Already in a tag\n",
                            filename, line_num);
                } else {
                    feed = av_mallocz(sizeof(FFStream));
                    /* add in stream list */
                    *last_stream = feed;
                    last_stream = &feed->next;
                    /* add in feed list */
                    *last_feed = feed;
                    last_feed = &feed->next_feed;
                    
                    get_arg(feed->filename, sizeof(feed->filename), &p);
                    q = strrchr(feed->filename, '>');
                    if (*q)
                        *q = '\0';
                    feed->fmt = guess_format("ffm", NULL, NULL);
                    /* defaut feed file */
                    snprintf(feed->feed_filename, sizeof(feed->feed_filename),
                             "/tmp/%s.ffm", feed->filename);
                    feed->feed_max_size = 5 * 1024 * 1024;
                    feed->is_feed = 1;
                    feed->feed = feed; /* self feeding :-) */
                }
            } else if (!strcasecmp(cmd, "File")) {
                if (feed) {
                    get_arg(feed->feed_filename, sizeof(feed->feed_filename), &p);
                } else if (stream) {
                    get_arg(stream->feed_filename, sizeof(stream->feed_filename), &p);
                }
            } else if (!strcasecmp(cmd, "FileMaxSize")) {
                if (feed) {
                    const char *p1;
                    double fsize;
    
                    get_arg(arg, sizeof(arg), &p);
                    p1 = arg;
                    fsize = strtod(p1, (char **)&p1);
                    switch(toupper(*p1)) {
                    case 'K':
                        fsize *= 1024;
                        break;
                    case 'M':
                        fsize *= 1024 * 1024;
                        break;
                    case 'G':
                        fsize *= 1024 * 1024 * 1024;
                        break;
                    }
                    feed->feed_max_size = (INT64)fsize;
                }
            } else if (!strcasecmp(cmd, "</Feed>")) {
                if (!feed) {
                    fprintf(stderr, "%s:%d: No corresponding <Feed> for </Feed>\n",
                            filename, line_num);
                    errors++;
    
                } else {
                    /* Make sure that we start out clean */
    
                    if (unlink(feed->feed_filename) < 0 
                        && errno != ENOENT) {
                        fprintf(stderr, "%s:%d: Unable to clean old feed file '%s': %s\n",
                            filename, line_num, feed->feed_filename, strerror(errno));
                        errors++;
                    }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                }
                feed = NULL;
            } else if (!strcasecmp(cmd, "<Stream")) {
                /*********************************************/
                /* Stream related options */
                char *q;
                if (stream || feed) {
                    fprintf(stderr, "%s:%d: Already in a tag\n",
                            filename, line_num);
                } else {
                    stream = av_mallocz(sizeof(FFStream));
                    *last_stream = stream;
                    last_stream = &stream->next;
    
                    get_arg(stream->filename, sizeof(stream->filename), &p);
                    q = strrchr(stream->filename, '>');
                    if (*q)
                        *q = '\0';
                    stream->fmt = guess_format(NULL, stream->filename, NULL);
                    memset(&audio_enc, 0, sizeof(AVCodecContext));
                    memset(&video_enc, 0, sizeof(AVCodecContext));
                    audio_id = CODEC_ID_NONE;
                    video_id = CODEC_ID_NONE;
                    if (stream->fmt) {
                        audio_id = stream->fmt->audio_codec;
                        video_id = stream->fmt->video_codec;
                    }
                }
            } else if (!strcasecmp(cmd, "Feed")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    FFStream *sfeed;
                    
                    sfeed = first_feed;
                    while (sfeed != NULL) {
                        if (!strcmp(sfeed->filename, arg))
                            break;
                        sfeed = sfeed->next_feed;
                    }
                    if (!sfeed) {
                        fprintf(stderr, "%s:%d: feed '%s' not defined\n",
                                filename, line_num, arg);
                    } else {
                        stream->feed = sfeed;
                    }
                }
            } else if (!strcasecmp(cmd, "Format")) {
                get_arg(arg, sizeof(arg), &p);
                if (!strcmp(arg, "status")) {
                    stream->stream_type = STREAM_TYPE_STATUS;
                    stream->fmt = NULL;
                } else {
                    stream->stream_type = STREAM_TYPE_LIVE;
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    /* jpeg cannot be used here, so use single frame jpeg */
                    if (!strcmp(arg, "jpeg"))
                        strcpy(arg, "singlejpeg");
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
                    stream->fmt = guess_format(arg, NULL, NULL);
                    if (!stream->fmt) {
                        fprintf(stderr, "%s:%d: Unknown Format: %s\n", 
                                filename, line_num, arg);
                        errors++;
                    }
                }
                if (stream->fmt) {
                    audio_id = stream->fmt->audio_codec;
                    video_id = stream->fmt->video_codec;
                }
    
            } else if (!strcasecmp(cmd, "Preroll")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    stream->prebuffer = atoi(arg) * 1000;
                }
    
            } else if (!strcasecmp(cmd, "StartSendOnKey")) {
                if (stream) {
                    stream->send_on_key = 1;
                }
    
            } else if (!strcasecmp(cmd, "AudioCodec")) {
                get_arg(arg, sizeof(arg), &p);
                audio_id = opt_audio_codec(arg);
                if (audio_id == CODEC_ID_NONE) {
                    fprintf(stderr, "%s:%d: Unknown AudioCodec: %s\n", 
                            filename, line_num, arg);
                    errors++;
                }
            } else if (!strcasecmp(cmd, "VideoCodec")) {
                get_arg(arg, sizeof(arg), &p);
                video_id = opt_video_codec(arg);
                if (video_id == CODEC_ID_NONE) {
                    fprintf(stderr, "%s:%d: Unknown VideoCodec: %s\n", 
                            filename, line_num, arg);
                    errors++;
                }
    
            } else if (!strcasecmp(cmd, "MaxTime")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    stream->max_time = atoi(arg);
                }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else if (!strcasecmp(cmd, "AudioBitRate")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    audio_enc.bit_rate = atoi(arg) * 1000;
                }
            } else if (!strcasecmp(cmd, "AudioChannels")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    audio_enc.channels = atoi(arg);
                }
            } else if (!strcasecmp(cmd, "AudioSampleRate")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    audio_enc.sample_rate = atoi(arg);
                }
            } else if (!strcasecmp(cmd, "VideoBitRate")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    video_enc.bit_rate = atoi(arg) * 1000;
                }
            } else if (!strcasecmp(cmd, "VideoSize")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    parse_image_size(&video_enc.width, &video_enc.height, arg);
                    if ((video_enc.width % 16) != 0 ||
                        (video_enc.height % 16) != 0) {
                        fprintf(stderr, "%s:%d: Image size must be a multiple of 16\n",
                                filename, line_num);
                        errors++;
                    }
                }
            } else if (!strcasecmp(cmd, "VideoFrameRate")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    video_enc.frame_rate = (int)(strtod(arg, NULL) * FRAME_RATE_BASE);
                }
            } else if (!strcasecmp(cmd, "VideoGopSize")) {
                get_arg(arg, sizeof(arg), &p);
                if (stream) {
                    video_enc.gop_size = atoi(arg);
                }
            } else if (!strcasecmp(cmd, "VideoIntraOnly")) {
                if (stream) {
                    video_enc.gop_size = 1;
                }
    
            } else if (!strcasecmp(cmd, "VideoHighQuality")) {
                if (stream) {
                    video_enc.flags |= CODEC_FLAG_HQ;
                }
    
            } else if (!strcasecmp(cmd, "VideoQDiff")) {
                if (stream) {
                    video_enc.max_qdiff = atoi(arg);
                    if (video_enc.max_qdiff < 1 || video_enc.max_qdiff > 31) {
                        fprintf(stderr, "%s:%d: VideoQDiff out of range\n",
                                filename, line_num);
                        errors++;
                    }
                }
            } else if (!strcasecmp(cmd, "VideoQMax")) {
                if (stream) {
                    video_enc.qmax = atoi(arg);
                    if (video_enc.qmax < 1 || video_enc.qmax > 31) {
                        fprintf(stderr, "%s:%d: VideoQMax out of range\n",
                                filename, line_num);
                        errors++;
                    }
                }
            } else if (!strcasecmp(cmd, "VideoQMin")) {
                if (stream) {
                    video_enc.qmin = atoi(arg);
                    if (video_enc.qmin < 1 || video_enc.qmin > 31) {
                        fprintf(stderr, "%s:%d: VideoQMin out of range\n",
                                filename, line_num);
                        errors++;
                    }
                }
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
            } else if (!strcasecmp(cmd, "NoVideo")) {
                video_id = CODEC_ID_NONE;
            } else if (!strcasecmp(cmd, "NoAudio")) {
                audio_id = CODEC_ID_NONE;
            } else if (!strcasecmp(cmd, "</Stream>")) {
                if (!stream) {
                    fprintf(stderr, "%s:%d: No corresponding <Stream> for </Stream>\n",
                            filename, line_num);
                    errors++;
                }
                if (stream->feed && stream->fmt && strcmp(stream->fmt->name, "ffm") != 0) {
                    if (audio_id != CODEC_ID_NONE) {
                        audio_enc.codec_type = CODEC_TYPE_AUDIO;
                        audio_enc.codec_id = audio_id;
                        add_codec(stream, &audio_enc);
                    }
                    if (video_id != CODEC_ID_NONE) {
                        video_enc.codec_type = CODEC_TYPE_VIDEO;
                        video_enc.codec_id = video_id;
                        add_codec(stream, &video_enc);
                    }
                }
                stream = NULL;
            } else {
                fprintf(stderr, "%s:%d: Incorrect keyword: '%s'\n", 
                        filename, line_num, cmd);
                errors++;
            }
        }
    
        fclose(f);
        if (errors)
            return -1;
        else
            return 0;
    }
    
    
    void *http_server_thread(void *arg)
    {
        http_server(my_addr);
        return NULL;
    }
    
    #if 0
    static void write_packet(FFCodec *ffenc,
                             UINT8 *buf, int size)
    {
        PacketHeader hdr;
        AVCodecContext *enc = &ffenc->enc;
        UINT8 *wptr;
        mk_header(&hdr, enc, size);
        wptr = http_fifo.wptr;
        fifo_write(&http_fifo, (UINT8 *)&hdr, sizeof(hdr), &wptr);
        fifo_write(&http_fifo, buf, size, &wptr);
        /* atomic modification of wptr */
        http_fifo.wptr = wptr;
        ffenc->data_count += size;
        ffenc->avg_frame_size = ffenc->avg_frame_size * AVG_COEF + size * (1.0 - AVG_COEF);
    }
    #endif
    
    void help(void)
    {
    
        printf("ffserver version " FFMPEG_VERSION ", Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
               "usage: ffserver [-L] [-h] [-f configfile]\n"
               "Hyper fast multi format Audio/Video streaming server\n"
               "\n"
               "-L            : print the LICENCE\n"
               "-h            : this help\n"
               "-f configfile : use configfile instead of /etc/ffserver.conf\n"
               );
    }
    
    void licence(void)
    {
        printf(
        "ffserver version " FFMPEG_VERSION "\n"
    
        "Copyright (c) 2000, 2001, 2002 Fabrice Bellard\n"
        "This library is free software; you can redistribute it and/or\n"
        "modify it under the terms of the GNU Lesser General Public\n"
        "License as published by the Free Software Foundation; either\n"
        "version 2 of the License, or (at your option) any later version.\n"
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        "\n"
    
        "This library is distributed in the hope that it will be useful,\n"
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
    
        "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n"
        "Lesser General Public License for more details.\n"
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        "\n"
    
        "You should have received a copy of the GNU Lesser General Public\n"
        "License along with this library; if not, write to the Free Software\n"
        "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\n"
    
    Fabrice Bellard's avatar
    Fabrice Bellard committed
        );
    }
    
    int main(int argc, char **argv)