Mercurial > libavformat.hg
changeset 335:b0ac206f232d libavformat
better and simpler logic for MPEG muxing - fixed rare MPEG muxing PTS generation bug (stuffing is added in such rare cases) - fixed AC3 payload size generation - generate correct AC3 frame header (need spec checking)
author | bellard |
---|---|
date | Tue, 16 Dec 2003 11:25:30 +0000 |
parents | 7f089db11f9a |
children | d75fd4c6ab62 |
files | mpeg.c |
diffstat | 1 files changed, 116 insertions(+), 31 deletions(-) [+] |
line wrap: on
line diff
--- a/mpeg.c Tue Dec 16 11:21:25 2003 +0000 +++ b/mpeg.c Tue Dec 16 11:25:30 2003 +0000 @@ -24,6 +24,8 @@ 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) */ uint8_t id; int max_buffer_size; /* in bytes */ int packet_number; @@ -33,10 +35,10 @@ typedef struct { int packet_size; /* required packet size */ - int packet_data_max_size; /* maximum data size inside a packet */ int packet_number; int pack_header_freq; /* frequency (in packets^-1) at which we send pack headers */ int system_header_freq; + int system_header_size; int mux_rate; /* bitrate in units of 50 bytes/s */ /* stream info */ int audio_bound; @@ -166,6 +168,25 @@ return size; } +static int get_system_header_size(AVFormatContext *ctx) +{ + int buf_index, i, private_stream_coded; + StreamInfo *stream; + + buf_index = 12; + private_stream_coded = 0; + for(i=0;i<ctx->nb_streams;i++) { + stream = ctx->streams[i]->priv_data; + if (stream->id < 0xc0) { + if (private_stream_coded) + continue; + private_stream_coded = 1; + } + buf_index += 3; + } + return buf_index; +} + static int mpeg_mux_init(AVFormatContext *ctx) { MpegMuxContext *s = ctx->priv_data; @@ -182,10 +203,6 @@ else s->packet_size = 2048; - /* startcode(4) + length(2) + flags(1) */ - s->packet_data_max_size = s->packet_size - 7; - if (s->is_mpeg2) - s->packet_data_max_size -= 2; s->audio_bound = 0; s->video_bound = 0; mpa_id = AUDIO_ID; @@ -260,6 +277,7 @@ stream->start_pts = AV_NOPTS_VALUE; stream->start_dts = AV_NOPTS_VALUE; } + s->system_header_size = get_system_header_size(ctx); s->last_scr = 0; return 0; fail: @@ -279,6 +297,50 @@ put_be16(pb, (uint16_t)((((timestamp) & 0x7fff) << 1) | 1)); } + +/* return the exact available payload size for the next packet for + stream 'stream_index'. 'pts' and 'dts' are only used to know if + timestamps are needed in the packet header. */ +static int get_packet_payload_size(AVFormatContext *ctx, int stream_index, + int64_t pts, int64_t dts) +{ + MpegMuxContext *s = ctx->priv_data; + int buf_index; + StreamInfo *stream; + + buf_index = 0; + if (((s->packet_number % s->pack_header_freq) == 0)) { + /* pack header size */ + if (s->is_mpeg2) + buf_index += 14; + else + buf_index += 12; + if ((s->packet_number % s->system_header_freq) == 0) + buf_index += s->system_header_size; + } + + /* packet header size */ + buf_index += 6; + if (s->is_mpeg2) + buf_index += 3; + if (pts != AV_NOPTS_VALUE) { + if (dts != AV_NOPTS_VALUE) + buf_index += 5 + 5; + else + buf_index += 5; + } else { + if (!s->is_mpeg2) + buf_index++; + } + + stream = ctx->streams[stream_index]->priv_data; + if (stream->id < 0xc0) { + /* AC3 private data header */ + buf_index += 4; + } + return s->packet_size - buf_index; +} + /* 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) @@ -286,7 +348,8 @@ MpegMuxContext *s = ctx->priv_data; StreamInfo *stream = ctx->streams[stream_index]->priv_data; uint8_t *buf_ptr; - int size, payload_size, startcode, id, len, stuffing_size, i, header_len; + int size, payload_size, startcode, id, stuffing_size, i, header_len; + int packet_size; uint8_t buffer[128]; id = stream->id; @@ -325,20 +388,21 @@ header_len++; } - payload_size = s->packet_size - (size + 6 + header_len); + packet_size = s->packet_size - (size + 6); + payload_size = packet_size - header_len; if (id < 0xc0) { startcode = PRIVATE_STREAM_1; payload_size -= 4; } else { startcode = 0x100 + id; } + stuffing_size = payload_size - stream->buffer_ptr; if (stuffing_size < 0) stuffing_size = 0; - put_be32(&ctx->pb, startcode); - put_be16(&ctx->pb, payload_size + header_len); + put_be16(&ctx->pb, packet_size); /* stuffing */ for(i=0;i<stuffing_size;i++) put_byte(&ctx->pb, 0xff); @@ -377,10 +441,8 @@ if (startcode == PRIVATE_STREAM_1) { put_byte(&ctx->pb, id); if (id >= 0x80 && id <= 0xbf) { - /* XXX: need to check AC3 spec */ - put_byte(&ctx->pb, 1); - put_byte(&ctx->pb, 0); - put_byte(&ctx->pb, 2); + put_byte(&ctx->pb, stream->nb_frames); + put_be16(&ctx->pb, stream->frame_start_offset); } } @@ -388,15 +450,10 @@ put_buffer(&ctx->pb, stream->buffer, payload_size - stuffing_size); put_flush_packet(&ctx->pb); - /* preserve remaining data */ - len = stream->buffer_ptr - payload_size; - if (len < 0) - len = 0; - memmove(stream->buffer, stream->buffer + stream->buffer_ptr - len, len); - stream->buffer_ptr = len; - s->packet_number++; stream->packet_number++; + stream->nb_frames = 0; + stream->frame_start_offset = 0; } static int mpeg_mux_write_packet(AVFormatContext *ctx, int stream_index, @@ -405,8 +462,8 @@ MpegMuxContext *s = ctx->priv_data; AVStream *st = ctx->streams[stream_index]; StreamInfo *stream = st->priv_data; - int64_t dts; - int len; + int64_t dts, new_start_pts, new_start_dts; + int len, avail_size; /* XXX: system clock should be computed precisely, especially for CBR case. The current mode gives at least something coherent */ @@ -422,28 +479,53 @@ dts = AV_NOPTS_VALUE; /* we assume here that pts != AV_NOPTS_VALUE */ + new_start_pts = stream->start_pts; + new_start_dts = stream->start_dts; + if (stream->start_pts == AV_NOPTS_VALUE) { - stream->start_pts = pts; - stream->start_dts = dts; + new_start_pts = pts; + new_start_dts = dts; } + avail_size = get_packet_payload_size(ctx, stream_index, + new_start_pts, + new_start_dts); + if (stream->buffer_ptr >= avail_size) { + /* unlikely case: outputing the pts or dts increase the packet + size so that we cannot write the start of the next + packet. In this case, we must flush the current packet with + padding */ + flush_packet(ctx, stream_index, + stream->start_pts, stream->start_dts, s->last_scr); + stream->buffer_ptr = 0; + } + 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; while (size > 0) { - len = s->packet_data_max_size - stream->buffer_ptr; + avail_size = get_packet_payload_size(ctx, stream_index, + stream->start_pts, + stream->start_dts); + len = avail_size - stream->buffer_ptr; if (len > size) len = size; memcpy(stream->buffer + stream->buffer_ptr, buf, len); stream->buffer_ptr += len; buf += len; size -= len; - while (stream->buffer_ptr >= s->packet_data_max_size) { - /* output the packet */ + if (stream->buffer_ptr >= avail_size) { + /* if packet full, we send it now */ flush_packet(ctx, stream_index, stream->start_pts, stream->start_dts, s->last_scr); + stream->buffer_ptr = 0; /* Make sure only the FIRST pes packet for this frame has a timestamp */ stream->start_pts = AV_NOPTS_VALUE; stream->start_dts = AV_NOPTS_VALUE; } } + return 0; } @@ -456,8 +538,11 @@ /* flush each packet */ for(i=0;i<ctx->nb_streams;i++) { stream = ctx->streams[i]->priv_data; - while (stream->buffer_ptr > 0) { - flush_packet(ctx, i, AV_NOPTS_VALUE, AV_NOPTS_VALUE, s->last_scr); + if (stream->buffer_ptr > 0) { + /* 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); } } @@ -755,7 +840,7 @@ } if (startcode >= 0x1e0 && startcode <= 0x1ef) { type = CODEC_TYPE_VIDEO; - codec_id = CODEC_ID_MPEG1VIDEO; + codec_id = CODEC_ID_MPEG2VIDEO; } else if (startcode >= 0x1c0 && startcode <= 0x1df) { type = CODEC_TYPE_AUDIO; codec_id = CODEC_ID_MP2; @@ -994,7 +1079,7 @@ "vob", sizeof(MpegMuxContext), CODEC_ID_MP2, - CODEC_ID_MPEG1VIDEO, + CODEC_ID_MPEG2VIDEO, mpeg_mux_init, mpeg_mux_write_packet, mpeg_mux_end,