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)

Originally committed as revision 2621 to svn://svn.ffmpeg.org/ffmpeg/trunk
This commit is contained in:
Fabrice Bellard 2003-12-16 11:25:30 +00:00
parent 6ec87caa70
commit 0dbb48d91e
1 changed files with 116 additions and 31 deletions

View File

@ -24,6 +24,8 @@
typedef struct { typedef struct {
uint8_t buffer[MAX_PAYLOAD_SIZE]; uint8_t buffer[MAX_PAYLOAD_SIZE];
int buffer_ptr; 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; uint8_t id;
int max_buffer_size; /* in bytes */ int max_buffer_size; /* in bytes */
int packet_number; int packet_number;
@ -33,10 +35,10 @@ typedef struct {
typedef struct { typedef struct {
int packet_size; /* required packet size */ int packet_size; /* required packet size */
int packet_data_max_size; /* maximum data size inside a packet */
int packet_number; int packet_number;
int pack_header_freq; /* frequency (in packets^-1) at which we send pack headers */ int pack_header_freq; /* frequency (in packets^-1) at which we send pack headers */
int system_header_freq; int system_header_freq;
int system_header_size;
int mux_rate; /* bitrate in units of 50 bytes/s */ int mux_rate; /* bitrate in units of 50 bytes/s */
/* stream info */ /* stream info */
int audio_bound; int audio_bound;
@ -166,6 +168,25 @@ static int put_system_header(AVFormatContext *ctx, uint8_t *buf)
return size; 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) static int mpeg_mux_init(AVFormatContext *ctx)
{ {
MpegMuxContext *s = ctx->priv_data; MpegMuxContext *s = ctx->priv_data;
@ -182,10 +203,6 @@ static int mpeg_mux_init(AVFormatContext *ctx)
else else
s->packet_size = 2048; 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->audio_bound = 0;
s->video_bound = 0; s->video_bound = 0;
mpa_id = AUDIO_ID; mpa_id = AUDIO_ID;
@ -260,6 +277,7 @@ static int mpeg_mux_init(AVFormatContext *ctx)
stream->start_pts = AV_NOPTS_VALUE; stream->start_pts = AV_NOPTS_VALUE;
stream->start_dts = AV_NOPTS_VALUE; stream->start_dts = AV_NOPTS_VALUE;
} }
s->system_header_size = get_system_header_size(ctx);
s->last_scr = 0; s->last_scr = 0;
return 0; return 0;
fail: fail:
@ -279,6 +297,50 @@ static inline void put_timestamp(ByteIOContext *pb, int id, int64_t timestamp)
put_be16(pb, (uint16_t)((((timestamp) & 0x7fff) << 1) | 1)); 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 */ /* flush the packet on stream stream_index */
static void flush_packet(AVFormatContext *ctx, int stream_index, static void flush_packet(AVFormatContext *ctx, int stream_index,
int64_t pts, int64_t dts, int64_t scr) int64_t pts, int64_t dts, int64_t scr)
@ -286,7 +348,8 @@ static void flush_packet(AVFormatContext *ctx, int stream_index,
MpegMuxContext *s = ctx->priv_data; MpegMuxContext *s = ctx->priv_data;
StreamInfo *stream = ctx->streams[stream_index]->priv_data; StreamInfo *stream = ctx->streams[stream_index]->priv_data;
uint8_t *buf_ptr; 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]; uint8_t buffer[128];
id = stream->id; id = stream->id;
@ -325,20 +388,21 @@ static void flush_packet(AVFormatContext *ctx, int stream_index,
header_len++; 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) { if (id < 0xc0) {
startcode = PRIVATE_STREAM_1; startcode = PRIVATE_STREAM_1;
payload_size -= 4; payload_size -= 4;
} else { } else {
startcode = 0x100 + id; startcode = 0x100 + id;
} }
stuffing_size = payload_size - stream->buffer_ptr; stuffing_size = payload_size - stream->buffer_ptr;
if (stuffing_size < 0) if (stuffing_size < 0)
stuffing_size = 0; stuffing_size = 0;
put_be32(&ctx->pb, startcode); put_be32(&ctx->pb, startcode);
put_be16(&ctx->pb, payload_size + header_len); put_be16(&ctx->pb, packet_size);
/* stuffing */ /* stuffing */
for(i=0;i<stuffing_size;i++) for(i=0;i<stuffing_size;i++)
put_byte(&ctx->pb, 0xff); put_byte(&ctx->pb, 0xff);
@ -377,10 +441,8 @@ static void flush_packet(AVFormatContext *ctx, int stream_index,
if (startcode == PRIVATE_STREAM_1) { if (startcode == PRIVATE_STREAM_1) {
put_byte(&ctx->pb, id); put_byte(&ctx->pb, id);
if (id >= 0x80 && id <= 0xbf) { if (id >= 0x80 && id <= 0xbf) {
/* XXX: need to check AC3 spec */ put_byte(&ctx->pb, stream->nb_frames);
put_byte(&ctx->pb, 1); put_be16(&ctx->pb, stream->frame_start_offset);
put_byte(&ctx->pb, 0);
put_byte(&ctx->pb, 2);
} }
} }
@ -388,15 +450,10 @@ static void flush_packet(AVFormatContext *ctx, int stream_index,
put_buffer(&ctx->pb, stream->buffer, payload_size - stuffing_size); put_buffer(&ctx->pb, stream->buffer, payload_size - stuffing_size);
put_flush_packet(&ctx->pb); 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++; s->packet_number++;
stream->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, static int mpeg_mux_write_packet(AVFormatContext *ctx, int stream_index,
@ -405,8 +462,8 @@ static int mpeg_mux_write_packet(AVFormatContext *ctx, int stream_index,
MpegMuxContext *s = ctx->priv_data; MpegMuxContext *s = ctx->priv_data;
AVStream *st = ctx->streams[stream_index]; AVStream *st = ctx->streams[stream_index];
StreamInfo *stream = st->priv_data; StreamInfo *stream = st->priv_data;
int64_t dts; int64_t dts, new_start_pts, new_start_dts;
int len; int len, avail_size;
/* XXX: system clock should be computed precisely, especially for /* XXX: system clock should be computed precisely, especially for
CBR case. The current mode gives at least something coherent */ CBR case. The current mode gives at least something coherent */
@ -422,28 +479,53 @@ static int mpeg_mux_write_packet(AVFormatContext *ctx, int stream_index,
dts = AV_NOPTS_VALUE; dts = AV_NOPTS_VALUE;
/* we assume here that pts != 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) { if (stream->start_pts == AV_NOPTS_VALUE) {
stream->start_pts = pts; new_start_pts = pts;
stream->start_dts = dts; 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) { 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) if (len > size)
len = size; len = size;
memcpy(stream->buffer + stream->buffer_ptr, buf, len); memcpy(stream->buffer + stream->buffer_ptr, buf, len);
stream->buffer_ptr += len; stream->buffer_ptr += len;
buf += len; buf += len;
size -= len; size -= len;
while (stream->buffer_ptr >= s->packet_data_max_size) { if (stream->buffer_ptr >= avail_size) {
/* output the packet */ /* if packet full, we send it now */
flush_packet(ctx, stream_index, flush_packet(ctx, stream_index,
stream->start_pts, stream->start_dts, s->last_scr); stream->start_pts, stream->start_dts, s->last_scr);
stream->buffer_ptr = 0;
/* Make sure only the FIRST pes packet for this frame has /* Make sure only the FIRST pes packet for this frame has
a timestamp */ a timestamp */
stream->start_pts = AV_NOPTS_VALUE; stream->start_pts = AV_NOPTS_VALUE;
stream->start_dts = AV_NOPTS_VALUE; stream->start_dts = AV_NOPTS_VALUE;
} }
} }
return 0; return 0;
} }
@ -456,8 +538,11 @@ static int mpeg_mux_end(AVFormatContext *ctx)
/* flush each packet */ /* flush each packet */
for(i=0;i<ctx->nb_streams;i++) { for(i=0;i<ctx->nb_streams;i++) {
stream = ctx->streams[i]->priv_data; stream = ctx->streams[i]->priv_data;
while (stream->buffer_ptr > 0) { if (stream->buffer_ptr > 0) {
flush_packet(ctx, i, AV_NOPTS_VALUE, AV_NOPTS_VALUE, s->last_scr); /* 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 @@ static int mpegps_read_packet(AVFormatContext *s,
} }
if (startcode >= 0x1e0 && startcode <= 0x1ef) { if (startcode >= 0x1e0 && startcode <= 0x1ef) {
type = CODEC_TYPE_VIDEO; type = CODEC_TYPE_VIDEO;
codec_id = CODEC_ID_MPEG1VIDEO; codec_id = CODEC_ID_MPEG2VIDEO;
} else if (startcode >= 0x1c0 && startcode <= 0x1df) { } else if (startcode >= 0x1c0 && startcode <= 0x1df) {
type = CODEC_TYPE_AUDIO; type = CODEC_TYPE_AUDIO;
codec_id = CODEC_ID_MP2; codec_id = CODEC_ID_MP2;
@ -994,7 +1079,7 @@ static AVOutputFormat mpeg2vob_mux = {
"vob", "vob",
sizeof(MpegMuxContext), sizeof(MpegMuxContext),
CODEC_ID_MP2, CODEC_ID_MP2,
CODEC_ID_MPEG1VIDEO, CODEC_ID_MPEG2VIDEO,
mpeg_mux_init, mpeg_mux_init,
mpeg_mux_write_packet, mpeg_mux_write_packet,
mpeg_mux_end, mpeg_mux_end,