Commit 22494489 authored by Hauke Duden's avatar Hauke Duden Committed by Michael Niedermayer

mpeg SVCD compatibility, SCR fixes, standard compliance

- fixed VBR+constrained bitstream header flags for non-VCD
- more sane (and SVCD compatible) value for video stream->max_buffer_size
- always write at least one PES header stuffing byte for MPEG-2 to
prevent accidental start code generation
- do not write more than 16 stuffing bytes in a PES header (not
allowed). Use padding packets instead.
- include a PES extension in the first MPEG-2 packet
- fill the first pack of SVCD files with padding
- "sanity hack" that prevents the SCR from overtaking the PTS for non-VCD
- fixed VCD PTS values to correspond to the SCR
- always include DTS in the first SVCD packet (fixes lots of
compatibility problems with DVD players)

patch by (Hauke Duden <H.NS.Duden at gmx dot net>)

Originally committed as revision 3078 to svn://svn.ffmpeg.org/ffmpeg/trunk
parent ae4b7d59
...@@ -144,11 +144,14 @@ static int put_system_header(AVFormatContext *ctx, uint8_t *buf,int only_for_str ...@@ -144,11 +144,14 @@ static int put_system_header(AVFormatContext *ctx, uint8_t *buf,int only_for_str
} else } else
put_bits(&pb, 6, s->audio_bound); put_bits(&pb, 6, s->audio_bound);
if (s->is_vcd) if (s->is_vcd) {
put_bits(&pb, 1, 0); /* see VCD standard, p. IV-7*/ /* see VCD standard, p. IV-7*/
else put_bits(&pb, 1, 0);
put_bits(&pb, 1, 1); /* variable bitrate*/ put_bits(&pb, 1, 1);
put_bits(&pb, 1, 1); /* non constrainted bit stream */ } else {
put_bits(&pb, 1, 0); /* variable bitrate*/
put_bits(&pb, 1, 0); /* non constrainted bit stream */
}
if (s->is_vcd) { if (s->is_vcd) {
/* see VCD standard p IV-7 */ /* see VCD standard p IV-7 */
...@@ -286,6 +289,9 @@ static int mpeg_mux_init(AVFormatContext *ctx) ...@@ -286,6 +289,9 @@ static int mpeg_mux_init(AVFormatContext *ctx)
} else { } else {
stream->id = mpa_id++; stream->id = mpa_id++;
} }
/* This value HAS to be used for VCD (see VCD standard, p. IV-7).
Right now it is also used for everything else.*/
stream->max_buffer_size = 4 * 1024; stream->max_buffer_size = 4 * 1024;
s->audio_bound++; s->audio_bound++;
break; break;
...@@ -294,7 +300,13 @@ static int mpeg_mux_init(AVFormatContext *ctx) ...@@ -294,7 +300,13 @@ static int mpeg_mux_init(AVFormatContext *ctx)
if (s->scr_stream_index == -1) if (s->scr_stream_index == -1)
s->scr_stream_index = i; s->scr_stream_index = i;
stream->id = mpv_id++; stream->id = mpv_id++;
if (s->is_vcd)
/* see VCD standard, p. IV-7*/
stream->max_buffer_size = 46 * 1024; stream->max_buffer_size = 46 * 1024;
else
/* This value HAS to be used for SVCD (see SVCD standard, p. 26 V.2.3.2).
Right now it is also used for everything else.*/
stream->max_buffer_size = 230 * 1024;
s->video_bound++; s->video_bound++;
break; break;
default: default:
...@@ -465,7 +477,8 @@ static int get_packet_payload_size(AVFormatContext *ctx, int stream_index, ...@@ -465,7 +477,8 @@ static int get_packet_payload_size(AVFormatContext *ctx, int stream_index,
} }
} }
if (s->is_vcd && stream->packet_number==0) if ((s->is_vcd && stream->packet_number==0)
|| (s->is_svcd && s->packet_number==0))
/* the first pack of each stream contains only the pack header, /* the first pack of each stream contains only the pack header,
the system header and some padding (see VCD standard p. IV-6) the system header and some padding (see VCD standard p. IV-6)
Add the padding size, so that the actual payload becomes 0.*/ Add the padding size, so that the actual payload becomes 0.*/
...@@ -473,8 +486,12 @@ static int get_packet_payload_size(AVFormatContext *ctx, int stream_index, ...@@ -473,8 +486,12 @@ static int get_packet_payload_size(AVFormatContext *ctx, int stream_index,
else { else {
/* packet header size */ /* packet header size */
buf_index += 6; buf_index += 6;
if (s->is_mpeg2) if (s->is_mpeg2) {
buf_index += 3; buf_index += 3;
if (stream->packet_number==0)
buf_index += 3; /* PES extension */
buf_index += 1; /* obligatory stuffing byte */
}
if (pts != AV_NOPTS_VALUE) { if (pts != AV_NOPTS_VALUE) {
if (dts != pts) if (dts != pts)
buf_index += 5 + 5; buf_index += 5 + 5;
...@@ -554,6 +571,8 @@ static void flush_packet(AVFormatContext *ctx, int stream_index, ...@@ -554,6 +571,8 @@ static void flush_packet(AVFormatContext *ctx, int stream_index,
uint8_t buffer[128]; uint8_t buffer[128];
int zero_trail_bytes = 0; int zero_trail_bytes = 0;
int pad_packet_bytes = 0; int pad_packet_bytes = 0;
int pes_flags;
int general_pack = 0; /*"general" pack without data specific to one stream?*/
id = stream->id; id = stream->id;
...@@ -595,11 +614,16 @@ static void flush_packet(AVFormatContext *ctx, int stream_index, ...@@ -595,11 +614,16 @@ static void flush_packet(AVFormatContext *ctx, int stream_index,
each audio pack (see standard p. IV-8).*/ each audio pack (see standard p. IV-8).*/
zero_trail_bytes += 20; zero_trail_bytes += 20;
if (s->is_vcd && stream->packet_number==0) { if ((s->is_vcd && stream->packet_number==0)
/* the first pack of each stream contains only the pack header, || (s->is_svcd && s->packet_number==0)) {
/* for VCD the first pack of each stream contains only the pack header,
the system header and lots of padding (see VCD standard p. IV-6). 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 In the case of an audio pack, 20 zero bytes are also added at
the end.*/ the end.*/
/* For SVCD we fill the very first pack to increase compatibility with
some DVD players. Not mandated by the standard.*/
if (s->is_svcd)
general_pack = 1; /* the system header refers to both streams and no stream data*/
pad_packet_bytes = packet_size - zero_trail_bytes; pad_packet_bytes = packet_size - zero_trail_bytes;
} }
...@@ -613,6 +637,9 @@ static void flush_packet(AVFormatContext *ctx, int stream_index, ...@@ -613,6 +637,9 @@ static void flush_packet(AVFormatContext *ctx, int stream_index,
/* packet header */ /* packet header */
if (s->is_mpeg2) { if (s->is_mpeg2) {
header_len = 3; header_len = 3;
if (stream->packet_number==0)
header_len += 3; /* PES extension */
header_len += 1; /* obligatory stuffing byte */
} else { } else {
header_len = 0; header_len = 0;
} }
...@@ -639,6 +666,13 @@ static void flush_packet(AVFormatContext *ctx, int stream_index, ...@@ -639,6 +666,13 @@ static void flush_packet(AVFormatContext *ctx, int stream_index,
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;
if (stuffing_size > 16) { /*<=16 for MPEG-1, <=32 for MPEG-2*/
pad_packet_bytes += stuffing_size;
packet_size -= stuffing_size;
payload_size -= stuffing_size;
stuffing_size = 0;
}
put_be32(&ctx->pb, startcode); put_be32(&ctx->pb, startcode);
put_be16(&ctx->pb, packet_size); put_be16(&ctx->pb, packet_size);
...@@ -650,21 +684,39 @@ static void flush_packet(AVFormatContext *ctx, int stream_index, ...@@ -650,21 +684,39 @@ static void flush_packet(AVFormatContext *ctx, int stream_index,
if (s->is_mpeg2) { if (s->is_mpeg2) {
put_byte(&ctx->pb, 0x80); /* mpeg2 id */ put_byte(&ctx->pb, 0x80); /* mpeg2 id */
pes_flags=0;
if (pts != AV_NOPTS_VALUE) { if (pts != AV_NOPTS_VALUE) {
if (dts != pts) { pes_flags |= 0x80;
put_byte(&ctx->pb, 0xc0); /* flags */ if (dts != pts)
put_byte(&ctx->pb, header_len - 3 + stuffing_size); pes_flags |= 0x40;
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, 0x00); /* flags */ /* Both the MPEG-2 and the SVCD standards demand that the
P-STD_buffer_size field be included in the first packet of
every stream. (see SVCD standard p. 26 V.2.3.1 and V.2.3.2
and MPEG-2 standard 2.7.7) */
if (stream->packet_number == 0)
pes_flags |= 0x01;
put_byte(&ctx->pb, pes_flags); /* flags */
put_byte(&ctx->pb, header_len - 3 + stuffing_size); put_byte(&ctx->pb, header_len - 3 + stuffing_size);
if (pes_flags & 0x80) /*write pts*/
put_timestamp(&ctx->pb, (pes_flags & 0x40) ? 0x03 : 0x02, pts);
if (pes_flags & 0x40) /*write dts*/
put_timestamp(&ctx->pb, 0x01, dts);
if (pes_flags & 0x01) { /*write pes extension*/
put_byte(&ctx->pb, 0x10); /* flags */
/* P-STD buffer info */
if (id == AUDIO_ID)
put_be16(&ctx->pb, 0x4000 | stream->max_buffer_size/128);
else
put_be16(&ctx->pb, 0x6000 | stream->max_buffer_size/1024);
} }
} else { } else {
if (pts != AV_NOPTS_VALUE) { if (pts != AV_NOPTS_VALUE) {
if (dts != pts) { if (dts != pts) {
...@@ -694,9 +746,14 @@ static void flush_packet(AVFormatContext *ctx, int stream_index, ...@@ -694,9 +746,14 @@ static void flush_packet(AVFormatContext *ctx, int stream_index,
} }
} }
if (s->is_mpeg2) if (s->is_mpeg2) {
/* special stuffing byte that is always written
to prevent accidental generation of start codes. */
put_byte(&ctx->pb, 0xff);
for(i=0;i<stuffing_size;i++) for(i=0;i<stuffing_size;i++)
put_byte(&ctx->pb, 0xff); put_byte(&ctx->pb, 0xff);
}
/* output data */ /* output data */
put_buffer(&ctx->pb, stream->buffer, payload_size - stuffing_size); put_buffer(&ctx->pb, stream->buffer, payload_size - stuffing_size);
...@@ -711,6 +768,11 @@ static void flush_packet(AVFormatContext *ctx, int stream_index, ...@@ -711,6 +768,11 @@ static void flush_packet(AVFormatContext *ctx, int stream_index,
put_flush_packet(&ctx->pb); put_flush_packet(&ctx->pb);
s->packet_number++; s->packet_number++;
/* only increase the stream packet number if this pack actually contains
something that is specific to this stream! I.e. a dedicated header
or some data.*/
if (!general_pack)
stream->packet_number++; stream->packet_number++;
stream->nb_frames = 0; stream->nb_frames = 0;
stream->frame_start_offset = 0; stream->frame_start_offset = 0;
...@@ -781,6 +843,7 @@ static void compute_pts_dts(AVStream *st, int64_t *ppts, int64_t *pdts, ...@@ -781,6 +843,7 @@ static void compute_pts_dts(AVStream *st, int64_t *ppts, int64_t *pdts,
pts = timestamp; pts = timestamp;
dts = timestamp; dts = timestamp;
} }
*ppts = pts & ((1LL << 33) - 1); *ppts = pts & ((1LL << 33) - 1);
*pdts = dts & ((1LL << 33) - 1); *pdts = dts & ((1LL << 33) - 1);
} }
...@@ -789,8 +852,10 @@ static int64_t update_scr(AVFormatContext *ctx,int stream_index,int64_t pts) ...@@ -789,8 +852,10 @@ static int64_t update_scr(AVFormatContext *ctx,int stream_index,int64_t pts)
{ {
MpegMuxContext *s = ctx->priv_data; MpegMuxContext *s = ctx->priv_data;
int64_t scr; int64_t scr;
StreamInfo *stream;
int i;
if (s->is_vcd) if (s->is_vcd) {
/* Since the data delivery rate is constant, SCR is computed /* Since the data delivery rate is constant, SCR is computed
using the formula C + i * 1200 where C is the start constant using the formula C + i * 1200 where C is the start constant
and i is the pack index. and i is the pack index.
...@@ -802,7 +867,21 @@ static int64_t update_scr(AVFormatContext *ctx,int stream_index,int64_t pts) ...@@ -802,7 +867,21 @@ static int64_t update_scr(AVFormatContext *ctx,int stream_index,int64_t pts)
will still be correct according to the standard. It just won't have will still be correct according to the standard. It just won't have
the "recommended" value).*/ the "recommended" value).*/
scr = 36000 + s->packet_number * 1200; scr = 36000 + s->packet_number * 1200;
#if 0
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 vcd: SCR above PTS (scr=%0.3f, stream index=%d, stream_pts=%0.3f).\n", scr/90000.0, i, stream->start_pts / 90000.0);
}
}
#endif
}
else { else {
/* XXX I believe this calculation of SCR is wrong. SCR /* XXX I believe this calculation of SCR is wrong. SCR
specifies at which time the data should enter the decoder. specifies at which time the data should enter the decoder.
Two packs cannot enter the decoder at the same time. */ Two packs cannot enter the decoder at the same time. */
...@@ -814,6 +893,17 @@ static int64_t update_scr(AVFormatContext *ctx,int stream_index,int64_t pts) ...@@ -814,6 +893,17 @@ static int64_t update_scr(AVFormatContext *ctx,int stream_index,int64_t pts)
scr = pts; scr = pts;
else else
scr = s->last_scr; 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; s->last_scr=scr;
...@@ -834,6 +924,28 @@ static int mpeg_mux_write_packet(AVFormatContext *ctx, int stream_index, ...@@ -834,6 +924,28 @@ static int mpeg_mux_write_packet(AVFormatContext *ctx, int stream_index,
compute_pts_dts(st, &pts, &dts, timestamp); compute_pts_dts(st, &pts, &dts, timestamp);
if(s->is_svcd) {
/* offset pts and dts slightly into the future to be able
to do the compatibility fix below.*/
pts = (pts + 2) & ((1LL << 33) - 1);
dts = (dts + 2) & ((1LL << 33) - 1);
if (stream->packet_number == 0 && dts == pts)
/* For the very first packet we want to force the DTS to be included.
This increases compatibility with lots of DVD players.
Since the MPEG-2 standard mandates that DTS is only written when
it is different from PTS we have to move it slightly into the past.*/
dts = (dts - 2) & ((1LL << 33) - 1);
}
if(s->is_vcd) {
/* We have to offset the PTS, so that it is consistent with the SCR.
SCR starts at 36000, but the first two packs contain only padding
and the first pack from the other stream, respectively, may also have
been written before.
So the real data starts at SCR 36000+3*1200. */
pts = (pts + 36000 + 3600) & ((1LL << 33) - 1);
dts = (dts + 36000 + 3600) & ((1LL << 33) - 1);
}
#if 0 #if 0
update_scr(ctx,stream_index,pts); update_scr(ctx,stream_index,pts);
......
...@@ -7,7 +7,7 @@ b166e89a9ec8c707573329d883c1b6f9 *./data/b-libav.asf ...@@ -7,7 +7,7 @@ b166e89a9ec8c707573329d883c1b6f9 *./data/b-libav.asf
./data/b-libav.asf CRC=525fdb22 ./data/b-libav.asf CRC=525fdb22
be8eb1b5705c8105e4727258e448cb24 *./data/b-libav.rm be8eb1b5705c8105e4727258e448cb24 *./data/b-libav.rm
356950 ./data/b-libav.rm 356950 ./data/b-libav.rm
072645ee8b36e432227cf9dff21561f0 *./data/b-libav.mpg e826aa1637ff15144ab484c1efca7fe7 *./data/b-libav.mpg
382976 ./data/b-libav.mpg 382976 ./data/b-libav.mpg
./data/b-libav.mpg CRC=eda0e29e ./data/b-libav.mpg CRC=eda0e29e
01a4130e776b8955fa99e477113e94fd *./data/b-libav.swf 01a4130e776b8955fa99e477113e94fd *./data/b-libav.swf
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment