OSDN Git Service

H.264 B-frame muxing for MP4, including B-pyramids. The latter are QuickTime incompat...
authorjbrjake <jbrjake@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Fri, 30 Mar 2007 00:50:20 +0000 (00:50 +0000)
committerjbrjake <jbrjake@b64f7644-9d1e-0410-96f1-a4d463321fa5>
Fri, 30 Mar 2007 00:50:20 +0000 (00:50 +0000)
"Most software today is very much like an Egyptian pyramid with millions of bricks piled on top of each other, with no structural integrity, but just done by brute force and thousands of slaves." -- Alan Kay

git-svn-id: svn://localhost/HandBrake/trunk@464 b64f7644-9d1e-0410-96f1-a4d463321fa5

libhb/encx264.c
libhb/internal.h
libhb/muxmp4.c

index 6973963..747bae9 100644 (file)
@@ -121,7 +121,16 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
                                }
                        }
 
-                       /*      Here's where the strings are passed to libx264 for parsing. */
+            /* Note b-pyramid here, so the initial delay can be doubled */
+            if (!(strcmp(name, "b-pyramid")))
+            {
+                   if (atoi(value) > 0)
+                   {
+                           job->areBframes = 2;
+                   }
+            }
+                       
+            /* Here's where the strings are passed to libx264 for parsing. */
                ret = x264_param_parse(&param, name, value);
        
                        /*      Let x264 sanity check the options for us*/
@@ -240,6 +249,9 @@ int encx264Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
     pv->pic_in.i_type    = X264_TYPE_AUTO;
     pv->pic_in.i_qpplus1 = 0;
 
+    /* Feed the input DTS to x264 so it can figure out proper output PTS */
+    pv->pic_in.i_pts = in->start;
+
     x264_encoder_encode( pv->x264, &nal, &i_nal,
                          &pv->pic_in, &pv->pic_out );
 
@@ -285,10 +297,37 @@ int encx264Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
                 buf->data[buf->size+1] = ( ( size - 4 ) >> 16 ) & 0xFF;
                 buf->data[buf->size+2] = ( ( size - 4 ) >>  8 ) & 0xFF;
                 buf->data[buf->size+3] = ( ( size - 4 ) >>  0 ) & 0xFF;
-                if( nal[i].i_ref_idc == NAL_PRIORITY_HIGHEST )
-                {
-                    buf->key = 1;
-                }
+
+        /* For IDR (key frames), buf->key = 1,
+           and the same for regular I-frames. */
+        if( (pv->pic_out.i_type == X264_TYPE_IDR) || (pv->pic_out.i_type == X264_TYPE_I) )
+        {
+        buf->key = 1;
+        }
+        /* For B-frames, buf->key = 2 */
+        else if( (pv->pic_out.i_type == X264_TYPE_B) )
+        {
+        buf->key = 2;
+        }                
+        /* This is for b-pyramid, which has reference b-frames
+           However, it doesn't seem to ever be used...
+           They just show up as buf->key == 2 like
+           regular b-frames. */
+        else if( (pv->pic_out.i_type == X264_TYPE_BREF) )
+        {
+            buf->key = 3;
+        }
+        /* For P-frames, buf->key = 0 */
+        else
+        {
+            buf->key = 0;
+        }
+
+        /* Store the output presentation time stamp
+           from x264 for use by muxmp4 in off-setting
+           b-frames with the CTTS atom. */
+        buf->encodedPTS = pv->pic_out.i_pts;
+
                 buf->size += size;
         }
     }
index fb57318..577ad30 100644 (file)
@@ -39,6 +39,9 @@ struct hb_buffer_s
     int64_t       stop;
     int           key;
 
+    /* Holds the output PTS from x264, for use by b-frame offsets in muxmp4.c */
+    int64_t     encodedPTS;
+
     int           x;
     int           y;
     int           width;
index f065272..1d599fb 100644 (file)
@@ -11,6 +11,9 @@
 
 void AddIPodUUID(MP4FileHandle, MP4TrackId);
 
+/* B-frame muxing variables */
+MP4SampleId thisSample = 0;
+uint64_t initDelay;
 
 struct hb_mux_object_s
 {
@@ -64,7 +67,7 @@ static int MP4Init( hb_mux_object_t * m )
         /* Stolen from mp4creator */
         MP4SetVideoProfileLevel( m->file, 0x7F );
 
-               if (job->areBframes == 1)
+               if (job->areBframes >= 1)
                {
                        hb_log("muxmp4: Adjusting duration for B-frames");
                    mux_data->track = MP4AddH264VideoTrack( m->file, job->arate,
@@ -238,8 +241,37 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
         duration = MP4_INVALID_DURATION;
     }
 
-    MP4WriteSample( m->file, mux_data->track, buf->data, buf->size,
-                    duration, 0, buf->key );
+    /* If for some reason the first frame muxmp4 gets isn't a key-frame,
+       drop frames until we get one. (Yes, very bad. Quick and dirty.)
+       This is for QuickTime--when it sees a non-IDR frame first, it
+       displays a white box instead of video until the second GOP.
+       Also, you've got to save the skipped duration to keep from
+       throwing off the offset values. */
+    if((mux_data->track == 1) && (thisSample == 0) && (buf->key != 1))
+    {
+           initDelay +=duration;
+           return 0;
+    }
+    /* When we do get the first keyframe, use its duration as the
+       initial delay added to the frame order offset for b-frames.
+       Because of b-pyramid, double this duration when there are
+       b-pyramids, as denoted by job->areBframes equalling 2. */
+    if ((mux_data->track == 1) && (thisSample == 0) && (buf->key == 1))
+    {
+        initDelay += duration * job->areBframes;
+        thisSample++;
+    }
+
+    /* Here's where the sample actually gets muxed. 
+       If it's an audio sample, don't offset the sample's playback.
+       If it's a video sample and there are no b-frames, ditto.
+       If there are b-frames, offset by the initDelay plus the
+       difference between the presentation time stamp x264 gives
+       and the decoding time stamp from the buffer data. */
+       MP4WriteSample( m->file, mux_data->track, buf->data, buf->size,
+            duration, ((mux_data->track != 1) || (job->areBframes==0)) ? 0 : ( initDelay + ((buf->encodedPTS - buf->start) * job->arate / 90000)),
+            (buf->key == 1) );
+                                
     return 0;
 }