OSDN Git Service

Soft Subtitle Support in the MP4 Muxer.
[handbrake-jp/handbrake-jp-git.git] / libhb / muxmp4.c
index 6795a33..f49ba74 100644 (file)
@@ -23,6 +23,8 @@ struct hb_mux_object_s
     // bias to keep render offsets in ctts atom positive (set up by encx264)
     int64_t init_delay;
 
+    uint64_t sum_sub_duration; // sum of subtitle frame durations so far
+
     /* Chapter state information for muxing */
     MP4TrackId chapter_track;
     int current_chapter;
@@ -32,6 +34,8 @@ struct hb_mux_object_s
 struct hb_mux_data_s
 {
     MP4TrackId track;
+    uint8_t    subtitle;
+    int        sub_format;
 };
 
 /* Tune video track chunk duration.
@@ -98,7 +102,7 @@ static int MP4Init( hb_mux_object_t * m )
     }
 
     /* Video track */
-    mux_data      = malloc( sizeof( hb_mux_data_t ) );
+    mux_data      = calloc(1, sizeof( hb_mux_data_t ) );
     job->mux_data = mux_data;
 
     if (!(MP4SetTimeScale( m->file, 90000 )))
@@ -220,7 +224,7 @@ static int MP4Init( hb_mux_object_t * m )
     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
     {
         audio = hb_list_item( title->list_audio, i );
-        mux_data = malloc( sizeof( hb_mux_data_t ) );
+        mux_data = calloc(1, sizeof( hb_mux_data_t ) );
         audio->priv.mux_data = mux_data;
 
         if( audio->config.out.codec == HB_ACODEC_AC3 )
@@ -399,6 +403,70 @@ static int MP4Init( hb_mux_object_t * m )
 
     }
 
+    for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
+    {
+        hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
+
+        if( subtitle && subtitle->format == TEXTSUB && 
+            subtitle->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 = MP4AddSubtitleTrack( m->file, 1 );
+
+            /* Tune track chunk duration */
+            MP4TuneTrackDurationPerChunk( m, mux_data->track );
+
+            const uint8_t textColor[4] = { 255,255,255,255 };
+            uint64_t subHeight = 60;
+
+            MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 2);
+
+            MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", job->width);
+            MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.height", subHeight);
+
+            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.bgColorAlpha", 255);
+
+            MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxBottom", subHeight);
+            MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.defTextBoxRight", job->width);
+
+            MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontID", 1);
+            MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontSize", 24);
+
+            MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorRed", textColor[0]);
+            MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorGreen", textColor[1]);
+            MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorBlue", textColor[2]);
+            MP4SetTrackIntegerProperty(m->file, mux_data->track, "mdia.minf.stbl.stsd.tx3g.fontColorAlpha", textColor[3]);
+            
+            /* translate the track */
+            uint8_t* val;
+            uint8_t nval[36];
+            uint32_t *ptr32 = (uint32_t*) nval;
+            uint32_t size;
+
+            MP4GetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", &val, &size);
+            memcpy(nval, val, size);
+
+            const uint32_t ytranslation = (job->height - subHeight) * 0x10000;
+                
+#ifdef WORDS_BIGENDIAN
+            ptr32[7] = ytranslation;
+#else
+            /* we need to switch the endianness, as the file format expects big endian */
+            ptr32[7] = ((ytranslation & 0x000000FF) << 24) + ((ytranslation & 0x0000FF00) << 8) + 
+                            ((ytranslation & 0x00FF0000) >> 8) + ((ytranslation & 0xFF000000) >> 24);
+#endif
+
+            MP4SetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", nval, size);  
+        }
+    }
+
     if (job->chapter_markers)
     {
         /* add a text track for the chapters. We add the 'chap' atom to track
@@ -437,7 +505,6 @@ 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;
-    int i;
 
     if( mux_data == job->mux_data )
     {
@@ -569,6 +636,51 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
             *job->die = 1;
         }
     }
+    else if (mux_data->subtitle)
+    {
+        if( mux_data->sub_format == TEXTSUB )
+        {
+            /* Write an empty sample */
+            if ( m->sum_sub_duration < buf->start )
+            {
+                uint8_t empty[2] = {0,0};
+                if( !MP4WriteSample( m->file,
+                                    mux_data->track,
+                                    empty,
+                                    2,
+                                    buf->start - m->sum_sub_duration,
+                                    0,
+                                    1 ))
+                {
+                    hb_error("Failed to write to output file, disk full?");
+                    *job->die = 1;
+                }
+                m->sum_sub_duration += buf->start - m->sum_sub_duration;
+            }
+
+            /* Write the subtitle sample */
+            uint8_t buffer[2048];
+            memcpy( buffer + 2, buf->data, buf->size );
+            buffer[0] = ( buf->size >> 8 ) & 0xff;
+            buffer[1] = buf->size & 0xff;
+            
+            if( !MP4WriteSample( m->file,
+                                 mux_data->track,
+                                 buffer,
+                                 buf->size,
+                                 buf->stop - buf->start,
+                                 0,
+                                 1 ))
+            {
+                hb_error("Failed to write to output file, disk full?");
+                *job->die = 1;
+            }
+
+            m->sum_sub_duration += buf->stop - buf->start;
+            hb_log("MuxMP4:Sub:%lld:%lld: %s", buf->start, buf->stop, buf->data);
+            hb_log("MuxMP4:Total time elapsed:%lld", m->sum_sub_duration);
+        }
+    }
     else
     {
         if( !MP4WriteSample( m->file,
@@ -584,43 +696,6 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
         }
     }
 
-    for( i = 0; i < hb_list_count( job->list_subtitle ); i++ )
-    {
-        hb_subtitle_t *subtitle = hb_list_item( job->list_subtitle, i );
-        
-        if( subtitle && subtitle->format == TEXTSUB && 
-            subtitle->dest == PASSTHRUSUB )
-        {
-            /*
-             * Should be adding this one if the timestamp is right.
-             */
-            hb_buffer_t *sub;
-
-            while( ( sub = hb_fifo_see( subtitle->fifo_out )) != NULL )
-            {
-                if( sub->size == 0 )
-                {
-                    /*
-                     * EOF 
-                     */ 
-                    hb_log("MuxMP4: Text Sub: EOF");
-                    sub = hb_fifo_get( subtitle->fifo_out );
-                    hb_buffer_close( &sub );
-                } else {
-                    if( sub->start < buf->start ) {
-                        sub = hb_fifo_get( subtitle->fifo_out );
-                        hb_log("MuxMP4: Text Sub:%lld: %s", sub->start, sub->data);
-                        hb_buffer_close( &sub );
-                    } else {
-                        /*
-                         * Not time yet
-                         */
-                        break;
-                    }
-                }
-            }
-        }
-    }
 
     return 0;
 }