[vlc-commits] demux: mp4: rewrite main non fragmented Demux logic

Francois Cartegnie git at videolan.org
Mon Oct 31 19:22:49 CET 2016


vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Fri Oct 28 15:19:54 2016 +0200| [1583a49459f680250dc5ec951215c661c26b1b20] | committer: Francois Cartegnie

demux: mp4: rewrite main non fragmented Demux logic

Makes main demux aware of interleaving, and avoid
access layer seeks. Also improves the large interleaves
and non interleaved playback.

Fixes the pcr jumps, as a min() dts based logic isn't
suitable for edit lists.

Improves empty edits handling by keeping demuxing on
empty samples time.

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=1583a49459f680250dc5ec951215c661c26b1b20
---

 modules/demux/mp4/mp4.c | 319 +++++++++++++++++++++++++-----------------------
 1 file changed, 168 insertions(+), 151 deletions(-)

diff --git a/modules/demux/mp4/mp4.c b/modules/demux/mp4/mp4.c
index 2fcc649..ba2505d 100644
--- a/modules/demux/mp4/mp4.c
+++ b/modules/demux/mp4/mp4.c
@@ -79,7 +79,6 @@ struct demux_sys_t
     bool         b_fragmented;   /* fMP4 */
     bool         b_seekable;
     bool         b_fastseekable;
-    bool         b_seekmode;
 
     bool            b_index_probed;
     bool            b_fragments_probed;
@@ -106,6 +105,9 @@ struct demux_sys_t
     int64_t  i_preroll_start;
 };
 
+#define DEMUX_INCREMENT (CLOCK_FREQ / 8) /* How far the pcr will go, each round */
+#define DEMUX_TRACK_MAX_PRELOAD (CLOCK_FREQ * 15) /* maximum preloading, to deal with interleaving */
+
 /*****************************************************************************
  * Declaration of local function
  *****************************************************************************/
@@ -125,7 +127,7 @@ static uint32_t MP4_TrackGetReadSize( mp4_track_t *, uint32_t * );
 static int      MP4_TrackNextSample( demux_t *, mp4_track_t *, uint32_t );
 static void     MP4_TrackSetELST( demux_t *, mp4_track_t *, int64_t );
 
-static void     MP4_UpdateSeekpoint( demux_t * );
+static void     MP4_UpdateSeekpoint( demux_t *, int64_t );
 
 static MP4_Box_t * MP4_GetTrexByTrackID( MP4_Box_t *p_moov, const uint32_t i_id );
 static void MP4_GetDefaultSizeAndDuration( demux_t *p_demux,
@@ -400,6 +402,13 @@ static block_t * MP4_EIA608_Convert( block_t * p_block )
     return p_newblock;
 }
 
+static uint32_t MP4_TrackGetRunSeq( mp4_track_t *p_track )
+{
+    if( p_track->i_chunk_count > 0 )
+        return p_track->chunk[p_track->i_chunk].i_virtual_run_number;
+    return 0;
+}
+
 /* Analyzes chunks to find max interleave length
  * sets flat flag if no interleaving is in use */
 static void MP4_GetInterleaving( demux_t *p_demux, uint64_t *pi_max_contiguous, bool *pb_flat )
@@ -587,7 +596,6 @@ static int Open( vlc_object_t * p_this )
     vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable );
     vlc_stream_Control( p_demux->s, STREAM_CAN_FASTSEEK,
                         &p_sys->b_fastseekable );
-    p_sys->b_seekmode = p_sys->b_fastseekable;
 
     /*Set exported functions */
     p_demux->pf_demux = Demux;
@@ -907,16 +915,15 @@ static int Open( vlc_object_t * p_this )
     DumpFragments( VLC_OBJECT(p_demux), &p_sys->fragments, p_sys->i_timescale );
 #endif
 
-    if( !p_sys->b_fragmented && p_sys->i_tracks > 1 && p_sys->b_seekable && !p_sys->b_seekmode )
+    if( !p_sys->b_fragmented && p_sys->i_tracks > 1 && !p_sys->b_fastseekable )
     {
         uint64_t i_max_continuity;
         bool b_flat;
         MP4_GetInterleaving( p_demux, &i_max_continuity, &b_flat );
         if( b_flat )
-        {
-                msg_Warn( p_demux, "that media doesn't look interleaved, will need to seek");
-                p_sys->b_seekmode = true;
-        }
+            msg_Warn( p_demux, "that media doesn't look interleaved, will need to seek");
+        else if( i_max_continuity > DEMUX_TRACK_MAX_PRELOAD )
+            msg_Warn( p_demux, "that media doesn't look properly interleaved, will need to seek");
     }
 
     /* */
@@ -1081,17 +1088,97 @@ static block_t * MP4_RTPHint_Convert( demux_t *p_demux, block_t *p_block, vlc_fo
  *****************************************************************************
  * TODO check for newly selected track (ie audio upt to now )
  *****************************************************************************/
+static int DemuxTrack( demux_t *p_demux, mp4_track_t *tk, uint64_t i_readpos )
+{
+    uint32_t i_nb_samples = 0;
+    uint32_t i_samplessize = 0;
+
+    if( !tk->b_ok || tk->i_sample >= tk->i_sample_count )
+        return VLC_DEMUXER_EOF;
+
+    if( tk->b_chapters_source )
+        return VLC_DEMUXER_EGENERIC;
+
+    uint32_t i_run_seq = MP4_TrackGetRunSeq( tk );
+    mtime_t i_current_nzdts = MP4_TrackGetDTS( p_demux, tk );
+    const mtime_t i_demux_max_nzdts = i_current_nzdts + DEMUX_TRACK_MAX_PRELOAD;
+    for( ; i_demux_max_nzdts > i_current_nzdts; )
+    {
+        if( tk->i_sample >= tk->i_sample_count )
+            break;
+
+#if 0
+        msg_Dbg( p_demux, "tk(%i)=%"PRId64" mv=%"PRId64" pos=%"PRIu64, tk->i_track_ID,
+                 MP4_TrackGetDTS( p_demux, tk ),
+                 MP4_GetMoviePTS( p_sys ), i_readpos );
+#endif
+
+        i_samplessize = MP4_TrackGetReadSize( tk, &i_nb_samples );
+        if( i_samplessize > 0 )
+        {
+            block_t *p_block;
+            int64_t i_delta;
+
+            if( vlc_stream_Tell( p_demux->s ) != i_readpos )
+            {
+                if( vlc_stream_Seek( p_demux->s, i_readpos ) )
+                {
+                    msg_Warn( p_demux, "track[0x%x] will be disabled (eof?)"
+                                       ": Failed to seek to %"PRIu64,
+                              tk->i_track_ID, i_readpos );
+                    MP4_TrackUnselect( p_demux, tk );
+                    goto end;
+                }
+            }
+
+            /* now read pes */
+            if( !(p_block = MP4_Block_Read( p_demux, tk, i_samplessize )) )
+            {
+                msg_Warn( p_demux, "track[0x%x] will be disabled (eof?)"
+                                   ": Failed to read %d bytes sample at %"PRIu64,
+                          tk->i_track_ID, i_samplessize, i_readpos );
+                MP4_TrackUnselect( p_demux, tk );
+                goto end;
+            }
+
+            /* dts */
+            p_block->i_dts = VLC_TS_0 + i_current_nzdts;
+            /* pts */
+            if( MP4_TrackGetPTSDelta( p_demux, tk, &i_delta ) )
+                p_block->i_pts = p_block->i_dts + i_delta;
+            else if( tk->fmt.i_cat != VIDEO_ES )
+                p_block->i_pts = p_block->i_dts;
+            else
+                p_block->i_pts = VLC_TS_INVALID;
+
+            MP4_Block_Send( p_demux, tk, p_block );
+        }
+
+        /* Next sample */
+        if ( i_nb_samples ) /* sample size could be 0, need to go fwd. see return */
+            MP4_TrackNextSample( p_demux, tk, i_nb_samples );
+
+        uint32_t i_next_run_seq = MP4_TrackGetRunSeq( tk );
+        if( i_next_run_seq != i_run_seq )
+            break;
+
+        i_current_nzdts = MP4_TrackGetDTS( p_demux, tk );
+        i_readpos = MP4_TrackGetPos( tk );
+    }
+
+    return VLC_DEMUXER_SUCCESS;
+
+end:
+    return VLC_DEMUXER_EGENERIC;
+}
+
 static int Demux( demux_t *p_demux )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
     unsigned int i_track;
 
-
-    unsigned int i_track_selected;
-
     /* check for newly selected/unselected track */
-    for( i_track = 0, i_track_selected = 0; i_track < p_sys->i_tracks;
-         i_track++ )
+    for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
     {
         mp4_track_t *tk = &p_sys->track[i_track];
         bool b;
@@ -1112,178 +1199,108 @@ static int Demux( demux_t *p_demux )
         {
             MP4_TrackSelect( p_demux, tk, MP4_GetMoviePTS( p_sys ) );
         }
-
-        if( tk->b_selected )
-        {
-            i_track_selected++;
-        }
     }
 
-    if( i_track_selected <= 0 )
+    const mtime_t i_nztime = MP4_GetMoviePTS( p_sys );
+
+    /* We demux/set pcr, even without selected tracks, (empty edits, ...) */
+    if( p_sys->i_pcr != VLC_TS_INVALID /* not after a seek */ )
     {
-        p_sys->i_time += __MAX( p_sys->i_timescale / 10 , 1 );
-        if( p_sys->i_timescale > 0 )
+        bool b_eof = true;
+        for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
         {
-            int64_t i_length = CLOCK_FREQ *
-                               (mtime_t)p_sys->i_overall_duration /
-                               (mtime_t)p_sys->i_timescale;
-            if( MP4_GetMoviePTS( p_sys ) >= i_length )
-                return VLC_DEMUXER_EOF;
-            return VLC_DEMUXER_SUCCESS;
+            mp4_track_t *tk = &p_sys->track[i_track];
+            if( !tk->b_ok || tk->b_chapters_source )
+                continue;
+            /* Test for EOF on each track (samples count, edit list) */
+            b_eof &= ( i_nztime > MP4_TrackGetDTS( p_demux, tk ) );
         }
-
-        msg_Warn( p_demux, "no track selected, exiting..." );
-        return VLC_DEMUXER_EOF;
+        if( b_eof )
+            return VLC_DEMUXER_EOF;
     }
 
-    /* */
-    MP4_UpdateSeekpoint( p_demux );
-
-    /* first wait for the good time to read a packet */
-    p_sys->i_pcr = MP4_GetMoviePTS( p_sys );
+    /* !important! Ensure clock is set before sending data */
+    if( p_sys->i_pcr == VLC_TS_INVALID )
+        es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + i_nztime );
 
-    bool b_data_sent = false;
-
-    /* Find next track matching contiguous data */
-    mp4_track_t *tk = NULL;
-    uint64_t i_candidate_pos = UINT64_MAX;
-    mtime_t i_candidate_dts = INT64_MAX;
-    for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
+    const mtime_t i_scaledincrement = DEMUX_INCREMENT * p_sys->i_timescale / CLOCK_FREQ;
+    int i_status;
+    /* demux up to increment amount of data on every track, or just set pcr if empty data */
+    for( ;; )
     {
-        mp4_track_t *tk_tmp = &p_sys->track[i_track];
-        if( !tk_tmp->b_ok || tk_tmp->b_chapters_source || !tk_tmp->b_selected || tk_tmp->i_sample >= tk_tmp->i_sample_count )
-            continue;
+        mp4_track_t *tk = NULL;
+        i_status = VLC_DEMUXER_EGENERIC;
 
-        if ( p_sys->b_seekmode )
-        {
-            mtime_t i_dts = MP4_TrackGetDTS( p_demux, tk_tmp );
-            if ( i_dts <= i_candidate_dts )
-            {
-                tk = tk_tmp;
-                i_candidate_dts = i_dts;
-                i_candidate_pos = MP4_TrackGetPos( tk_tmp );
-            }
-        }
-        else
+        /* First pass, find any track within our target increment, ordered by position */
+        for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
         {
-            /* Try to avoid seeking on non fastseekable. Will fail with non interleaved content */
-            uint64_t i_pos = MP4_TrackGetPos( tk_tmp );
-            if ( i_pos <= i_candidate_pos )
+            mp4_track_t *tk_tmp = &p_sys->track[i_track];
+            if( !tk_tmp->b_ok || tk_tmp->b_chapters_source ||
+                !tk_tmp->b_selected || tk_tmp->i_sample >= tk_tmp->i_sample_count )
+                continue;
+
+            if ( MP4_TrackGetDTS( p_demux, tk_tmp ) <= i_nztime + DEMUX_INCREMENT )
             {
-                i_candidate_pos = i_pos;
-                tk = tk_tmp;
+                if( tk == NULL || MP4_TrackGetPos( tk_tmp ) < MP4_TrackGetPos( tk ) )
+                    tk = tk_tmp;
             }
         }
-    }
-
-    uint32_t i_nb_samples = 0;
-    uint32_t i_samplessize = 0;
 
-    if ( !tk )
-    {
-        msg_Dbg( p_demux, "Could not select track by data position" );
-        goto end;
-    }
-    else if ( p_sys->b_seekmode )
-    {
-        if( vlc_stream_Seek( p_demux->s, i_candidate_pos ) )
+        if( tk )
         {
-            msg_Warn( p_demux, "track[0x%x] will be disabled (eof?)",
-                      tk->i_track_ID );
-            MP4_TrackUnselect( p_demux, tk );
-            goto end;
-        }
-    }
-
-#if 0
-    msg_Dbg( p_demux, "tk(%i)=%"PRId64" mv=%"PRId64" pos=%"PRIu64, tk->i_track_ID,
-             MP4_TrackGetDTS( p_demux, tk ),
-             MP4_GetMoviePTS( p_sys ), i_candidate_pos );
-#endif
-
-    i_samplessize = MP4_TrackGetReadSize( tk, &i_nb_samples );
-    if( i_samplessize > 0 )
-    {
-        block_t *p_block;
-        int64_t i_delta;
-        uint64_t i_current_pos;
-
-        /* go,go go ! */
-        i_current_pos = vlc_stream_Tell( p_demux->s );
-
-        if( i_current_pos != i_candidate_pos )
-        {
-            if( vlc_stream_Seek( p_demux->s, i_candidate_pos ) )
+            /* Second pass, refine and find any best candidate having a chunk pos closer than
+             * current candidate (avoids seeks when increment falls between the 2) from
+             * current position, but within extended interleave time */
+            for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
             {
-                msg_Warn( p_demux, "track[0x%x] will be disabled (eof?)"
-                          ": Failed to seek to %"PRIu64,
-                          tk->i_track_ID, i_candidate_pos );
-                MP4_TrackUnselect( p_demux, tk );
-                goto end;
+                mp4_track_t *tk_tmp = &p_sys->track[i_track];
+                if( tk_tmp == tk ||
+                    !tk_tmp->b_ok || tk_tmp->b_chapters_source ||
+                    !tk_tmp->b_selected || tk_tmp->i_sample >= tk_tmp->i_sample_count )
+                    continue;
+
+                mtime_t i_nzdts = MP4_TrackGetDTS( p_demux, tk_tmp );
+                assert(i_nzdts >= p_sys->i_pcr);
+                if ( i_nzdts <= i_nztime + DEMUX_TRACK_MAX_PRELOAD )
+                {
+                    /* Found a better candidate to avoid seeking */
+                    if( MP4_TrackGetPos( tk_tmp ) < MP4_TrackGetPos( tk ) )
+                        tk = tk_tmp;
+                    /* Note: previous candidate will be repicked on next loop */
+                }
             }
-            i_current_pos = i_candidate_pos;
-        }
 
-        /* now read pes */
-        if( !(p_block = MP4_Block_Read( p_demux, tk, i_samplessize )) )
-        {
-            msg_Warn( p_demux, "track[0x%x] will be disabled (eof?)"
-                      ": Failed to read %d bytes sample at %"PRIu64,
-                      tk->i_track_ID, i_samplessize, i_current_pos );
-            MP4_TrackUnselect( p_demux, tk );
-            goto end;
+            uint64_t i_pos = MP4_TrackGetPos( tk );
+            int i_ret = DemuxTrack( p_demux, tk, i_pos );
+            if( i_ret == VLC_DEMUXER_SUCCESS )
+                i_status = VLC_DEMUXER_SUCCESS;
         }
-
-        /* dts */
-        p_block->i_dts = VLC_TS_0 + MP4_TrackGetDTS( p_demux, tk );
-        /* pts */
-        if( MP4_TrackGetPTSDelta( p_demux, tk, &i_delta ) )
-            p_block->i_pts = p_block->i_dts + i_delta;
-        else if( tk->fmt.i_cat != VIDEO_ES )
-            p_block->i_pts = p_block->i_dts;
         else
-            p_block->i_pts = VLC_TS_INVALID;
-
-        if ( !b_data_sent )
         {
-            es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_pcr );
-            b_data_sent = true;
+            i_status = VLC_DEMUXER_SUCCESS;
         }
-        MP4_Block_Send( p_demux, tk, p_block );
+
+        if( i_status != VLC_DEMUXER_SUCCESS || !tk )
+            break;
     }
 
-    /* Next sample */
-    if ( i_nb_samples ) /* sample size could be 0, need to go fwd. see return */
-        MP4_TrackNextSample( p_demux, tk, i_nb_samples );
+    p_sys->i_time += __MAX(i_scaledincrement, 1);
+    p_sys->i_pcr = i_nztime + DEMUX_INCREMENT;
 
-end:
-    if ( b_data_sent )
-    {
-        p_sys->i_pcr = INT64_MAX;
-        for( i_track = 0; i_track < p_sys->i_tracks; i_track++ )
-        {
-            mp4_track_t *tk = &p_sys->track[i_track];
-            if ( !tk->b_ok || !tk->b_selected  ||
-                 (tk->fmt.i_cat != AUDIO_ES && tk->fmt.i_cat != VIDEO_ES) )
-                continue;
+    /* */
+    MP4_UpdateSeekpoint( p_demux, i_nztime + DEMUX_INCREMENT );
 
-            mtime_t i_dts = MP4_TrackGetDTS( p_demux, tk );
-            p_sys->i_pcr = __MIN( i_dts, p_sys->i_pcr );
-            p_sys->i_time = p_sys->i_pcr * p_sys->i_timescale / CLOCK_FREQ;
-        }
-    }
+    es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_pcr );
 
-    return (b_data_sent || ( i_samplessize == 0 && i_nb_samples )) ? VLC_DEMUXER_SUCCESS : VLC_DEMUXER_EOF;
+    return (i_status == VLC_DEMUXER_SUCCESS) ? VLC_DEMUXER_SUCCESS : VLC_DEMUXER_EOF;
 }
 
-static void MP4_UpdateSeekpoint( demux_t *p_demux )
+static void MP4_UpdateSeekpoint( demux_t *p_demux, int64_t i_time )
 {
     demux_sys_t *p_sys = p_demux->p_sys;
-    int64_t i_time;
     int i;
     if( !p_sys->p_title )
         return;
-    i_time = MP4_GetMoviePTS( p_sys );
     for( i = 0; i < p_sys->p_title->i_seekpoint; i++ )
     {
         if( i_time < p_sys->p_title->seekpoint[i]->i_time_offset )
@@ -1315,7 +1332,7 @@ static int Seek( demux_t *p_demux, mtime_t i_date )
         mp4_track_t *tk = &p_sys->track[i_track];
         MP4_TrackSeek( p_demux, tk, i_date );
     }
-    MP4_UpdateSeekpoint( p_demux );
+    MP4_UpdateSeekpoint( p_demux, i_date );
 
     MP4ASF_ResetFrames( p_sys );
     es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, i_date );



More information about the vlc-commits mailing list