From f747e6d343ee6ae5f2f5265194c7737448f3950a Mon Sep 17 00:00:00 2001 From: Philip Gladstone Date: Thu, 9 May 2002 01:11:08 +0000 Subject: [PATCH] * Fix a nasty problem with output buffering not have enough (or large enough) buffers. In fact, the code was pretty much shot. * Try to fool WMP into thinking that we are a microsoft server. * When we establish a stream to a user, copy the codec information from that saved as part of the stream. This gives us the real frame_size and other important parameters. * ASF needs to know about key frames, so add some logic to copy this information around. * When we get the data from ffmpeg as part of a feed, make sure that we save the actual codec parameters. * Allow configuration of AudioCodec and VideoCodec * Make sure that we delete the feed file before starting. This is not ideal but it makes things work a whole lot better! Originally committed as revision 454 to svn://svn.ffmpeg.org/ffmpeg/trunk --- ffserver.c | 158 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 136 insertions(+), 22 deletions(-) diff --git a/ffserver.c b/ffserver.c index 4704c51c75..40b1d8924b 100644 --- a/ffserver.c +++ b/ffserver.c @@ -63,7 +63,8 @@ const char *http_state[] = { "WAIT_FEED", }; -#define IOBUFFER_MAX_SIZE 16384 +#define IOBUFFER_MAX_SIZE 32768 +#define PACKET_MAX_SIZE 16384 /* coef for exponential mean for bitrate estimation in statistics */ #define AVG_COEF 0.9 @@ -79,7 +80,6 @@ typedef struct HTTPContext { struct sockaddr_in from_addr; /* origin */ struct pollfd *poll_entry; /* used when polling */ long timeout; - UINT8 buffer[IOBUFFER_MAX_SIZE]; UINT8 *buffer_ptr, *buffer_end; int http_error; struct HTTPContext *next; @@ -93,6 +93,8 @@ typedef struct HTTPContext { struct FFStream *stream; AVFormatContext fmt_ctx; int last_packet_sent; /* true if last data packet was sent */ + UINT8 buffer[IOBUFFER_MAX_SIZE]; + UINT8 pbuffer[PACKET_MAX_SIZE]; } HTTPContext; /* each generated stream is described here */ @@ -528,13 +530,14 @@ static int http_parse_request(HTTPContext *c) mime_type = c->stream->fmt->mime_type; if (!mime_type) mime_type = "application/x-octet_stream"; - q += sprintf(q, "Content-type: %s\r\n", mime_type); q += sprintf(q, "Pragma: no-cache\r\n"); /* for asf, we need extra headers */ if (!strcmp(c->stream->fmt->name,"asf")) { - q += sprintf(q, "Pragma: features=broadcast\r\n"); + q += sprintf(q, "Server: Cougar 4.1.0.3923\r\nCache-Control: no-cache\r\nPragma: client-id=1234\r\nPragma: features=\"broadcast\"\r\n"); + mime_type = "application/octet-stream"; } + q += sprintf(q, "Content-Type: %s\r\n", mime_type); q += sprintf(q, "\r\n"); /* prepare output buffer */ @@ -606,6 +609,8 @@ static void compute_stats(HTTPContext *c) case CODEC_TYPE_VIDEO: video_bit_rate += st->codec.bit_rate; break; + default: + abort(); } } q += sprintf(q, " %s %d %d %d", @@ -696,11 +701,15 @@ static void http_write_packet(void *opaque, unsigned char *buf, int size) { HTTPContext *c = opaque; - if (size > IOBUFFER_MAX_SIZE) + + if (c->buffer_ptr == c->buffer_end || !c->buffer_ptr) + c->buffer_ptr = c->buffer_end = c->buffer; + + if (c->buffer_end - c->buffer + size > IOBUFFER_MAX_SIZE) abort(); - memcpy(c->buffer, buf, size); - c->buffer_ptr = c->buffer; - c->buffer_end = c->buffer + size; + + memcpy(c->buffer_end, buf, size); + c->buffer_end += size; } static int open_input_stream(HTTPContext *c, const char *info) @@ -718,6 +727,9 @@ static int open_input_stream(HTTPContext *c, const char *info) /* compute position (absolute time) */ if (find_info_tag(buf, sizeof(buf), "date", info)) { stream_pos = parse_date(buf, 0); + } else if (find_info_tag(buf, sizeof(buf), "buffer", info)) { + int prebuffer = strtol(buf, 0, 10); + stream_pos = gettime() - prebuffer * 1000000; } else { stream_pos = gettime(); } @@ -763,7 +775,11 @@ static int http_prepare_data(HTTPContext *c) AVStream *st; st = av_mallocz(sizeof(AVStream)); c->fmt_ctx.streams[i] = st; - memcpy(st, c->stream->streams[i], sizeof(AVStream)); + 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)); + st->codec.frame_number = 0; /* XXX: should be done in AVStream, not in codec */ c->got_key_frame[i] = 0; @@ -782,7 +798,7 @@ static int http_prepare_data(HTTPContext *c) c->got_key_frame[i] = 0; } } - init_put_byte(&c->fmt_ctx.pb, c->buffer, IOBUFFER_MAX_SIZE, + init_put_byte(&c->fmt_ctx.pb, c->pbuffer, PACKET_MAX_SIZE, 1, c, NULL, http_write_packet, NULL); c->fmt_ctx.pb.is_streamed = 1; /* prepare header */ @@ -881,9 +897,24 @@ static int http_prepare_data(HTTPContext *c) } } } else { - send_it: + 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; + c->state = HTTPSTATE_SEND_DATA_TRAILER; + + codec->frame_number++; } av_free_packet(&pkt); @@ -920,15 +951,17 @@ static int http_send_data(HTTPContext *c) } } - 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; + 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; } - } else { - c->buffer_ptr += len; - c->data_count += len; } return 0; } @@ -963,10 +996,10 @@ static int http_receive_data(HTTPContext *c) HTTPContext *c1; if (c->buffer_ptr >= c->buffer_end) { + FFStream *feed = c->stream; /* a packet has been received : write it in the store, except if header */ if (c->data_count > FFM_PACKET_SIZE) { - FFStream *feed = c->stream; // printf("writing pos=0x%Lx size=0x%Lx\n", feed->feed_write_index, feed->feed_size); /* XXX: use llseek or url_seek */ @@ -992,6 +1025,29 @@ static int http_receive_data(HTTPContext *c) c1->state = HTTPSTATE_SEND_DATA; } } + } else { + /* We have a header in our hands that contains useful data */ + AVFormatContext s; + 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; + + if (feed->fmt->read_header(&s, 0) < 0) { + goto fail; + } + + /* Now we have the actual streams */ + if (s.nb_streams != feed->nb_streams) { + goto fail; + } + for (i = 0; i < s.nb_streams; i++) { + memcpy(&feed->streams[i]->codec, &s.streams[i]->codec, sizeof(AVCodecContext)); + } } c->buffer_ptr = c->buffer; } @@ -1028,7 +1084,8 @@ int add_av_stream(FFStream *feed, for(i=0;inb_streams;i++) { st = feed->streams[i]; av1 = &st->codec; - if (av1->codec == av->codec && + if (av1->codec_id == av->codec_id && + av1->codec_type == av->codec_type && av1->bit_rate == av->bit_rate) { switch(av->codec_type) { @@ -1044,6 +1101,8 @@ int add_av_stream(FFStream *feed, av1->gop_size == av->gop_size) goto found; break; + default: + abort(); } } } @@ -1187,6 +1246,8 @@ void add_codec(FFStream *stream, AVCodecContext *av) av->max_qdiff= 3; break; + default: + abort(); } st = av_mallocz(sizeof(AVStream)); @@ -1196,6 +1257,40 @@ void add_codec(FFStream *stream, AVCodecContext *av) 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; +} + int parse_ffconfig(const char *filename) { FILE *f; @@ -1319,6 +1414,9 @@ int parse_ffconfig(const char *filename) fprintf(stderr, "%s:%d: No corresponding for \n", filename, line_num); errors++; + } else { + /* Make sure that we start out clean */ + unlink(feed->feed_filename); } feed = NULL; } else if (!strcasecmp(cmd, "fmt->audio_codec; video_id = stream->fmt->video_codec; } + } 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, "AudioBitRate")) { get_arg(arg, sizeof(arg), &p); if (stream) {