Mercurial > libavformat.hg
changeset 542:be81a0f1974d libavformat
SCR timestamp fix try #1
author | michael |
---|---|
date | Sun, 03 Oct 2004 02:42:01 +0000 |
parents | 23a5448ade32 |
children | acf8a88674ff |
files | avformat.h mpeg.c utils.c |
diffstat | 3 files changed, 283 insertions(+), 69 deletions(-) [+] |
line wrap: on
line diff
--- a/avformat.h Fri Oct 01 20:05:04 2004 +0000 +++ b/avformat.h Sun Oct 03 02:42:01 2004 +0000 @@ -524,6 +524,7 @@ int fifo_size(FifoBuffer *f, uint8_t *rptr); int fifo_read(FifoBuffer *f, uint8_t *buf, int buf_size, uint8_t **rptr_ptr); void fifo_write(FifoBuffer *f, uint8_t *buf, int size, uint8_t **wptr_ptr); +int put_fifo(ByteIOContext *pb, FifoBuffer *f, int buf_size, uint8_t **rptr_ptr); /* media file input */ AVInputFormat *av_find_input_format(const char *short_name);
--- a/mpeg.c Fri Oct 01 20:05:04 2004 +0000 +++ b/mpeg.c Sun Oct 03 02:42:01 2004 +0000 @@ -19,18 +19,29 @@ #include "avformat.h" #define MAX_PAYLOAD_SIZE 4096 +#define PRELOAD 45000 //0.5sec //#define DEBUG_SEEK #undef NDEBUG #include <assert.h> +typedef struct PacketDesc { + int64_t pts; + int64_t dts; + int size; + int unwritten_size; + int flags; + struct PacketDesc *next; +} PacketDesc; + typedef struct { - uint8_t buffer[MAX_PAYLOAD_SIZE]; - int buffer_ptr; - int nb_frames; /* number of starting frame encountered (AC3) */ - int frame_start_offset; /* starting offset of the frame + 1 (0 if none) */ + FifoBuffer fifo; uint8_t id; int max_buffer_size; /* in bytes */ + int buffer_index; + PacketDesc *predecode_packet; + PacketDesc *premux_packet; + PacketDesc **next_packet; int packet_number; int64_t start_pts; int64_t start_dts; @@ -51,8 +62,6 @@ int is_mpeg2; int is_vcd; int is_svcd; - int scr_stream_index; /* stream from which the system clock is - computed (VBR case) */ int64_t last_scr; /* current system clock */ double vcd_padding_bitrate; //FIXME floats @@ -127,7 +136,7 @@ static int put_system_header(AVFormatContext *ctx, uint8_t *buf,int only_for_stream_id) { MpegMuxContext *s = ctx->priv_data; - int size, rate_bound, i, private_stream_coded, id; + int size, i, private_stream_coded, id; PutBitContext pb; init_put_bits(&pb, buf, 128); @@ -136,8 +145,7 @@ put_bits(&pb, 16, 0); put_bits(&pb, 1, 1); - rate_bound = s->mux_rate; /* maximum bit rate of the multiplexed stream */ - put_bits(&pb, 22, rate_bound); + put_bits(&pb, 22, s->mux_rate); /* maximum bit rate of the multiplexed stream */ put_bits(&pb, 1, 1); /* marker */ if (s->is_vcd && only_for_stream_id==VIDEO_ID) { /* This header applies only to the video stream (see VCD standard p. IV-7)*/ @@ -262,7 +270,6 @@ dts_id = DTS_ID; mpv_id = VIDEO_ID; lpcm_id = LPCM_ID; - s->scr_stream_index = -1; for(i=0;i<ctx->nb_streams;i++) { st = ctx->streams[i]; stream = av_mallocz(sizeof(StreamInfo)); @@ -300,9 +307,6 @@ s->audio_bound++; break; case CODEC_TYPE_VIDEO: - /* by default, video is used for the SCR computation */ - if (s->scr_stream_index == -1) - s->scr_stream_index = i; stream->id = mpv_id++; if (s->is_vcd) /* see VCD standard, p. IV-7*/ @@ -316,24 +320,31 @@ default: return -1; } + fifo_init(&stream->fifo, 2*stream->max_buffer_size + 100*MAX_PAYLOAD_SIZE); //FIXME think about the size maybe dynamically realloc + stream->next_packet= &stream->premux_packet; } - /* if no SCR, use first stream (audio) */ - if (s->scr_stream_index == -1) - s->scr_stream_index = 0; - bitrate = 0; audio_bitrate = 0; video_bitrate = 0; for(i=0;i<ctx->nb_streams;i++) { + int codec_rate; st = ctx->streams[i]; stream = (StreamInfo*) st->priv_data; - - bitrate += st->codec.bit_rate; + + if(st->codec.rc_max_rate || stream->id==VIDEO_ID) + codec_rate= st->codec.rc_max_rate; + else + codec_rate= st->codec.bit_rate; + + if(!codec_rate) + codec_rate= (1<<21)*8*50/ctx->nb_streams; + + bitrate += codec_rate; if (stream->id==AUDIO_ID) - audio_bitrate += st->codec.bit_rate; + audio_bitrate += codec_rate; else if (stream->id==VIDEO_ID) - video_bitrate += st->codec.bit_rate; + video_bitrate += codec_rate; } if (s->is_vcd) { @@ -367,7 +378,8 @@ } else { /* we increase slightly the bitrate to take into account the headers. XXX: compute it exactly */ - bitrate += 2000; + bitrate += bitrate*5/100; + bitrate += 10000; s->mux_rate = (bitrate + (8 * 50) - 1) / (8 * 50); } @@ -395,7 +407,6 @@ for(i=0;i<ctx->nb_streams;i++) { stream = ctx->streams[i]->priv_data; - stream->buffer_ptr = 0; stream->packet_number = 0; stream->start_pts = AV_NOPTS_VALUE; stream->start_dts = AV_NOPTS_VALUE; @@ -432,7 +443,7 @@ { int64_t full_pad_bytes; - full_pad_bytes = (int64_t)((s->vcd_padding_bitrate * (pts / 90000.0)) / 8.0); + full_pad_bytes = (int64_t)((s->vcd_padding_bitrate * (pts / 90000.0)) / 8.0); //FIXME this is wrong pad_bytes = (int) (full_pad_bytes - s->vcd_padding_bytes_written); if (pad_bytes<0) @@ -547,10 +558,23 @@ put_byte(pb, 0xff); } +static int get_nb_frames(AVFormatContext *ctx, StreamInfo *stream, int len){ + int nb_frames=0; + PacketDesc *pkt_desc= stream->premux_packet; + + while(len>0){ + if(pkt_desc->size == pkt_desc->unwritten_size) + nb_frames++; + len -= pkt_desc->unwritten_size; + pkt_desc= pkt_desc->next; + } + + return nb_frames; +} /* flush the packet on stream stream_index */ -static void flush_packet(AVFormatContext *ctx, int stream_index, - int64_t pts, int64_t dts, int64_t scr) +static int flush_packet(AVFormatContext *ctx, int stream_index, + int64_t pts, int64_t dts, int64_t scr, int trailer_size) { MpegMuxContext *s = ctx->priv_data; StreamInfo *stream = ctx->streams[stream_index]->priv_data; @@ -562,6 +586,7 @@ int pad_packet_bytes = 0; int pes_flags; int general_pack = 0; /*"general" pack without data specific to one stream?*/ + int nb_frames; id = stream->id; @@ -572,10 +597,11 @@ buf_ptr = buffer; - if (((s->packet_number % s->pack_header_freq) == 0)) { + if ((s->packet_number % s->pack_header_freq) == 0 || s->last_scr != scr) { /* output pack and systems header if needed */ size = put_pack_header(ctx, buf_ptr, scr); buf_ptr += size; + s->last_scr= scr; if (s->is_vcd) { /* there is exactly one system header for each stream in a VCD MPEG, @@ -652,7 +678,23 @@ startcode = 0x100 + id; } - stuffing_size = payload_size - stream->buffer_ptr; + stuffing_size = payload_size - fifo_size(&stream->fifo, stream->fifo.rptr); + + // first byte doesnt fit -> reset pts/dts + stuffing + if(payload_size <= trailer_size && pts != AV_NOPTS_VALUE){ + int timestamp_len=0; + if(dts != pts) + timestamp_len += 5; + if(pts != AV_NOPTS_VALUE) + timestamp_len += s->is_mpeg2 ? 5 : 4; + pts=dts= AV_NOPTS_VALUE; + header_len -= timestamp_len; + payload_size += timestamp_len; + stuffing_size += timestamp_len; + if(payload_size > trailer_size) + stuffing_size += payload_size - trailer_size; + } + if (stuffing_size < 0) stuffing_size = 0; if (stuffing_size > 16) { /*<=16 for MPEG-1, <=32 for MPEG-2*/ @@ -661,6 +703,8 @@ payload_size -= stuffing_size; stuffing_size = 0; } + + nb_frames= get_nb_frames(ctx, stream, payload_size - stuffing_size); put_be32(&ctx->pb, startcode); @@ -739,13 +783,17 @@ put_byte(&ctx->pb, stream->lpcm_header[2]); } else { /* AC3 */ - put_byte(&ctx->pb, stream->nb_frames); - put_be16(&ctx->pb, stream->frame_start_offset); + put_byte(&ctx->pb, nb_frames); + put_be16(&ctx->pb, trailer_size+1); } } /* output data */ - put_buffer(&ctx->pb, stream->buffer, payload_size - stuffing_size); + if(put_fifo(&ctx->pb, &stream->fifo, payload_size - stuffing_size, &stream->fifo.rptr) < 0) + return -1; + }else{ + payload_size= + stuffing_size= 0; } if (pad_packet_bytes > 0) @@ -763,8 +811,8 @@ or some data.*/ if (!general_pack) stream->packet_number++; - stream->nb_frames = 0; - stream->frame_start_offset = 0; + + return payload_size - stuffing_size; } static void put_vcd_padding_sector(AVFormatContext *ctx) @@ -832,22 +880,11 @@ /* XXX: system clock should be computed precisely, especially for CBR case. The current mode gives at least something coherent */ - if (stream_index == s->scr_stream_index + if (stream_index == 0 && pts != AV_NOPTS_VALUE) scr = pts; else scr = s->last_scr; - - /* "Sanity hack": make sure that the SCR does not overtake the pts of - buffered data that is still waiting to be written.*/ - for(i=0;i<ctx->nb_streams;i++) { - stream = ctx->streams[i]->priv_data; - - if(scr > stream->start_pts && stream->start_pts!=AV_NOPTS_VALUE) { - /* av_log(ctx, AV_LOG_DEBUG, "mpeg: restricting scr to stream pts (scr=%0.3f, stream index=%d, stream_pts=%0.3f).\n", scr/90000.0, i, stream->start_pts / 90000.0); */ - scr = stream->start_pts; - } - } } s->last_scr=scr; @@ -855,6 +892,135 @@ return scr; } +static int remove_decoded_packets(AVFormatContext *ctx, int64_t scr){ +// MpegMuxContext *s = ctx->priv_data; + int i; + + for(i=0; i<ctx->nb_streams; i++){ + AVStream *st = ctx->streams[i]; + StreamInfo *stream = st->priv_data; + PacketDesc *pkt_desc= stream->predecode_packet; + + while(pkt_desc && scr > pkt_desc->dts){ //FIXME > vs >= + if(stream->buffer_index < pkt_desc->size || + stream->predecode_packet == stream->premux_packet){ + av_log(ctx, AV_LOG_ERROR, "buffer underflow\n"); + break; + } + stream->buffer_index -= pkt_desc->size; + + stream->predecode_packet= pkt_desc->next; + av_freep(&pkt_desc); + } + } + + return 0; +} + +static int output_packet(AVFormatContext *ctx, int flush){ + MpegMuxContext *s = ctx->priv_data; + AVStream *st; + StreamInfo *stream; + int i, avail_space, es_size, trailer_size; + int best_i= -1; + int best_score= INT_MIN; + int ignore_constraints=0; + int64_t scr= s->last_scr; + +retry: + for(i=0; i<ctx->nb_streams; i++){ + AVStream *st = ctx->streams[i]; + StreamInfo *stream = st->priv_data; + const int avail_data= fifo_size(&stream->fifo, stream->fifo.rptr); + const int space= stream->max_buffer_size - stream->buffer_index; + int rel_space= 1024*space / stream->max_buffer_size; + + if(s->packet_size > avail_data && !flush) + return 0; + if(avail_data==0) + continue; + assert(avail_data>0); + + if(space < s->packet_size && !ignore_constraints) + continue; + + if(rel_space > best_score){ + best_score= rel_space; + best_i = i; + avail_space= space; + } + } + + if(best_i < 0){ + int64_t best_dts= INT64_MAX; + + for(i=0; i<ctx->nb_streams; i++){ + AVStream *st = ctx->streams[i]; + StreamInfo *stream = st->priv_data; + PacketDesc *pkt_desc= stream->predecode_packet; + if(pkt_desc && pkt_desc->dts < best_dts) + best_dts= pkt_desc->dts; + } + +#if 0 + av_log(ctx, AV_LOG_DEBUG, "bumping scr, scr:%f, dts:%f\n", + scr/90000.0, best_dts/90000.0); +#endif + if(best_dts == INT64_MAX) + return 0; + + if(scr >= best_dts+1 && !ignore_constraints){ + av_log(ctx, AV_LOG_ERROR, "packet too large, ignoring buffer limits to mux it\n"); + ignore_constraints= 1; + } + scr= FFMAX(best_dts+1, scr); + if(remove_decoded_packets(ctx, scr) < 0) + return -1; + goto retry; + } + + assert(best_i >= 0); + + st = ctx->streams[best_i]; + stream = st->priv_data; + + assert(fifo_size(&stream->fifo, stream->fifo.rptr) > 0); + + assert(avail_space >= s->packet_size || ignore_constraints); + + if(stream->premux_packet->unwritten_size == stream->premux_packet->size) + trailer_size= 0; + else + trailer_size= stream->premux_packet->unwritten_size; + + es_size= flush_packet(ctx, best_i, stream->premux_packet->pts, stream->premux_packet->dts, scr, trailer_size); + + if (s->is_vcd) { + /* Write one or more padding sectors, if necessary, to reach + the constant overall bitrate.*/ + int vcd_pad_bytes; + + while((vcd_pad_bytes = get_vcd_padding_size(ctx,stream->start_pts) ) >= s->packet_size){ //FIXME pts cannot be correct here + put_vcd_padding_sector(ctx); + s->last_scr += s->packet_size*90000LL / (s->mux_rate*50LL); //FIXME rounding and first few bytes of each packet + } + } + + stream->buffer_index += es_size; + s->last_scr += s->packet_size*90000LL / (s->mux_rate*50LL); //FIXME rounding and first few bytes of each packet + + while(stream->premux_packet && stream->premux_packet->unwritten_size <= es_size){ + es_size -= stream->premux_packet->unwritten_size; + stream->premux_packet= stream->premux_packet->next; + } + if(es_size) + stream->premux_packet->unwritten_size -= es_size; + + if(remove_decoded_packets(ctx, s->last_scr) < 0) + return -1; + + return 1; +} static int mpeg_mux_write_packet(AVFormatContext *ctx, AVPacket *pkt) { @@ -866,6 +1032,7 @@ StreamInfo *stream = st->priv_data; int64_t pts, dts, new_start_pts, new_start_dts; int len, avail_size; + PacketDesc *pkt_desc; pts= pkt->pts; dts= pkt->dts; @@ -891,17 +1058,36 @@ So the real data starts at SCR 36000+3*1200. */ pts = (pts + 36000 + 3600) & ((1LL << 33) - 1); dts = (dts + 36000 + 3600) & ((1LL << 33) - 1); + }else{ + pts = (pts + PRELOAD) & ((1LL << 33) - 1); + dts = (dts + PRELOAD) & ((1LL << 33) - 1); + } + + *stream->next_packet= + pkt_desc= av_mallocz(sizeof(PacketDesc)); + pkt_desc->pts= pts; + pkt_desc->dts= dts; + pkt_desc->unwritten_size= + pkt_desc->size= size; + if(!stream->predecode_packet) + stream->predecode_packet= pkt_desc; + stream->next_packet= &pkt_desc->next; + + if(stream->fifo.end - stream->fifo.buffer - fifo_size(&stream->fifo, stream->fifo.rptr) < size){ + av_log(ctx, AV_LOG_ERROR, "fifo overflow\n"); + return -1; + } + fifo_write(&stream->fifo, buf, size, &stream->fifo.wptr); + + for(;;){ + int ret= output_packet(ctx, 0); + if(ret<0) + return ret; + else if(ret==0) + break; } #if 0 - update_scr(ctx,stream_index,pts); - - printf("%d: pts=%0.3f dts=%0.3f scr=%0.3f\n", - stream_index, - pts / 90000.0, - dts / 90000.0, - s->last_scr / 90000.0); -#endif /* we assume here that pts != AV_NOPTS_VALUE */ new_start_pts = stream->start_pts; @@ -931,8 +1117,6 @@ stream->start_pts = new_start_pts; stream->start_dts = new_start_dts; stream->nb_frames++; - if (stream->frame_start_offset == 0) - stream->frame_start_offset = stream->buffer_ptr + 1; while (size > 0) { avail_size = get_packet_payload_size(ctx, stream_index, stream->start_pts, @@ -968,27 +1152,22 @@ stream->start_dts = AV_NOPTS_VALUE; } } - +#endif return 0; } static int mpeg_mux_end(AVFormatContext *ctx) { - MpegMuxContext *s = ctx->priv_data; +// MpegMuxContext *s = ctx->priv_data; StreamInfo *stream; int i; - - /* flush each packet */ - for(i=0;i<ctx->nb_streams;i++) { - stream = ctx->streams[i]->priv_data; - if (stream->buffer_ptr > 0) { - update_scr(ctx,i,stream->start_pts); - - /* NOTE: we can always write the remaining data as it was - tested before in mpeg_mux_write_packet() */ - flush_packet(ctx, i, stream->start_pts, stream->start_dts, - s->last_scr); - } + + for(;;){ + int ret= output_packet(ctx, 1); + if(ret<0) + return ret; + else if(ret==0) + break; } /* End header according to MPEG1 systems standard. We do not write @@ -997,6 +1176,12 @@ //put_be32(&ctx->pb, ISO_11172_END_CODE); //put_flush_packet(&ctx->pb); + for(i=0;i<ctx->nb_streams;i++) { + stream = ctx->streams[i]->priv_data; + + assert(fifo_size(&stream->fifo, stream->fifo.rptr) == 0); + fifo_free(&stream->fifo); + } return 0; } #endif //CONFIG_ENCODERS
--- a/utils.c Fri Oct 01 20:05:04 2004 +0000 +++ b/utils.c Sun Oct 03 02:42:01 2004 +0000 @@ -264,6 +264,34 @@ *wptr_ptr = wptr; } +/* get data from the fifo (return -1 if not enough data) */ +int put_fifo(ByteIOContext *pb, FifoBuffer *f, int buf_size, uint8_t **rptr_ptr) +{ + uint8_t *rptr = *rptr_ptr; + int size, len; + + if (f->wptr >= rptr) { + size = f->wptr - rptr; + } else { + size = (f->end - rptr) + (f->wptr - f->buffer); + } + + if (size < buf_size) + return -1; + while (buf_size > 0) { + len = f->end - rptr; + if (len > buf_size) + len = buf_size; + put_buffer(pb, rptr, len); + rptr += len; + if (rptr >= f->end) + rptr = f->buffer; + buf_size -= len; + } + *rptr_ptr = rptr; + return 0; +} + int filename_number_test(const char *filename) { char buf[1024];