OSDN Git Service

LinGui: make Help->Guide work on windows/mingw
[handbrake-jp/handbrake-jp-git.git] / libhb / muxmp4.c
index 7ab95bd..bc523f8 100644 (file)
@@ -20,8 +20,7 @@ struct hb_mux_object_s
 
     int64_t sum_dur;    // sum of video frame durations so far
 
-    // bias to keep render offsets in ctts atom positive (set up by encx264)
-    int64_t init_delay;
+    hb_buffer_t *delay_buf;
 
     /* Chapter state information for muxing */
     MP4TrackId chapter_track;
@@ -146,8 +145,6 @@ static int MP4Init( hb_mux_object_t * m )
                        hb_deep_log( 2, "muxmp4: adding iPod atom");
                        MP4AddIPodUUID(m->file, mux_data->track);
                }
-
-        m->init_delay = job->config.h264.init_delay;
     }
     else /* FFmpeg or XviD */
     {
@@ -350,7 +347,8 @@ static int MP4Init( hb_mux_object_t * m )
                     (const uint8_t*)(audio->config.out.name),
                     strlen(audio->config.out.name));
             }
-        } else {
+        } else if( audio->config.out.codec == HB_ACODEC_FAAC ||
+                   audio->config.out.codec == HB_ACODEC_CA_AAC ) {
             mux_data->track = MP4AddAudioTrack(
                 m->file,
                 audio->config.out.samplerate, 1024, MP4_MPEG4_AUDIO_TYPE );
@@ -379,6 +377,32 @@ static int MP4Init( hb_mux_object_t * m )
 
             /* Set the correct number of channels for this track */
              MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (uint16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown));
+        } else if( audio->config.out.codec == HB_ACODEC_LAME ) {
+            mux_data->track = MP4AddAudioTrack(
+                m->file,
+                audio->config.out.samplerate, 1152, MP4_MPEG2_AUDIO_TYPE );
+
+            /* Tune track chunk duration */
+            MP4TuneTrackDurationPerChunk( m, mux_data->track );
+
+            if (audio->config.out.name == NULL) {
+                MP4SetTrackBytesProperty(
+                    m->file, mux_data->track,
+                    "udta.name.value",
+                    (const uint8_t*)"Stereo", strlen("Stereo"));
+            }
+            else {
+                MP4SetTrackBytesProperty(
+                    m->file, mux_data->track,
+                    "udta.name.value",
+                    (const uint8_t*)(audio->config.out.name),
+                    strlen(audio->config.out.name));
+            }
+
+            MP4SetAudioProfileLevel( m->file, 0x0F );
+
+            /* Set the correct number of channels for this track */
+             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.mp4a.channels", (uint16_t)HB_AMIXDOWN_GET_DISCRETE_CHANNEL_COUNT(audio->config.out.mixdown));
         }
 
         /* Set the language for this track */
@@ -451,7 +475,7 @@ static int MP4Init( hb_mux_object_t * m )
 
             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.dataReferenceIndex", 1);
             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.horizontalJustification", 1);
-            MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.verticalJustification", 0);
+            MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.verticalJustification", 255);
 
             MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.bgColorAlpha", 255);
 
@@ -496,6 +520,46 @@ static int MP4Init( hb_mux_object_t * m )
                 MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
             }
         }
+        else if( subtitle && subtitle->format == PICTURESUB && 
+            subtitle->config.dest == PASSTHRUSUB )
+        {
+            mux_data = calloc(1, sizeof( hb_mux_data_t ) );
+            subtitle->mux_data = mux_data;
+            mux_data->subtitle = 1;
+            mux_data->sub_format = subtitle->format;
+
+            mux_data->track = MP4AddSubpicTrack( m->file, 90000, subtitle->width, subtitle->height );
+
+            MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2);
+
+            /* Tune track chunk duration */
+            MP4TuneTrackDurationPerChunk( m, mux_data->track );
+            uint8_t palette[16][4];
+            int ii;
+            for ( ii = 0; ii < 16; ii++ )
+            {
+                palette[ii][0] = 0;
+                palette[ii][1] = (subtitle->palette[ii] >> 16) & 0xff;
+                palette[ii][2] = (subtitle->palette[ii] >> 8) & 0xff;
+                palette[ii][3] = (subtitle->palette[ii]) & 0xff;
+            }
+            if (!(MP4SetTrackESConfiguration( m->file, mux_data->track,
+                    (uint8_t*)palette, 16 * 4 )))
+            {
+                hb_error("muxmp4.c: MP4SetTrackESConfiguration failed!");
+                *job->die = 1;
+                return 0;
+            }
+            if ( !subtitle_default || subtitle->config.default_track ) {
+                /* Enable the default subtitle track */
+                MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_ENABLED | TRACK_IN_MOVIE));
+                subtitle_default = 1;
+            }
+            else
+            {
+                MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.flags", (TRACK_DISABLED | TRACK_IN_MOVIE));
+            }
+        }
     }
 
     if (job->chapter_markers)
@@ -698,7 +762,7 @@ static void hb_muxmp4_process_subtitle_style( uint8_t *input,
                     if (*(reader+3) == '>') {
                         if (stylestack && stylestack->style == BOLD) {
                             uint8_t style_record[12];
-                            stylestack->stop = writer - output - utf8_count - 1;
+                            stylestack->stop = writer - output - utf8_count;
                             hb_makestyleatom(stylestack, style_record);
 
                             memcpy(style + 10 + (12 * stylecount), style_record, 12);
@@ -719,7 +783,7 @@ static void hb_muxmp4_process_subtitle_style( uint8_t *input,
                     if (*(reader+3) == '>') {
                         if (stylestack && stylestack->style == UNDERLINE) {
                             uint8_t style_record[12];
-                            stylestack->stop = writer - output - utf8_count - 1;
+                            stylestack->stop = writer - output - utf8_count;
                             hb_makestyleatom(stylestack, style_record);
 
                             memcpy(style + 10 + (12 * stylecount), style_record, 12);
@@ -774,25 +838,37 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
     hb_job_t * job = m->job;
     int64_t duration;
     int64_t offset = 0;
+    hb_buffer_t *tmp;
 
     if( mux_data == job->mux_data )
     {
         /* Video */
 
-        // if there are b-frames compute the render offset
-        // (we'll need it for both the video frame & the chapter track)
-        if ( m->init_delay )
+        if( job->vcodec == HB_VCODEC_X264 )
         {
-            offset = buf->start + m->init_delay - m->sum_dur;
-            if ( offset < 0 )
+            if ( buf && buf->start < buf->renderOffset )
             {
-                hb_log("MP4Mux: illegal render offset %"PRId64", start %"PRId64","
-                       "stop %"PRId64", sum_dur %"PRId64,
-                       offset, buf->start, buf->stop, m->sum_dur );
-                offset = 0;
+                hb_log("MP4Mux: PTS %"PRId64" < DTS %"PRId64,
+                       buf->start, buf->renderOffset );
+                buf->renderOffset = buf->start;
             }
         }
 
+        // We delay muxing video by one frame so that we can calculate
+        // the dts to dts duration of the frames.
+        tmp = buf;
+        buf = m->delay_buf;
+        m->delay_buf = tmp;
+
+        if ( !buf )
+            return 0;
+
+        if( job->vcodec == HB_VCODEC_X264 )
+        {
+            // x264 supplies us with DTS, so offset is PTS - DTS
+            offset = buf->start - buf->renderOffset;
+        }
+
         /* Add the sample before the new frame.
            It is important that this be calculated prior to the duration
            of the new video sample, as we want to sync to right after it.
@@ -824,10 +900,54 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
             }
         }
 
-        // We're getting the frames in decode order but the timestamps are
-        // for presentation so we have to use durations and effectively
-        // compute a DTS.
-        duration = buf->stop - buf->start;
+        if( job->vcodec == HB_VCODEC_X264 )
+        {
+            // x264 supplies us with DTS
+            if ( m->delay_buf )
+            {
+                duration = m->delay_buf->renderOffset - buf->renderOffset;
+            }
+            else
+            {
+                duration = buf->stop - m->sum_dur;
+                // Due to how libx264 generates DTS, it's possible for the
+                // above calculation to be negative. 
+                //
+                // x264 generates DTS by rearranging PTS in this sequence:
+                // pts0 - delay, pts1 - delay, pts2 - delay, pts1, pts2, pts3...
+                //
+                // where delay == pts2.  This guarantees that DTS <= PTS for
+                // any frame, but also generates this sequence of durations:
+                // d0 + d1 + d0 + d1 + d2 + d3 ... + d(N-2)
+                // 
+                // so the sum up to the last frame is:
+                // sum_dur = d0 + d1 + d0 + d1 + d2 + d3 ... + d(N-3)
+                //
+                // while the original total duration of the video was:
+                // duration = d0 + d1 + d2 + d3 ... + d(N)
+                //
+                // Note that if d0 + d1 != d(N-1) + d(N), the total
+                // length of the video changes since d(N-1) and d(N) are
+                // replaced by d0 and d1 in the final duration sum.
+                //
+                // To keep the total length of the video the same as the source
+                // we try to make 
+                // d(N-2) = duration - sum_dur
+                //
+                // But if d0 + d1 >= d(N-1) + d(N), the above calculation
+                // results in a nagative value and we need to fix it.
+                if ( duration <= 0 )
+                    duration = 90000. / ((double)job->vrate / (double)job->vrate_base);
+            }
+        }
+        else
+        {
+            // We're getting the frames in decode order but the timestamps are
+            // for presentation so we have to use durations and effectively
+            // compute a DTS.
+            duration = buf->stop - buf->start;
+        }
+
         if ( duration <= 0 )
         {
             /* We got an illegal mp4/h264 duration. This shouldn't
@@ -968,6 +1088,39 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
 
             mux_data->sum_dur += (buf->stop - buf->start);
         }
+        else if( mux_data->sub_format == PICTURESUB )
+        {
+            /* Write an empty sample */
+            if ( mux_data->sum_dur < buf->start )
+            {
+                uint8_t empty[2] = {0,0};
+                if( !MP4WriteSample( m->file,
+                                    mux_data->track,
+                                    empty,
+                                    2,
+                                    buf->start - mux_data->sum_dur,
+                                    0,
+                                    1 ))
+                {
+                    hb_error("Failed to write to output file, disk full?");
+                    *job->die = 1;
+                } 
+                mux_data->sum_dur += buf->start - mux_data->sum_dur;
+            }
+            if( !MP4WriteSample( m->file,
+                                 mux_data->track,
+                                 buf->data,
+                                 buf->size,
+                                 buf->stop - buf->start,
+                                 0,
+                                 1 ))
+            {
+                hb_error("Failed to write to output file, disk full?");
+                *job->die = 1;
+            }
+
+            mux_data->sum_dur += (buf->stop - buf->start);
+        }
     }
     else
     {
@@ -986,7 +1139,7 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
             *job->die = 1;
         }
     }
-
+    hb_buffer_close( &buf );
 
     return 0;
 }
@@ -996,6 +1149,10 @@ static int MP4End( hb_mux_object_t * m )
     hb_job_t   * job   = m->job;
     hb_title_t * title = job->title;
 
+    // Flush the delayed frame
+    if ( m->delay_buf )
+        MP4Mux( m, job->mux_data, NULL );
+
     /* Write our final chapter marker */
     if( m->job->chapter_markers )
     {
@@ -1016,11 +1173,11 @@ static int MP4End( hb_mux_object_t * m )
         }
     }
 
-    if (job->areBframes)
+    if ( job->config.h264.init_delay )
     {
            // Insert track edit to get A/V back in sync.  The edit amount is
            // the init_delay.
-           int64_t edit_amt = m->init_delay;
+           int64_t edit_amt = job->config.h264.init_delay;
            MP4AddTrackEdit(m->file, 1, MP4_INVALID_EDIT_ID, edit_amt,
                            MP4GetTrackDuration(m->file, 1), 0);
             if ( m->job->chapter_markers )