OSDN Git Service

WinGui:
[handbrake-jp/handbrake-jp-git.git] / libhb / muxmp4.c
index dadfcdd..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;
@@ -60,7 +59,7 @@ static int MP4TuneTrackDurationPerChunk( hb_mux_object_t* m, MP4TrackId trackId
         return 0;
     }
 
-    hb_deep_log( 2, "muxmp4: track %u, chunk duration %llu", MP4FindTrackIndex( m->file, trackId ), dur );
+    hb_deep_log( 2, "muxmp4: track %u, chunk duration %"PRIu64, MP4FindTrackIndex( m->file, trackId ), dur );
     return 1;
 }
 
@@ -77,6 +76,7 @@ static int MP4Init( hb_mux_object_t * m )
     hb_audio_t    * audio;
     hb_mux_data_t * mux_data;
     int i;
+    int subtitle_default;
 
     /* Flags for enabling/disabling tracks in an MP4. */
     typedef enum { TRACK_DISABLED = 0x0, TRACK_ENABLED = 0x1, TRACK_IN_MOVIE = 0x2, TRACK_IN_PREVIEW = 0x4, TRACK_IN_POSTER = 0x8}  track_header_flags;
@@ -145,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 */
     {
@@ -349,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 );
@@ -378,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 */
@@ -403,12 +428,29 @@ static int MP4Init( hb_mux_object_t * m )
 
     }
 
+    // Quicktime requires that at least one subtitle is enabled,
+    // else it doesn't show any of the subtitles.
+    // So check to see if any of the subtitles are flagged to be
+    // the defualt.  The default will the the enabled track, else
+    // enable the first track.
+    subtitle_default = 0;
+    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->config.dest == PASSTHRUSUB )
+        {
+            if ( subtitle->config.default_track )
+                subtitle_default = 1;
+        }
+    }
     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 )
+            subtitle->config.dest == PASSTHRUSUB )
         {
             uint64_t width, height = 60;
             if( job->anamorphic.mode )
@@ -420,7 +462,7 @@ static int MP4Init( hb_mux_object_t * m )
             subtitle->mux_data = mux_data;
             mux_data->subtitle = 1;
             mux_data->sub_format = subtitle->format;
-            mux_data->track = MP4AddSubtitleTrack( m->file, 1 );
+            mux_data->track = MP4AddSubtitleTrack( m->file, 90000, width, height );
 
             MP4SetTrackLanguage(m->file, mux_data->track, subtitle->iso639_2);
 
@@ -431,12 +473,9 @@ static int MP4Init( hb_mux_object_t * m )
 
             MP4SetTrackIntegerProperty(m->file, mux_data->track, "tkhd.alternate_group", 2);
 
-            MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.width", width);
-            MP4SetTrackFloatProperty(m->file, mux_data->track, "tkhd.height", height);
-
             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);
 
@@ -471,6 +510,55 @@ static int MP4Init( hb_mux_object_t * m )
 #endif
 
             MP4SetTrackBytesProperty(m->file, mux_data->track, "tkhd.matrix", nval, size);  
+            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));
+            }
+        }
+        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));
+            }
         }
     }
 
@@ -506,31 +594,281 @@ static int MP4Init( hb_mux_object_t * m )
     return 0;
 }
 
+typedef struct stylerecord_s {
+    enum style_s {ITALIC, BOLD, UNDERLINE} style;
+    uint16_t start;
+    uint16_t stop;
+    struct stylerecord_s *next;
+} stylerecord;
+
+static void hb_makestylerecord( stylerecord **stack, 
+                                enum style_s style, int start )
+{
+    stylerecord *record = calloc( sizeof( stylerecord ), 1 );
+
+    if( record ) 
+    {
+        record->style = style;
+        record->start = start;
+        record->next = *stack;
+        *stack = record;
+    }
+}
+
+static void hb_makestyleatom( stylerecord *record, uint8_t *style)
+{
+    uint8_t face = 1;
+    hb_deep_log(3, "Made style '%s' from %d to %d", 
+           record->style == ITALIC ? "Italic" : record->style == BOLD ? "Bold" : "Underline", record->start, record->stop);
+    
+    switch( record->style )
+    {
+    case ITALIC:
+        face = 2;
+        break;
+    case BOLD:
+        face = 1;
+        break;
+    case UNDERLINE:
+        face = 4;
+        break;
+    default:
+        face = 2;
+        break;
+    }
+
+    style[0] = (record->start >> 8) & 0xff; // startChar
+    style[1] = record->start & 0xff;
+    style[2] = (record->stop >> 8) & 0xff;   // endChar
+    style[3] = record->stop & 0xff;
+    style[4] = (1 >> 8) & 0xff;    // font-ID
+    style[5] = 1 & 0xff;
+    style[6] = face;   // face-style-flags: 1 bold; 2 italic; 4 underline
+    style[7] = 24;      // font-size
+    style[8] = 255;     // r
+    style[9] = 255;     // g
+    style[10] = 255;    // b
+    style[11] = 255;    // a
+}
+
+/*
+ * Copy the input to output removing markup and adding markup to the style
+ * atom where appropriate.
+ */
+static void hb_muxmp4_process_subtitle_style( uint8_t *input,
+                                              uint8_t *output,
+                                              uint8_t *style, uint16_t *stylesize )
+{
+    uint8_t *reader = input;
+    uint8_t *writer = output;
+    uint8_t stylecount = 0;
+    uint16_t utf8_count = 0;         // utf8 count from start of subtitle
+    stylerecord *stylestack = NULL;
+    stylerecord *oldrecord = NULL;
+    
+    while(*reader != '\0') {
+        if( ( *reader & 0xc0 ) == 0x80 ) 
+        {
+            /*
+             * Track the utf8_count when doing markup so that we get the tx3g stops
+             * based on UTF8 chr counts rather than bytes.
+             */
+            utf8_count++;
+            hb_deep_log( 3, "MuxMP4: Counted %d UTF-8 chrs within subtitle so far", 
+                             utf8_count);
+        }
+        if (*reader == '<') {
+            /*
+             * possible markup, peek at the next chr
+             */
+            switch(*(reader+1)) {
+            case 'i':
+                if (*(reader+2) == '>') {
+                    reader += 3;
+                    hb_makestylerecord(&stylestack, ITALIC, (writer - output - utf8_count));
+                } else {
+                    *writer++ = *reader++;
+                }
+                break;
+            case 'b':
+                if (*(reader+2) == '>') {
+                    reader += 3; 
+                    hb_makestylerecord(&stylestack, BOLD, (writer - output - utf8_count));
+                } else {
+                    *writer++ = *reader++;  
+                }
+                break;
+            case 'u': 
+                if (*(reader+2) == '>') {
+                    reader += 3;
+                    hb_makestylerecord(&stylestack, UNDERLINE, (writer - output - utf8_count));
+                } else {
+                    *writer++ = *reader++;
+                }
+                break;
+            case '/':
+                switch(*(reader+2)) {
+                case 'i':
+                    if (*(reader+3) == '>') {
+                        /*
+                         * Check whether we then immediately start more markup of the same type, if so then
+                         * lets not close it now and instead continue this markup.
+                         */
+                        if ((*(reader+4) && *(reader+4) == '<') &&
+                            (*(reader+5) && *(reader+5) == 'i') &&
+                            (*(reader+6) && *(reader+6) == '>')) {
+                            /*
+                             * Opening italics right after, so don't close off these italics.
+                             */
+                            hb_deep_log(3, "Joining two sets of italics");
+                            reader += (4 + 3);
+                            continue;
+                        }
+
+
+                        if ((*(reader+4) && *(reader+4) == ' ') && 
+                            (*(reader+5) && *(reader+5) == '<') &&
+                            (*(reader+6) && *(reader+6) == 'i') &&
+                            (*(reader+7) && *(reader+7) == '>')) {
+                            /*
+                             * Opening italics right after, so don't close off these italics.
+                             */
+                            hb_deep_log(3, "Joining two sets of italics (plus space)");
+                            reader += (4 + 4);
+                            *writer++ = ' ';
+                            continue;
+                        }
+                        if (stylestack && stylestack->style == ITALIC) {
+                            uint8_t style_record[12];
+                            stylestack->stop = writer - output - utf8_count;
+                            hb_makestyleatom(stylestack, style_record);
+
+                            memcpy(style + 10 + (12 * stylecount), style_record, 12);
+                            stylecount++;
+
+                            oldrecord = stylestack;
+                            stylestack = stylestack->next;
+                            free(oldrecord);
+                        } else {
+                            hb_error("Mismatched Subtitle markup '%s'", input);
+                        }
+                        reader += 4;
+                    } else {
+                        *writer++ = *reader++;
+                    }
+                    break;
+                case 'b':
+                    if (*(reader+3) == '>') {
+                        if (stylestack && stylestack->style == BOLD) {
+                            uint8_t style_record[12];
+                            stylestack->stop = writer - output - utf8_count;
+                            hb_makestyleatom(stylestack, style_record);
+
+                            memcpy(style + 10 + (12 * stylecount), style_record, 12);
+                            stylecount++;
+                            oldrecord = stylestack;
+                            stylestack = stylestack->next;
+                            free(oldrecord);
+                        } else {
+                            hb_error("Mismatched Subtitle markup '%s'", input);
+                        }
+
+                        reader += 4;
+                    } else {
+                        *writer++ = *reader++;
+                    }
+                    break;
+                case 'u': 
+                    if (*(reader+3) == '>') {
+                        if (stylestack && stylestack->style == UNDERLINE) {
+                            uint8_t style_record[12];
+                            stylestack->stop = writer - output - utf8_count;
+                            hb_makestyleatom(stylestack, style_record);
+
+                            memcpy(style + 10 + (12 * stylecount), style_record, 12);
+                            stylecount++;
+
+                            oldrecord = stylestack;
+                            stylestack = stylestack->next;
+                            free(oldrecord);
+                        } else {
+                            hb_error("Mismatched Subtitle markup '%s'", input);
+                        }
+                        reader += 4;
+                    } else {
+                        *writer++ = *reader++;
+                    }
+                    break;
+                default:
+                    *writer++ = *reader++;
+                    break;
+                }
+                break;
+            default:
+                *writer++ = *reader++;
+                break;
+            }
+        } else {
+            *writer++ = *reader++;
+        }
+    }
+    *writer = '\0';
+
+    if( stylecount )
+    {
+        *stylesize = 10 + ( stylecount * 12 );
+
+        memcpy( style + 4, "styl", 4);
+
+        style[0] = 0;
+        style[1] = 0;
+        style[2] = (*stylesize >> 8) & 0xff;
+        style[3] = *stylesize & 0xff;
+        style[8] = (stylecount >> 8) & 0xff;
+        style[9] = stylecount & 0xff;
+
+    }
+
+}
+
 static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
                    hb_buffer_t * buf )
 {
     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 %lld, start %lld,"
-                       "stop %lld, sum_dur %lld",
-                       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.
@@ -562,18 +900,62 @@ 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
                be possible and usually indicates a bug in the upstream code.
                Complain in the hope that someone will go find the bug but
                try to fix the error so that the file will still be playable. */
-            hb_log("MP4Mux: illegal duration %lld, start %lld,"
-                   "stop %lld, sum_dur %lld",
+            hb_log("MP4Mux: illegal duration %"PRId64", start %"PRId64","
+                   "stop %"PRId64", sum_dur %"PRId64,
                    duration, buf->start, buf->stop, m->sum_dur );
             /* we don't know when the next frame starts so we can't pick a
                valid duration for this one. we pick something "short"
@@ -664,17 +1046,71 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
                 } 
                 mux_data->sum_dur += buf->start - mux_data->sum_dur;
             }
+            uint8_t styleatom[2048];;
+            uint16_t stylesize = 0;
+            uint8_t buffer[2048];
+            uint16_t buffersize = 0;
+            uint8_t output[2048];
+
+            *buffer = '\0';
+
+            /*
+             * Copy the subtitle into buffer stripping markup and creating
+             * style atoms for them.
+             */
+            hb_muxmp4_process_subtitle_style( buf->data,
+                                              buffer,
+                                              styleatom, &stylesize );
+
+            buffersize = strlen((char*)buffer);
+
+            hb_deep_log(3, "MuxMP4:Sub:%fs:%"PRId64":%"PRId64":%"PRId64": %s",
+                        (float)buf->start / 90000, buf->start, buf->stop, 
+                        (buf->stop - buf->start), buffer);
 
             /* 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;
+            memcpy( output + 2, buffer, buffersize );
+            memcpy( output + 2 + buffersize, styleatom, stylesize);
+            output[0] = ( buffersize >> 8 ) & 0xff;
+            output[1] = buffersize & 0xff;
 
             if( !MP4WriteSample( m->file,
                                  mux_data->track,
-                                 buffer,
-                                 buf->size + 2,
+                                 output,
+                                 buffersize + stylesize + 2,
+                                 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 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 ))
@@ -684,9 +1120,6 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
             }
 
             mux_data->sum_dur += (buf->stop - buf->start);
-            hb_deep_log(3, "MuxMP4:Sub:%fs:%lld:%lld:%lld: %s", (float)buf->start / 90000, buf->start, buf->stop, 
-                   (buf->stop - buf->start), buf->data);
-            hb_deep_log(3, "MuxMP4:Total time elapsed:%lld", mux_data->sum_dur);
         }
     }
     else
@@ -706,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;
 }
@@ -716,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 )
     {
@@ -736,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 )
@@ -768,13 +1205,20 @@ static int MP4End( hb_mux_object_t * m )
         MP4TagsFetch( tags, m->file );
 
         /* populate */
-        MP4TagsSetName( tags, md->name );
-        MP4TagsSetArtist( tags, md->artist );
-        MP4TagsSetComposer( tags, md->composer );
-        MP4TagsSetComments( tags, md->comment );
-        MP4TagsSetReleaseDate( tags, md->release_date );
-        MP4TagsSetAlbum( tags, md->album );
-        MP4TagsSetGenre( tags, md->genre );
+        if( strlen( md->name ))
+            MP4TagsSetName( tags, md->name );
+        if( strlen( md->artist ))
+            MP4TagsSetArtist( tags, md->artist );
+        if( strlen( md->composer ))
+            MP4TagsSetComposer( tags, md->composer );
+        if( strlen( md->comment ))
+            MP4TagsSetComments( tags, md->comment );
+        if( strlen( md->release_date ))
+            MP4TagsSetReleaseDate( tags, md->release_date );
+        if( strlen( md->album ))
+            MP4TagsSetAlbum( tags, md->album );
+        if( strlen( md->genre ))
+            MP4TagsSetGenre( tags, md->genre );
 
         if( md->coverart )
         {