OSDN Git Service

Allow mac gui to select AC-3 + AAC at the same time, still needs CLI work for the...
[handbrake-jp/handbrake-jp-git.git] / libhb / muxmp4.c
index c029487..ff30bd9 100644 (file)
 
 void AddIPodUUID(MP4FileHandle, MP4TrackId);
 
-/* B-frame muxing variables */
-MP4SampleId thisSample = 0;
-uint64_t initDelay;
-
 struct hb_mux_object_s
 {
     HB_MUX_COMMON;
@@ -157,7 +153,7 @@ static int MP4Init( hb_mux_object_t * m )
     
     if (m->file == MP4_INVALID_FILE_HANDLE)
     {
-        hb_log("muxmp4.c: MP4Create failed!");
+        hb_error("muxmp4.c: MP4Create failed!");
         *job->die = 1;
         return 0;
     }
@@ -172,7 +168,7 @@ static int MP4Init( hb_mux_object_t * m )
        timescale */
     if (!(MP4SetTimeScale( m->file, job->arate )))
     {
-        hb_log("muxmp4.c: MP4SetTimeScale failed!");
+        hb_error("muxmp4.c: MP4SetTimeScale failed!");
         *job->die = 1;
         return 0;
     }
@@ -182,7 +178,7 @@ static int MP4Init( hb_mux_object_t * m )
         /* Stolen from mp4creator */
         if(!(MP4SetVideoProfileLevel( m->file, 0x7F )))
         {
-            hb_log("muxmp4.c: MP4SetVideoProfileLevel failed!");
+            hb_error("muxmp4.c: MP4SetVideoProfileLevel failed!");
             *job->die = 1;
             return 0;
         }
@@ -200,7 +196,7 @@ static int MP4Init( hb_mux_object_t * m )
         MP4AddH264PictureParameterSet( m->file, mux_data->track,
                 job->config.h264.pps, job->config.h264.pps_length );
 
-               if( job->h264_level == 30)
+               if( job->h264_level == 30 || job->ipod_atom)
                {
                        hb_log("About to add iPod atom");
                        AddIPodUUID(m->file, mux_data->track);
@@ -211,7 +207,7 @@ static int MP4Init( hb_mux_object_t * m )
     {
         if(!(MP4SetVideoProfileLevel( m->file, MPEG4_SP_L3 )))
         {
-            hb_log("muxmp4.c: MP4SetVideoProfileLevel failed!");
+            hb_error("muxmp4.c: MP4SetVideoProfileLevel failed!");
             *job->die = 1;
             return 0;
         }
@@ -220,7 +216,7 @@ static int MP4Init( hb_mux_object_t * m )
                 MP4_MPEG4_VIDEO_TYPE );
         if (mux_data->track == MP4_INVALID_TRACK_ID)
         {
-            hb_log("muxmp4.c: MP4AddVideoTrack failed!");
+            hb_error("muxmp4.c: MP4AddVideoTrack failed!");
             *job->die = 1;
             return 0;
         }
@@ -230,7 +226,7 @@ static int MP4Init( hb_mux_object_t * m )
         if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
                 job->config.mpeg4.bytes, job->config.mpeg4.length )))
         {
-            hb_log("muxmp4.c: MP4SetTrackESConfiguration failed!");
+            hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
             *job->die = 1;
             return 0;
         }
@@ -252,20 +248,33 @@ static int MP4Init( hb_mux_object_t * m )
                        memcpy(nval, val, size);
 
                        float width, height;
-                       float widthRatio;
                        width = job->pixel_aspect_width;
                        height = job->pixel_aspect_height;
-                       widthRatio = (width / height) * 0x10000;
 
-                       uint32_t widthRatioInt;
-                       widthRatioInt = (uint32_t)widthRatio;
+           uint32_t pixelRatioInt;
+           if (width >= height)
+           {
+               pixelRatioInt = (uint32_t)((width / height) * 0x10000);
+
+#ifdef WORDS_BIGENDIAN
+               ptr32[0] = pixelRatioInt;
+#else
+               /* we need to switch the endianness, as the file format expects big endian */
+               ptr32[0] = ((pixelRatioInt & 0x000000FF) << 24) + ((pixelRatioInt & 0x0000FF00) << 8) + ((pixelRatioInt & 0x00FF0000) >> 8) + ((pixelRatioInt & 0xFF000000) >> 24);
+#endif
 
+           }
+           else
+           {
+               pixelRatioInt = (uint32_t)((height / width) * 0x10000);
 #ifdef WORDS_BIGENDIAN
-                       ptr32[0] = widthRatioInt;
+               ptr32[4] = pixelRatioInt;
 #else
-                       /* we need to switch the endianness, as the file format expects big endian */
-                       ptr32[0] = ((widthRatioInt & 0x000000FF) << 24) + ((widthRatioInt & 0x0000FF00) << 8) + ((widthRatioInt & 0x00FF0000) >> 8) + ((widthRatioInt & 0xFF000000) >> 24);
+                /* we need to switch the endianness, as the file format expects big endian */
+                ptr32[4] = ((pixelRatioInt & 0x000000FF) << 24) + ((pixelRatioInt & 0x0000FF00) << 8) + ((pixelRatioInt & 0x00FF0000) >> 8) + ((pixelRatioInt & 0xFF000000) >> 24);
 #endif
+            }
+
 
                        if(!MP4SetBytesProperty(m->file, "moov.trak.tkhd.reserved3", nval, size)) {
                                hb_log("Problem setting transform matrix");
@@ -294,12 +303,35 @@ static int MP4Init( hb_mux_object_t * m )
         mux_data = malloc( sizeof( hb_mux_data_t ) );
         audio->mux_data = mux_data;
 
-        mux_data->track = MP4AddAudioTrack( m->file,
+        if( job->acodec & HB_ACODEC_AC3 ||
+            job->audio_mixdowns[i] == HB_AMIXDOWN_AC3 )
+        {
+            mux_data->track = MP4AddAC3AudioTrack( 
+                m->file,
+                job->arate, 1536, MP4_MPEG4_AUDIO_TYPE );  
+            MP4SetTrackBytesProperty( 
+                m->file, mux_data->track,
+                "udta.name.value", 
+                (const u_int8_t*)"Surround", strlen("Surround"));
+        } else {
+            mux_data->track = MP4AddAudioTrack( 
+                m->file,
                 job->arate, 1024, MP4_MPEG4_AUDIO_TYPE );
-        MP4SetAudioProfileLevel( m->file, 0x0F );
-        MP4SetTrackESConfiguration( m->file, mux_data->track,
+            MP4SetTrackBytesProperty( 
+                m->file, mux_data->track,
+                "udta.name.value", 
+                (const u_int8_t*)"Stereo", strlen("Stereo"));
+            
+            MP4SetAudioProfileLevel( m->file, 0x0F );
+            MP4SetTrackESConfiguration( 
+                m->file, mux_data->track,
                 audio->config.aac.bytes, audio->config.aac.length );
-                
+
+            /* Set the correct number of channels for this track */
+            reserved2[9] = (u_int8_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown);
+            MP4SetTrackBytesProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.reserved2", reserved2, sizeof(reserved2));
+
+        }
         /* Set the language for this track */
         /* The language is stored as 5-bit text - 0x60 */
         language_code = audio->iso639_2[0] - 0x60;   language_code <<= 5;
@@ -307,9 +339,12 @@ static int MP4Init( hb_mux_object_t * m )
         language_code |= audio->iso639_2[2] - 0x60;
         MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.mdhd.language", language_code);
         
-        /* Set the correct number of channels for this track */
-        reserved2[9] = (u_int8_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown);
-        MP4SetTrackBytesProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.reserved2", reserved2, sizeof(reserved2));
+
+        /* Set the audio track alternate group */
+        MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 1);
+        
+        /* If we ever upgrade mpeg4ip, the line above should be replaced with the line below.*/
+//        MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels",  (u_int16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->amixdown));
         
         /* store a reference to the first audio track,
         so we can use it to feed the chapter text track's sample rate */
@@ -341,6 +376,13 @@ static int MP4Init( hb_mux_object_t * m )
         m->current_chapter = job->chapter_start;
        }
        
+    /* Add encoded-by metadata listing version and build date */
+    char *tool_string;
+    tool_string = (char *)malloc(80);
+    snprintf( tool_string, 80, "HandBrake %s %i", HB_VERSION, HB_BUILD);
+    MP4SetMetadataTool(m->file, tool_string);
+    free(tool_string);
+       
     return 0;
 }
 
@@ -359,19 +401,43 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
            (This is because of how durations for text tracks work in QT) */
         if( job->chapter_markers && buf->new_chap )
         {
-            struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
-        
-            MP4WriteSample(m->file, m->chapter_track, sample->sample, sample->length, sample->duration, 0, true);
+            struct hb_text_sample_s *sample;
+
+            /* If this is an x264 encode with bframes the IDR frame we're
+               trying to mark will be displayed offset by its renderOffset
+               so we need to offset the chapter by the same amount.
+               MP4 render offsets don't seem to work for text tracks so
+               we have to fudge the duration instead. */
+            duration = m->sum_dur - m->chapter_duration;
+
+            if ( job->areBframes )
+            {
+                duration += buf->renderOffset * job->arate / 90000;
+            }
+
+            sample = MP4GenerateChapterSample( m, duration );
+            
+            if( !MP4WriteSample(m->file, 
+                                m->chapter_track, 
+                                sample->sample, 
+                                sample->length, 
+                                sample->duration, 
+                                0, true) )
+            {
+                hb_error("Failed to write to output file, disk full?");
+                *job->die = 1;
+            }
             free(sample);
             m->current_chapter++;
-            m->chapter_duration = m->sum_dur;
+            m->chapter_duration += duration;
         }
     
         /* Video */
         /* Because we use the audio samplerate as the timescale,
            we have to use potentially variable durations so the video
            doesn't go out of sync */
-        duration    = ( buf->stop * job->arate / 90000 ) - m->sum_dur;
+        int64_t bias = ( buf->start * job->arate / 90000 ) - m->sum_dur;
+        duration = ( buf->stop - buf->start ) * job->arate / 90000 + bias;
         m->sum_dur += duration;
     }
     else
@@ -380,84 +446,77 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
         duration = MP4_INVALID_DURATION;
     }
 
-    /* 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->frametype & HB_FRAME_KEY) && (job->vcodec == HB_VCODEC_X264))
-    {
-        initDelay = buf->renderOffset;
-        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) || (job->vcodec != HB_VCODEC_X264)) ? 0 : (  buf->renderOffset * job->arate / 90000),
-            ((buf->frametype & HB_FRAME_KEY) != 0) );
+    if( !MP4WriteSample( m->file, 
+                         mux_data->track, 
+                         buf->data, 
+                         buf->size,
+                         duration, 
+                         ((mux_data->track != 1) || 
+                          (job->areBframes==0) || 
+                          (job->vcodec != HB_VCODEC_X264)) ? 0 : (  buf->renderOffset * job->arate / 90000),
+                         ((buf->frametype & HB_FRAME_KEY) != 0) ) )
+    {
+        hb_error("Failed to write to output file, disk full?");   
+        *job->die = 1;
+    }
                                 
     return 0;
 }
 
 static int MP4End( hb_mux_object_t * m )
-{
+{ 
+    hb_job_t   * job   = m->job;
+
     /* Write our final chapter marker */
     if( m->job->chapter_markers )
     {
         struct hb_text_sample_s *sample = MP4GenerateChapterSample( m, (m->sum_dur - m->chapter_duration) );
     
-        MP4WriteSample(m->file, m->chapter_track, sample->sample, sample->length, sample->duration, 0, true);
+        if( !MP4WriteSample(m->file, 
+                            m->chapter_track, 
+                            sample->sample, 
+                            sample->length, 
+                            sample->duration, 
+                            0, true) )
+        {
+            hb_error("Failed to write to output file, disk full?");      
+            *job->die = 1;
+        }
         free(sample);
     }
     
-#if 0
-    hb_job_t * job = m->job;
-    char filename[1024]; memset( filename, 0, 1024 );
-#endif
-
-    hb_job_t * job = m->job;
-    
     if (job->areBframes)
-    /* Walk the entire video sample table and find the minumum ctts value. */
     {
-           MP4SampleId count = MP4GetTrackNumberOfSamples( m->file, 1);
-           MP4SampleId i;
-           MP4Duration renderingOffset = 2000000000, tmp;
-           
-           // Find the smallest rendering offset
-           for(i = 1; i <= count; i++)
-           {
-               tmp = MP4GetSampleRenderingOffset(m->file, 1, i);
-               if(tmp < renderingOffset)
-                   renderingOffset = tmp;
-           }
-           
-           // Adjust all ctts values down by renderingOffset
-           for(i = 1; i <= count; i++)
-           {
-               MP4SetSampleRenderingOffset(m->file,1,i,
-                   MP4GetSampleRenderingOffset(m->file,1,i) - renderingOffset);
-           }
-           
            // Insert track edit to get A/V back in sync.  The edit amount is
            // the rendering offset of the first sample.
            MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, MP4GetSampleRenderingOffset(m->file,1,1),
                MP4GetTrackDuration(m->file, 1), 0);
+            if ( m->job->chapter_markers )
+            {
+                // apply same edit to chapter track to keep it in sync with video
+                MP4AddTrackEdit(m->file, m->chapter_track, MP4_INVALID_EDIT_ID,
+                                MP4GetSampleRenderingOffset(m->file,1,1),
+                                MP4GetTrackDuration(m->file, m->chapter_track), 0);
+            }
      }
 
     MP4Close( m->file );
 
-#if 0
-    hb_log( "muxmp4: optimizing file" );
-    snprintf( filename, 1024, "%s.tmp", job->file );
-    MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
-    remove( job->file );
-    rename( filename, job->file );
-#endif
+    if ( job->mp4_optimize )
+    {
+        hb_log( "muxmp4: optimizing file" );
+        char filename[1024]; memset( filename, 0, 1024 );
+        snprintf( filename, 1024, "%s.tmp", job->file );
+        MP4Optimize( job->file, filename, MP4_DETAILS_ERROR );
+        remove( job->file );
+        rename( filename, job->file );
+    }
 
     return 0;
 }