changeset 452:11d07f14b077 libavformat

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>)
author michael
date Mon, 26 Apr 2004 22:16:06 +0000
parents 31784bdb76ee
children a699cf5c703d
files mpeg.c
diffstat 1 files changed, 138 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/mpeg.c	Sun Apr 25 13:30:21 2004 +0000
+++ b/mpeg.c	Mon Apr 26 22:16:06 2004 +0000
@@ -144,11 +144,14 @@
     } else
         put_bits(&pb, 6, s->audio_bound);
 
-    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 */
+    if (s->is_vcd) {
+        /* see VCD standard, p. IV-7*/
+        put_bits(&pb, 1, 0); 
+        put_bits(&pb, 1, 1);
+    } else {
+        put_bits(&pb, 1, 0); /* variable bitrate*/
+        put_bits(&pb, 1, 0); /* non constrainted bit stream */
+    }
     
     if (s->is_vcd) {
         /* see VCD standard p IV-7 */
@@ -286,6 +289,9 @@
             } else {
                 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; 
             s->audio_bound++;
             break;
@@ -294,7 +300,13 @@
             if (s->scr_stream_index == -1)
                 s->scr_stream_index = i;
             stream->id = mpv_id++;
-            stream->max_buffer_size = 46 * 1024; 
+            if (s->is_vcd)
+                /* see VCD standard, p. IV-7*/
+                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++;
             break;
         default:
@@ -465,7 +477,8 @@
         }
     }
 
-    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 system header and some padding (see VCD standard p. IV-6) 
            Add the padding size, so that the actual payload becomes 0.*/
@@ -473,8 +486,12 @@
     else {
         /* packet header size */
         buf_index += 6;
-        if (s->is_mpeg2)
+        if (s->is_mpeg2) {
             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 (dts != pts)
                 buf_index += 5 + 5;
@@ -554,6 +571,8 @@
     uint8_t buffer[128];
     int zero_trail_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;
     
@@ -595,11 +614,16 @@
            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,
+    if ((s->is_vcd && stream->packet_number==0)
+        || (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).
            In the case of an audio pack, 20 zero bytes are also added at
            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;
     }
 
@@ -613,6 +637,9 @@
         /* packet header */
         if (s->is_mpeg2) {
             header_len = 3;
+            if (stream->packet_number==0)
+                header_len += 3; /* PES extension */
+            header_len += 1; /* obligatory stuffing byte */
         } else {
             header_len = 0;
         }
@@ -639,6 +666,13 @@
         stuffing_size = payload_size - stream->buffer_ptr;
         if (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_be16(&ctx->pb, packet_size);
@@ -650,21 +684,39 @@
         if (s->is_mpeg2) {
             put_byte(&ctx->pb, 0x80); /* mpeg2 id */
 
+            pes_flags=0;
+
             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, 0x00); /* flags */
-                put_byte(&ctx->pb, header_len - 3 + stuffing_size);
+                pes_flags |= 0x80;
+                if (dts != pts)
+                    pes_flags |= 0x40;
             }
+
+            /* 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);
+
+            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 {
             if (pts != AV_NOPTS_VALUE) {
                 if (dts != pts) {
@@ -694,9 +746,14 @@
             }
         }
 
-        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++)
                 put_byte(&ctx->pb, 0xff);
+        }
 
         /* output data */
         put_buffer(&ctx->pb, stream->buffer, payload_size - stuffing_size);
@@ -711,7 +768,12 @@
     put_flush_packet(&ctx->pb);
     
     s->packet_number++;
-    stream->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->nb_frames = 0;
     stream->frame_start_offset = 0;
 }
@@ -781,6 +843,7 @@
         pts = timestamp;
         dts = timestamp;
     }
+
     *ppts = pts & ((1LL << 33) - 1);
     *pdts = dts & ((1LL << 33) - 1);
 }
@@ -789,8 +852,10 @@
 {
     MpegMuxContext *s = ctx->priv_data;
     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
            using the formula C + i * 1200 where C is the start constant
            and i is the pack index.
@@ -802,7 +867,21 @@
            will still be correct according to the standard. It just won't have
            the "recommended" value).*/
         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 {
+        
+        
         /* 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. */
@@ -814,6 +893,17 @@
             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;
@@ -834,6 +924,28 @@
     
     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
     update_scr(ctx,stream_index,pts);