summaryrefslogtreecommitdiff
path: root/libavformat/mpeg.c
diff options
context:
space:
mode:
authorHauke Duden <H.NS.Duden@gmx.net>2004-02-19 22:34:13 +0000
committerMichael Niedermayer <michaelni@gmx.at>2004-02-19 22:34:13 +0000
commit245159265267f0636142114cef3ea33b26e99510 (patch)
tree532adb5d8355939b0776c9c7d50f6105483048cb /libavformat/mpeg.c
parent16806499fc13ec0a7664186a42a48d9154b2a8a3 (diff)
improved VCD support patch by ("Hauke Duden" <H.NS.Duden at gmx dot net>)
- the first audio and video packs now contain only a system header and lots of padding. - no system headers in any packs other than the first ones - the two system headers only contain information about "their" stream - fixed some header values (muxrate, some flags, ...) so that they have the values specified by the standard - padding packs are inserted if the mux rate would be below 75 packs per second (the rate must not be below or above that value). - fixed the SCR of the packs - 20 zero bytes are now inserted at the end of each audio pack, after the data packet Originally committed as revision 2804 to svn://svn.ffmpeg.org/ffmpeg/trunk
Diffstat (limited to 'libavformat/mpeg.c')
-rw-r--r--libavformat/mpeg.c593
1 files changed, 451 insertions, 142 deletions
diff --git a/libavformat/mpeg.c b/libavformat/mpeg.c
index 4362e1b1a6..6ba516370f 100644
--- a/libavformat/mpeg.c
+++ b/libavformat/mpeg.c
@@ -50,9 +50,14 @@ typedef struct {
int video_bound;
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;
+ int64_t vcd_padding_bytes_written;
+
} MpegMuxContext;
#define PACK_START_CODE ((unsigned int)0x000001ba)
@@ -80,6 +85,7 @@ static const int lpcm_freq_tab[4] = { 48000, 96000, 44100, 32000 };
extern AVOutputFormat mpeg1system_mux;
extern AVOutputFormat mpeg1vcd_mux;
extern AVOutputFormat mpeg2vob_mux;
+extern AVOutputFormat mpeg2svcd_mux;
static int put_pack_header(AVFormatContext *ctx,
uint8_t *buf, int64_t timestamp)
@@ -117,7 +123,7 @@ static int put_pack_header(AVFormatContext *ctx,
return pbBufPtr(&pb) - pb.buf;
}
-static int put_system_header(AVFormatContext *ctx, uint8_t *buf)
+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;
@@ -132,40 +138,67 @@ static int put_system_header(AVFormatContext *ctx, uint8_t *buf)
rate_bound = s->mux_rate; /* maximum bit rate of the multiplexed stream */
put_bits(&pb, 22, rate_bound);
put_bits(&pb, 1, 1); /* marker */
- put_bits(&pb, 6, s->audio_bound);
+ if (s->is_vcd && only_for_stream_id==VIDEO_ID) {
+ /* This header applies only to the video stream (see VCD standard p. IV-7)*/
+ put_bits(&pb, 6, 0);
+ } else
+ put_bits(&pb, 6, s->audio_bound);
- put_bits(&pb, 1, 1); /* variable bitrate */
+ if (s->is_vcd)
+ put_bits(&pb, 1, 0); /* see VCD standard, p. IV-7*/
+ else
+ put_bits(&pb, 1, 1); /* variable bitrate*/
put_bits(&pb, 1, 1); /* non constrainted bit stream */
- put_bits(&pb, 1, 0); /* audio locked */
- put_bits(&pb, 1, 0); /* video locked */
+ if (s->is_vcd) {
+ /* see VCD standard p IV-7 */
+ put_bits(&pb, 1, 1); /* audio locked */
+ put_bits(&pb, 1, 1); /* video locked */
+ } else {
+ put_bits(&pb, 1, 0); /* audio locked */
+ put_bits(&pb, 1, 0); /* video locked */
+ }
+
put_bits(&pb, 1, 1); /* marker */
- put_bits(&pb, 5, s->video_bound);
+ if (s->is_vcd && only_for_stream_id==AUDIO_ID) {
+ /* This header applies only to the audio stream (see VCD standard p. IV-7)*/
+ put_bits(&pb, 5, 0);
+ } else
+ put_bits(&pb, 5, s->video_bound);
+
put_bits(&pb, 8, 0xff); /* reserved byte */
/* audio stream info */
private_stream_coded = 0;
for(i=0;i<ctx->nb_streams;i++) {
StreamInfo *stream = ctx->streams[i]->priv_data;
- id = stream->id;
- if (id < 0xc0) {
- /* special case for private streams (AC3 use that) */
- if (private_stream_coded)
- continue;
- private_stream_coded = 1;
- id = 0xbd;
- }
- put_bits(&pb, 8, id); /* stream ID */
- put_bits(&pb, 2, 3);
- if (id < 0xe0) {
- /* audio */
- put_bits(&pb, 1, 0);
- put_bits(&pb, 13, stream->max_buffer_size / 128);
- } else {
- /* video */
- put_bits(&pb, 1, 1);
- put_bits(&pb, 13, stream->max_buffer_size / 1024);
+
+ /* For VCDs, only include the stream info for the stream
+ that the pack which contains this system belongs to.
+ (see VCD standard p. IV-7) */
+ if ( !s->is_vcd || stream->id==only_for_stream_id
+ || only_for_stream_id==0) {
+
+ id = stream->id;
+ if (id < 0xc0) {
+ /* special case for private streams (AC3 use that) */
+ if (private_stream_coded)
+ continue;
+ private_stream_coded = 1;
+ id = 0xbd;
+ }
+ put_bits(&pb, 8, id); /* stream ID */
+ put_bits(&pb, 2, 3);
+ if (id < 0xe0) {
+ /* audio */
+ put_bits(&pb, 1, 0);
+ put_bits(&pb, 13, stream->max_buffer_size / 128);
+ } else {
+ /* video */
+ put_bits(&pb, 1, 1);
+ put_bits(&pb, 13, stream->max_buffer_size / 1024);
+ }
}
}
flush_put_bits(&pb);
@@ -202,15 +235,21 @@ static int mpeg_mux_init(AVFormatContext *ctx)
int bitrate, i, mpa_id, mpv_id, ac3_id, lpcm_id, j;
AVStream *st;
StreamInfo *stream;
+ int audio_bitrate;
+ int video_bitrate;
s->packet_number = 0;
s->is_vcd = (ctx->oformat == &mpeg1vcd_mux);
- s->is_mpeg2 = (ctx->oformat == &mpeg2vob_mux);
+ s->is_svcd = (ctx->oformat == &mpeg2svcd_mux);
+ s->is_mpeg2 = (ctx->oformat == &mpeg2vob_mux || ctx->oformat == &mpeg2svcd_mux);
- if (s->is_vcd)
- s->packet_size = 2324; /* VCD packet size */
+ if (s->is_vcd || s->is_svcd)
+ s->packet_size = 2324; /* VCD/SVCD packet size */
else
s->packet_size = 2048;
+
+ s->vcd_padding_bytes_written = 0;
+ s->vcd_padding_bitrate=0;
s->audio_bound = 0;
s->video_bound = 0;
@@ -266,14 +305,55 @@ static int mpeg_mux_init(AVFormatContext *ctx)
if (s->scr_stream_index == -1)
s->scr_stream_index = 0;
- /* we increase slightly the bitrate to take into account the
- headers. XXX: compute it exactly */
- bitrate = 2000;
+ bitrate = 0;
+ audio_bitrate = 0;
+ video_bitrate = 0;
for(i=0;i<ctx->nb_streams;i++) {
st = ctx->streams[i];
+ stream = (StreamInfo*) st->priv_data;
+
bitrate += st->codec.bit_rate;
+
+ if (stream->id==AUDIO_ID)
+ audio_bitrate += st->codec.bit_rate;
+ else if (stream->id==VIDEO_ID)
+ video_bitrate += st->codec.bit_rate;
+ }
+
+ if (s->is_vcd) {
+ double overhead_rate;
+
+ /* The VCD standard mandates that the mux_rate field is 3528
+ (see standard p. IV-6).
+ The value is actually "wrong", i.e. if you calculate
+ it using the normal formula and the 75 sectors per second transfer
+ rate you get a different value because the real pack size is 2324,
+ not 2352. But the standard explicitly specifies that the mux_rate
+ field in the header must have this value.*/
+ s->mux_rate=2352 * 75 / 50; /* = 3528*/
+
+ /* The VCD standard states that the muxed stream must be
+ exactly 75 packs / second (the data rate of a single speed cdrom).
+ Since the video bitrate (probably 1150000 bits/sec) will be below
+ the theoretical maximum we have to add some padding packets
+ to make up for the lower data rate.
+ (cf. VCD standard p. IV-6 )*/
+
+ /* Add the header overhead to the data rate.
+ 2279 data bytes per audio pack, 2294 data bytes per video pack*/
+ overhead_rate = ((audio_bitrate / 8.0) / 2279) * (2324 - 2279);
+ overhead_rate += ((video_bitrate / 8.0) / 2294) * (2324 - 2294);
+ overhead_rate *= 8;
+
+ /* Add padding so that the full bitrate is 2324*75 bytes/sec */
+ s->vcd_padding_bitrate = 2324 * 75 * 8 - (bitrate + overhead_rate);
+
+ } else {
+ /* we increase slightly the bitrate to take into account the
+ headers. XXX: compute it exactly */
+ bitrate += 2000;
+ s->mux_rate = (bitrate + (8 * 50) - 1) / (8 * 50);
}
- s->mux_rate = (bitrate + (8 * 50) - 1) / (8 * 50);
if (s->is_vcd || s->is_mpeg2)
/* every packet */
@@ -290,8 +370,10 @@ static int mpeg_mux_init(AVFormatContext *ctx)
/* every 200 packets. Need to look at the spec. */
s->system_header_freq = s->pack_header_freq * 40;
else if (s->is_vcd)
- /* every 40 packets, this is my invention */
- s->system_header_freq = s->pack_header_freq * 40;
+ /* the standard mandates that there are only two system headers
+ in the whole file: one in the first packet of each stream.
+ (see standard p. IV-7 and IV-8) */
+ s->system_header_freq = 0x7fffffff;
else
s->system_header_freq = s->pack_header_freq * 5;
@@ -323,6 +405,30 @@ static inline void put_timestamp(ByteIOContext *pb, int id, int64_t timestamp)
}
+/* return the number of padding bytes that should be inserted into
+ the multiplexed stream.*/
+static int get_vcd_padding_size(AVFormatContext *ctx, int64_t pts)
+{
+ MpegMuxContext *s = ctx->priv_data;
+ int pad_bytes = 0;
+
+ if (s->vcd_padding_bitrate > 0 && pts!=AV_NOPTS_VALUE)
+ {
+ int64_t full_pad_bytes;
+
+ full_pad_bytes = (int64_t)((s->vcd_padding_bitrate * (pts / 90000.0)) / 8.0);
+ pad_bytes = (int) (full_pad_bytes - s->vcd_padding_bytes_written);
+
+ if (pad_bytes<0)
+ /* might happen if we have already padded to a later timestamp. This
+ can occur if another stream has already advanced further.*/
+ pad_bytes=0;
+ }
+
+ return pad_bytes;
+}
+
+
/* 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. */
@@ -333,6 +439,8 @@ static int get_packet_payload_size(AVFormatContext *ctx, int stream_index,
int buf_index;
StreamInfo *stream;
+ stream = ctx->streams[stream_index]->priv_data;
+
buf_index = 0;
if (((s->packet_number % s->pack_header_freq) == 0)) {
/* pack header size */
@@ -340,41 +448,100 @@ static int get_packet_payload_size(AVFormatContext *ctx, int stream_index,
buf_index += 14;
else
buf_index += 12;
- if ((s->packet_number % s->system_header_freq) == 0)
- buf_index += s->system_header_size;
+
+ if (s->is_vcd) {
+ /* there is exactly one system header for each stream in a VCD MPEG,
+ One in the very first video packet and one in the very first
+ audio packet (see VCD standard p. IV-7 and IV-8).*/
+
+ if (stream->packet_number==0)
+ /* The system headers refer only to the stream they occur in,
+ so they have a constant size.*/
+ buf_index += 15;
+
+ } else {
+ 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 != pts)
- 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/LPCM private data header */
- buf_index += 4;
- if (stream->id >= 0xa0) {
- int n;
+ if (s->is_vcd && stream->packet_number==0)
+ /* the first pack of each stream contains only the pack header,
+ the system header and some padding (see VCD standard p. IV-6)
+ Add the padding size, so that the actual payload becomes 0.*/
+ buf_index += s->packet_size - buf_index;
+ else {
+ /* packet header size */
+ buf_index += 6;
+ if (s->is_mpeg2)
buf_index += 3;
- /* NOTE: we round the payload size to an integer number of
- LPCM samples */
- n = (s->packet_size - buf_index) % stream->lpcm_align;
- if (n)
- buf_index += (stream->lpcm_align - n);
+ if (pts != AV_NOPTS_VALUE) {
+ if (dts != pts)
+ buf_index += 5 + 5;
+ else
+ buf_index += 5;
+
+ } else {
+ if (!s->is_mpeg2)
+ buf_index++;
+ }
+
+ if (stream->id < 0xc0) {
+ /* AC3/LPCM private data header */
+ buf_index += 4;
+ if (stream->id >= 0xa0) {
+ int n;
+ buf_index += 3;
+ /* NOTE: we round the payload size to an integer number of
+ LPCM samples */
+ n = (s->packet_size - buf_index) % stream->lpcm_align;
+ if (n)
+ buf_index += (stream->lpcm_align - n);
+ }
}
+
+ if (s->is_vcd && stream->id == AUDIO_ID)
+ /* The VCD standard demands that 20 zero bytes follow
+ each audio packet (see standard p. IV-8).*/
+ buf_index+=20;
}
return s->packet_size - buf_index;
}
+/* Write an MPEG padding packet header. */
+static int put_padding_header(AVFormatContext *ctx,uint8_t* buf, int full_padding_size)
+{
+ MpegMuxContext *s = ctx->priv_data;
+ int size = full_padding_size - 6; /* subtract header length */
+
+ buf[0] = (uint8_t)(PADDING_STREAM >> 24);
+ buf[1] = (uint8_t)(PADDING_STREAM >> 16);
+ buf[2] = (uint8_t)(PADDING_STREAM >> 8);
+ buf[3] = (uint8_t)(PADDING_STREAM);
+ buf[4] = (uint8_t)(size >> 8);
+ buf[5] = (uint8_t)(size & 0xff);
+
+ if (!s->is_mpeg2) {
+ buf[6] = 0x0f;
+ return 7;
+ } else
+ return 6;
+}
+
+static void put_padding_packet(AVFormatContext *ctx, ByteIOContext *pb,int packet_bytes)
+{
+ uint8_t buffer[7];
+ int size, i;
+
+ size = put_padding_header(ctx,buffer, packet_bytes);
+ put_buffer(pb, buffer, size);
+ packet_bytes -= size;
+
+ for(i=0;i<packet_bytes;i++)
+ put_byte(pb, 0xff);
+}
+
+
/* 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)
@@ -385,6 +552,8 @@ static void flush_packet(AVFormatContext *ctx, int stream_index,
int size, payload_size, startcode, id, stuffing_size, i, header_len;
int packet_size;
uint8_t buffer[128];
+ int zero_trail_bytes = 0;
+ int pad_packet_bytes = 0;
id = stream->id;
@@ -394,109 +563,151 @@ static void flush_packet(AVFormatContext *ctx, int stream_index,
#endif
buf_ptr = buffer;
+
if (((s->packet_number % s->pack_header_freq) == 0)) {
/* output pack and systems header if needed */
size = put_pack_header(ctx, buf_ptr, scr);
buf_ptr += size;
- if ((s->packet_number % s->system_header_freq) == 0) {
- size = put_system_header(ctx, buf_ptr);
- buf_ptr += size;
+
+ if (s->is_vcd) {
+ /* there is exactly one system header for each stream in a VCD MPEG,
+ One in the very first video packet and one in the very first
+ audio packet (see VCD standard p. IV-7 and IV-8).*/
+
+ if (stream->packet_number==0) {
+ size = put_system_header(ctx, buf_ptr, id);
+ buf_ptr += size;
+ }
+ } else {
+ if ((s->packet_number % s->system_header_freq) == 0) {
+ size = put_system_header(ctx, buf_ptr, 0);
+ buf_ptr += size;
+ }
}
}
size = buf_ptr - buffer;
put_buffer(&ctx->pb, buffer, size);
- /* packet header */
- if (s->is_mpeg2) {
- header_len = 3;
- } else {
- header_len = 0;
- }
- if (pts != AV_NOPTS_VALUE) {
- if (dts != pts)
- header_len += 5 + 5;
- else
- header_len += 5;
- } else {
- if (!s->is_mpeg2)
- header_len++;
+ packet_size = s->packet_size - size;
+
+ if (s->is_vcd && id == AUDIO_ID)
+ /* The VCD standard demands that 20 zero bytes follow
+ each audio pack (see standard p. IV-8).*/
+ zero_trail_bytes += 20;
+
+ if (s->is_vcd && stream->packet_number==0) {
+ /* the first pack of each stream contains only the pack header,
+ the system header and lots of padding (see VCD standard p. IV-6).
+ In the case of an audio pack, 20 zero bytes are also added at
+ the end.*/
+ pad_packet_bytes = packet_size - zero_trail_bytes;
}
- packet_size = s->packet_size - (size + 6);
- payload_size = packet_size - header_len;
- if (id < 0xc0) {
- startcode = PRIVATE_STREAM_1;
- payload_size -= 4;
- if (id >= 0xa0)
- payload_size -= 3;
- } else {
- startcode = 0x100 + id;
- }
+ packet_size -= pad_packet_bytes + zero_trail_bytes;
- stuffing_size = payload_size - stream->buffer_ptr;
- if (stuffing_size < 0)
- stuffing_size = 0;
- put_be32(&ctx->pb, startcode);
+ if (packet_size > 0) {
- put_be16(&ctx->pb, packet_size);
+ /* packet header size */
+ packet_size -= 6;
+
+ /* packet header */
+ if (s->is_mpeg2) {
+ header_len = 3;
+ } else {
+ header_len = 0;
+ }
+ if (pts != AV_NOPTS_VALUE) {
+ if (dts != pts)
+ header_len += 5 + 5;
+ else
+ header_len += 5;
+ } else {
+ if (!s->is_mpeg2)
+ header_len++;
+ }
- if (!s->is_mpeg2)
- for(i=0;i<stuffing_size;i++)
- put_byte(&ctx->pb, 0xff);
+ payload_size = packet_size - header_len;
+ if (id < 0xc0) {
+ startcode = PRIVATE_STREAM_1;
+ payload_size -= 4;
+ if (id >= 0xa0)
+ payload_size -= 3;
+ } else {
+ startcode = 0x100 + id;
+ }
- if (s->is_mpeg2) {
- put_byte(&ctx->pb, 0x80); /* mpeg2 id */
+ stuffing_size = payload_size - stream->buffer_ptr;
+ if (stuffing_size < 0)
+ stuffing_size = 0;
+ put_be32(&ctx->pb, startcode);
- if (pts != AV_NOPTS_VALUE) {
- if (dts != pts) {
- put_byte(&ctx->pb, 0xc0); /* flags */
- put_byte(&ctx->pb, header_len - 3 + stuffing_size);
- put_timestamp(&ctx->pb, 0x03, pts);
- put_timestamp(&ctx->pb, 0x01, dts);
+ put_be16(&ctx->pb, packet_size);
+
+ if (!s->is_mpeg2)
+ for(i=0;i<stuffing_size;i++)
+ put_byte(&ctx->pb, 0xff);
+
+ if (s->is_mpeg2) {
+ put_byte(&ctx->pb, 0x80); /* mpeg2 id */
+
+ if (pts != AV_NOPTS_VALUE) {
+ if (dts != pts) {
+ put_byte(&ctx->pb, 0xc0); /* flags */
+ put_byte(&ctx->pb, header_len - 3 + stuffing_size);
+ put_timestamp(&ctx->pb, 0x03, pts);
+ put_timestamp(&ctx->pb, 0x01, dts);
+ } else {
+ put_byte(&ctx->pb, 0x80); /* flags */
+ put_byte(&ctx->pb, header_len - 3 + stuffing_size);
+ put_timestamp(&ctx->pb, 0x02, pts);
+ }
} else {
- put_byte(&ctx->pb, 0x80); /* flags */
+ put_byte(&ctx->pb, 0x00); /* flags */
put_byte(&ctx->pb, header_len - 3 + stuffing_size);
- put_timestamp(&ctx->pb, 0x02, pts);
}
} else {
- put_byte(&ctx->pb, 0x00); /* flags */
- put_byte(&ctx->pb, header_len - 3 + stuffing_size);
- }
- } else {
- if (pts != AV_NOPTS_VALUE) {
- if (dts != pts) {
- put_timestamp(&ctx->pb, 0x03, pts);
- put_timestamp(&ctx->pb, 0x01, dts);
+ if (pts != AV_NOPTS_VALUE) {
+ if (dts != pts) {
+ put_timestamp(&ctx->pb, 0x03, pts);
+ put_timestamp(&ctx->pb, 0x01, dts);
+ } else {
+ put_timestamp(&ctx->pb, 0x02, pts);
+ }
} else {
- put_timestamp(&ctx->pb, 0x02, pts);
+ put_byte(&ctx->pb, 0x0f);
}
- } else {
- put_byte(&ctx->pb, 0x0f);
}
- }
- if (startcode == PRIVATE_STREAM_1) {
- put_byte(&ctx->pb, id);
- if (id >= 0xa0) {
- /* LPCM (XXX: check nb_frames) */
- put_byte(&ctx->pb, 7);
- put_be16(&ctx->pb, 4); /* skip 3 header bytes */
- put_byte(&ctx->pb, stream->lpcm_header[0]);
- put_byte(&ctx->pb, stream->lpcm_header[1]);
- 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);
+ if (startcode == PRIVATE_STREAM_1) {
+ put_byte(&ctx->pb, id);
+ if (id >= 0xa0) {
+ /* LPCM (XXX: check nb_frames) */
+ put_byte(&ctx->pb, 7);
+ put_be16(&ctx->pb, 4); /* skip 3 header bytes */
+ put_byte(&ctx->pb, stream->lpcm_header[0]);
+ put_byte(&ctx->pb, stream->lpcm_header[1]);
+ 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);
+ }
}
+
+ if (s->is_mpeg2)
+ for(i=0;i<stuffing_size;i++)
+ put_byte(&ctx->pb, 0xff);
+
+ /* output data */
+ put_buffer(&ctx->pb, stream->buffer, payload_size - stuffing_size);
}
- if (s->is_mpeg2)
- for(i=0;i<stuffing_size;i++)
- put_byte(&ctx->pb, 0xff);
+ if (pad_packet_bytes > 0)
+ put_padding_packet(ctx,&ctx->pb, pad_packet_bytes);
- /* output data */
- put_buffer(&ctx->pb, stream->buffer, payload_size - stuffing_size);
+ for(i=0;i<zero_trail_bytes;i++)
+ put_byte(&ctx->pb, 0x00);
+
put_flush_packet(&ctx->pb);
s->packet_number++;
@@ -505,6 +716,31 @@ static void flush_packet(AVFormatContext *ctx, int stream_index,
stream->frame_start_offset = 0;
}
+static void put_vcd_padding_sector(AVFormatContext *ctx)
+{
+ /* There are two ways to do this padding: writing a sector/pack
+ of 0 values, or writing an MPEG padding pack. Both seem to
+ work with most decoders, BUT the VCD standard only allows a 0-sector
+ (see standard p. IV-4, IV-5).
+ So a 0-sector it is...*/
+
+ MpegMuxContext *s = ctx->priv_data;
+ int i;
+
+ for(i=0;i<s->packet_size;i++)
+ put_byte(&ctx->pb, 0);
+
+ s->vcd_padding_bytes_written += s->packet_size;
+
+ put_flush_packet(&ctx->pb);
+
+ /* increasing the packet number is correct. The SCR of the following packs
+ is calculated from the packet_number and it has to include the padding
+ sector (it represents the sector index, not the MPEG pack index)
+ (see VCD standard p. IV-6)*/
+ s->packet_number++;
+}
+
/* XXX: move that to upper layer */
/* XXX: we assume that there are always 'max_b_frames' between
reference frames. A better solution would be to use the AVFrame pts
@@ -549,6 +785,43 @@ static void compute_pts_dts(AVStream *st, int64_t *ppts, int64_t *pdts,
*pdts = dts & ((1LL << 33) - 1);
}
+static int64_t update_scr(AVFormatContext *ctx,int stream_index,int64_t pts)
+{
+ MpegMuxContext *s = ctx->priv_data;
+ int64_t scr;
+
+ if (s->is_vcd)
+ /* Since the data delivery rate is constant, SCR is computed
+ using the formula C + i * 1200 where C is the start constant
+ and i is the pack index.
+ It is recommended that SCR 0 is at the beginning of the VCD front
+ margin (a sequence of empty Form 2 sectors on the CD).
+ It is recommended that the front margin is 30 sectors long, so
+ we use C = 30*1200 = 36000
+ (Note that even if the front margin is not 30 sectors the file
+ will still be correct according to the standard. It just won't have
+ the "recommended" value).*/
+ scr = 36000 + s->packet_number * 1200;
+ else {
+ /* XXX I believe this calculation of SCR is wrong. SCR
+ specifies at which time the data should enter the decoder.
+ Two packs cannot enter the decoder at the same time. */
+
+ /* 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
+ && pts != AV_NOPTS_VALUE)
+ scr = pts;
+ else
+ scr = s->last_scr;
+ }
+
+ s->last_scr=scr;
+
+ return scr;
+}
+
+
static int mpeg_mux_write_packet(AVFormatContext *ctx, int stream_index,
const uint8_t *buf, int size,
int64_t timestamp)
@@ -558,15 +831,13 @@ static int mpeg_mux_write_packet(AVFormatContext *ctx, int stream_index,
StreamInfo *stream = st->priv_data;
int64_t pts, dts, new_start_pts, new_start_dts;
int len, avail_size;
-
+
compute_pts_dts(st, &pts, &dts, timestamp);
- /* 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)
- s->last_scr = pts;
#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,
@@ -586,10 +857,15 @@ static int mpeg_mux_write_packet(AVFormatContext *ctx, int stream_index,
new_start_pts,
new_start_dts);
if (stream->buffer_ptr >= avail_size) {
+
+ update_scr(ctx,stream_index,stream->start_pts);
+
/* 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 */
+ padding.
+ Note: this always happens for the first audio and video packet
+ in a VCD file, since they do not carry any data.*/
flush_packet(ctx, stream_index,
stream->start_pts, stream->start_dts, s->last_scr);
stream->buffer_ptr = 0;
@@ -611,10 +887,23 @@ static int mpeg_mux_write_packet(AVFormatContext *ctx, int stream_index,
buf += len;
size -= len;
if (stream->buffer_ptr >= avail_size) {
+
+ update_scr(ctx,stream_index,stream->start_pts);
+
/* 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;
+
+ 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)
+ put_vcd_padding_sector(ctx);
+ }
+
/* Make sure only the FIRST pes packet for this frame has
a timestamp */
stream->start_pts = AV_NOPTS_VALUE;
@@ -635,6 +924,8 @@ static int mpeg_mux_end(AVFormatContext *ctx)
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,
@@ -1139,7 +1430,7 @@ static int mpegps_read_seek(AVFormatContext *s,
start_pos= pos;
// read the next timestamp
- dts = mpegps_read_dts(s, stream_index, &pos, 1);
+ dts = mpegps_read_dts(s, stream_index, &pos, 1);
if(pos == pos_max)
no_change++;
else
@@ -1214,6 +1505,23 @@ static AVOutputFormat mpeg2vob_mux = {
mpeg_mux_write_packet,
mpeg_mux_end,
};
+
+/* Same as mpeg2vob_mux except that the pack size is 2324 */
+static AVOutputFormat mpeg2svcd_mux = {
+ "svcd",
+ "MPEG2 PS format (VOB)",
+ "video/mpeg",
+ "vob",
+ sizeof(MpegMuxContext),
+ CODEC_ID_MP2,
+ CODEC_ID_MPEG2VIDEO,
+ mpeg_mux_init,
+ mpeg_mux_write_packet,
+ mpeg_mux_end,
+};
+
+
+
#endif //CONFIG_ENCODERS
AVInputFormat mpegps_demux = {
@@ -1233,6 +1541,7 @@ int mpegps_init(void)
av_register_output_format(&mpeg1system_mux);
av_register_output_format(&mpeg1vcd_mux);
av_register_output_format(&mpeg2vob_mux);
+ av_register_output_format(&mpeg2svcd_mux);
#endif //CONFIG_ENCODERS
av_register_input_format(&mpegps_demux);
return 0;