* mkv: better seeking support.
authorLaurent Aimar <fenrir@videolan.org>
Sun, 22 Jun 2003 14:36:34 +0000 (14:36 +0000)
committerLaurent Aimar <fenrir@videolan.org>
Sun, 22 Jun 2003 14:36:34 +0000 (14:36 +0000)
modules/demux/mkv.cpp

index 7a15aa3..dea5a6c 100644 (file)
@@ -2,7 +2,7 @@
  * mkv.cpp : matroska demuxer
  *****************************************************************************
  * Copyright (C) 2001 VideoLAN
- * $Id: mkv.cpp,v 1.2 2003/06/22 12:27:16 fenrir Exp $
+ * $Id: mkv.cpp,v 1.3 2003/06/22 14:36:34 fenrir Exp $
  *
  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  *
@@ -399,6 +399,8 @@ typedef struct
     int             i_data_init;
     uint8_t         *p_data_init;
 
+    /* hack : it's for seek */
+    vlc_bool_t      b_search_keyframe;
 } mkv_track_t;
 
 typedef struct
@@ -1443,211 +1445,30 @@ static void Deactivate( vlc_object_t *p_this )
     free( p_sys );
 }
 
-static void Seek( input_thread_t *p_input, mtime_t i_date, int i_percent)
+static int BlockGet( input_thread_t *p_input, KaxBlock **pp_block, int64_t *pi_ref1, int64_t *pi_ref2, int64_t *pi_duration )
 {
     demux_sys_t    *p_sys   = p_input->p_demux_data;
 
-    int i_index;
-
-    msg_Dbg( p_input, "seek request to %lld (%d%%)", i_date, i_percent );
-
-    for( i_index = 0; i_index < p_sys->i_index; i_index++ )
-    {
-        if( p_sys->index[i_index].i_time >= i_date )
-        {
-            break;
-        }
-    }
-
-    if( i_index > 0 )
-    {
-        i_index--;
-    }
-
-    msg_Dbg( p_input, "seek got %lld (%d%%)",
-             p_sys->index[i_index].i_time, (int)(100 * p_sys->index[i_index].i_position /p_input->stream.p_selected_area->i_size ) );
-
-    delete p_sys->ep;
-
-    p_sys->in->setFilePointer( p_sys->index[i_index].i_position, seek_beginning );
-
-    p_sys->ep = new EbmlParser( p_sys->es, p_sys->segment );
-
-//    p_sys->cluster = p_sys->es->FindNextElement( p_sys->segment->Generic().Context,
-//                                                 i_ulev, 0xFFFFFFFFL, true, 1);
-
-}
-
-/*****************************************************************************
- * Demux: reads and demuxes data packets
- *****************************************************************************
- * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
- *****************************************************************************/
-static int Demux( input_thread_t * p_input )
-{
-    demux_sys_t    *p_sys   = p_input->p_demux_data;
-    mtime_t        i_start_pts = p_sys->i_pts;
-    KaxBlock *block = NULL;
-    int64_t i_block_duration = -1;
-    int64_t i_block_ref1 = 0;
-    int64_t i_block_ref2 = 0;
-
-    if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT )
-    {
-        mtime_t i_duration = (mtime_t)( p_sys->f_duration / 1000 );
-        mtime_t i_date;
-        int i_percent;
-
-        i_date = (mtime_t)1000000 *
-                 (mtime_t)i_duration*
-                 (mtime_t)p_sys->in->getFilePointer() /
-                 (mtime_t)p_input->stream.p_selected_area->i_size;
-
-        i_percent = 100 * p_sys->in->getFilePointer() /
-                        p_input->stream.p_selected_area->i_size;
-
-        Seek( p_input, i_date, i_percent);
-        i_start_pts = -1;
-    }
+    *pp_block = NULL;
+    *pi_ref1  = -1;
+    *pi_ref2  = -1;
 
     for( ;; )
     {
         EbmlElement *el;
         int         i_level;
 
+        if( p_input->b_die )
+        {
+            return VLC_EGENERIC;
+        }
+
         el = p_sys->ep->Get();
         i_level = p_sys->ep->GetLevel();
 
-        /* First send last collected blocks (before ep->Up, because of cluster) */
-        if( el == NULL && block != NULL )
+        if( el == NULL && *pp_block != NULL )
         {
-            /* we have data */
-            int i_track;
-#define tk  p_sys->track[i_track]
-            for( i_track = 0; i_track < p_sys->i_track; i_track++ )
-            {
-                if( tk.i_number == block->TrackNum() )
-                {
-                    break;
-                }
-            }
-
-            p_sys->i_pts = block->GlobalTimecode() * (mtime_t) 1000 / p_sys->i_timescale;
-
-            if( p_sys->i_pts > 0 )
-            {
-                input_ClockManageRef( p_input,
-                                      p_input->stream.p_selected_program,
-                                      p_sys->i_pts * 9 / 100 );
-            }
-
-            if( i_track >= p_sys->i_track )
-            {
-                msg_Err( p_input, "invalid track number=%d", block->TrackNum() );
-            }
-            else if( tk.p_es->p_decoder_fifo != NULL )
-            {
-                pes_packet_t  *p_pes;
-                unsigned int i;
-
-                if( ( p_pes = input_NewPES( p_input->p_method_data ) ) != NULL )
-                {
-                    mtime_t i_pts;
-
-                    i_pts = input_ClockGetTS( p_input,
-                                              p_input->stream.p_selected_program,
-                                              p_sys->i_pts * 9 / 100 );
-
-                    p_pes->i_pts = i_pts;
-                    p_pes->i_dts = i_pts;
-                    if( tk.i_cat == SPU_ES )
-                    {
-                        /* special case : dts mean end of display */
-                        if( i_block_duration > 0 )
-                        {
-                            /* FIXME not sure about that */
-                            p_pes->i_dts += i_block_duration * 1000;// * (mtime_t) 1000 / p_sys->i_timescale;
-                        }
-                        else
-                        {
-                            /* unknown */
-                            p_pes->i_dts = 0;
-                        }
-                    }
-
-                    p_pes->p_first = p_pes->p_last = NULL;
-                    p_pes->i_nb_data = 0;
-                    p_pes->i_pes_size = 0;
-
-                    for( i = 0; i < block->NumberFrames(); i++ )
-                    {
-                        data_packet_t *p_data = NULL;
-                        DataBuffer &data = block->GetBuffer(i);
-
-                        if( ( p_data = input_NewPacket( p_input->p_method_data, data.Size() ) ) != NULL )
-                        {
-                            memcpy( p_data->p_payload_start, data.Buffer(), data.Size() );
-
-                            p_data->p_payload_end = p_data->p_payload_start + data.Size();
-
-                            if( p_pes->p_first == NULL )
-                            {
-                                p_pes->p_first = p_data;
-                            }
-                            else
-                            {
-                                p_pes->p_last->p_next  = p_data;
-                            }
-                            p_pes->i_nb_data++;
-                            p_pes->i_pes_size += data.Size();
-                            p_pes->p_last  = p_data;
-                        }
-                    }
-                    if( tk.i_cat == SPU_ES && p_pes->p_first && p_pes->i_pes_size > 0 )
-                    {
-                        p_pes->p_first->p_payload_end[-1] = '\0';
-                    }
-
-                    if( !tk.b_inited && tk.i_data_init > 0 )
-                    {
-                        pes_packet_t *p_init;
-                        data_packet_t *p_data;
-
-                        msg_Dbg( p_input, "sending header (%d bytes)", tk.i_data_init );
-
-                        p_init = input_NewPES( p_input->p_method_data );
-
-                        p_data = input_NewPacket( p_input->p_method_data, tk.i_data_init );
-                        memcpy( p_data->p_payload_start, tk.p_data_init, tk.i_data_init );
-                        p_data->p_payload_end = p_data->p_payload_start + tk.i_data_init;
-
-                        p_init->p_first = p_init->p_last = p_data;
-                        p_init->i_nb_data = 1;
-                        p_init->i_pes_size = tk.i_data_init;
-
-                        input_DecodePES( tk.p_es->p_decoder_fifo, p_init );
-                    }
-                    tk.b_inited = VLC_TRUE;
-
-                    input_DecodePES( tk.p_es->p_decoder_fifo, p_pes );
-                }
-                else
-                {
-                    tk.b_inited = VLC_FALSE;
-                }
-            }
-#undef tk
-            delete block;
-            block = NULL;
-
-            if( i_start_pts == -1 )
-            {
-                i_start_pts = p_sys->i_pts;
-            }
-            else if( p_sys->i_pts > i_start_pts + (mtime_t)100000)
-            {
-                return 1;
-            }
+            return VLC_SUCCESS;
         }
 
         if( el == NULL )
@@ -1658,7 +1479,7 @@ static int Demux( input_thread_t * p_input )
                 continue;
             }
             msg_Warn( p_input, "EOF" );
-            return 0;
+            return VLC_EGENERIC;
         }
 
         /* do parsing */
@@ -1672,7 +1493,7 @@ static int Demux( input_thread_t * p_input )
             else if( EbmlId( *el ) == KaxCues::ClassInfos.GlobalId )
             {
                 msg_Warn( p_input, "find KaxCues FIXME" );
-                return 0;
+                return VLC_EGENERIC;
             }
             else
             {
@@ -1697,10 +1518,10 @@ static int Demux( input_thread_t * p_input )
         {
             if( EbmlId( *el ) == KaxBlock::ClassInfos.GlobalId )
             {
-                block = (KaxBlock*)el;
+                *pp_block = (KaxBlock*)el;
 
-                block->ReadData( p_sys->es->I_O() );
-                block->SetParent( *p_sys->cluster );
+                (*pp_block)->ReadData( p_sys->es->I_O() );
+                (*pp_block)->SetParent( *p_sys->cluster );
 
                 p_sys->ep->Keep();
             }
@@ -1709,30 +1530,301 @@ static int Demux( input_thread_t * p_input )
                 KaxBlockDuration &dur = *(KaxBlockDuration*)el;
 
                 dur.ReadData( p_sys->es->I_O() );
-                i_block_duration = uint64( dur );
+                *pi_duration = uint64( dur );
             }
             else if( EbmlId( *el ) == KaxReferenceBlock::ClassInfos.GlobalId )
             {
                 KaxReferenceBlock &ref = *(KaxReferenceBlock*)el;
 
                 ref.ReadData( p_sys->es->I_O() );
-                if( i_block_ref1 == 0 )
+                if( *pi_ref1 == -1 )
                 {
-                    i_block_ref1 = int64( ref );
+                    *pi_ref1 = int64( ref );
                 }
                 else
                 {
-                    i_block_ref2 = int64( ref );
+                    *pi_ref2 = int64( ref );
                 }
             }
         }
         else
         {
             msg_Err( p_input, "invalid level = %d", i_level );
-            return 0;
+            return VLC_EGENERIC;
+        }
+    }
+}
+
+static void BlockDecode( input_thread_t *p_input, KaxBlock *block, mtime_t i_pts, mtime_t i_duration )
+{
+    demux_sys_t    *p_sys   = p_input->p_demux_data;
+
+    int             i_track;
+    pes_packet_t    *p_pes;
+    unsigned int    i;
+
+#define tk  p_sys->track[i_track]
+    for( i_track = 0; i_track < p_sys->i_track; i_track++ )
+    {
+        if( tk.i_number == block->TrackNum() )
+        {
+            break;
+        }
+    }
+
+    if( i_track >= p_sys->i_track )
+    {
+        msg_Err( p_input, "invalid track number=%d", block->TrackNum() );
+        delete block;
+
+        return;
+    }
+
+    if( tk.p_es->p_decoder_fifo == NULL )
+    {
+        delete block;
+        return;
+    }
+
+    if( ( p_pes = input_NewPES( p_input->p_method_data ) ) == NULL )
+    {
+        msg_Err( p_input, "cannot allocate PES" );
+    }
+
+
+    p_pes->i_pts = i_pts;
+    p_pes->i_dts = i_pts;
+    if( tk.i_cat == SPU_ES )
+    {
+        /* special case : dts mean end of display */
+        if( i_duration > 0 )
+        {
+            /* FIXME not sure about that */
+            p_pes->i_dts += i_duration * 1000;// * (mtime_t) 1000 / p_sys->i_timescale;
+        }
+        else
+        {
+            /* unknown */
+            p_pes->i_dts = 0;
+        }
+    }
+
+    p_pes->p_first = p_pes->p_last = NULL;
+    p_pes->i_nb_data = 0;
+    p_pes->i_pes_size = 0;
+
+    for( i = 0; i < block->NumberFrames(); i++ )
+    {
+        data_packet_t *p_data = NULL;
+        DataBuffer &data = block->GetBuffer(i);
+
+        if( ( p_data = input_NewPacket( p_input->p_method_data, data.Size() ) ) != NULL )
+        {
+            memcpy( p_data->p_payload_start, data.Buffer(), data.Size() );
+
+            p_data->p_payload_end = p_data->p_payload_start + data.Size();
+
+            if( p_pes->p_first == NULL )
+            {
+                p_pes->p_first = p_data;
+            }
+            else
+            {
+                p_pes->p_last->p_next  = p_data;
+            }
+            p_pes->i_nb_data++;
+            p_pes->i_pes_size += data.Size();
+            p_pes->p_last  = p_data;
+        }
+    }
+    if( tk.i_cat == SPU_ES && p_pes->p_first && p_pes->i_pes_size > 0 )
+    {
+        p_pes->p_first->p_payload_end[-1] = '\0';
+    }
+
+    if( !tk.b_inited && tk.i_data_init > 0 )
+    {
+        pes_packet_t *p_init;
+        data_packet_t *p_data;
+
+        msg_Dbg( p_input, "sending header (%d bytes)", tk.i_data_init );
+
+        p_init = input_NewPES( p_input->p_method_data );
+
+        p_data = input_NewPacket( p_input->p_method_data, tk.i_data_init );
+        memcpy( p_data->p_payload_start, tk.p_data_init, tk.i_data_init );
+        p_data->p_payload_end = p_data->p_payload_start + tk.i_data_init;
+
+        p_init->p_first = p_init->p_last = p_data;
+        p_init->i_nb_data = 1;
+        p_init->i_pes_size = tk.i_data_init;
+
+        input_DecodePES( tk.p_es->p_decoder_fifo, p_init );
+    }
+    tk.b_inited = VLC_TRUE;
+
+    input_DecodePES( tk.p_es->p_decoder_fifo, p_pes );
+
+#undef tk
+}
+
+static void Seek( input_thread_t *p_input, mtime_t i_date, int i_percent)
+{
+    demux_sys_t    *p_sys   = p_input->p_demux_data;
+
+    KaxBlock    *block;
+    int64_t     i_block_duration;
+    int64_t     i_block_ref1;
+    int64_t     i_block_ref2;
+
+    int         i_index;
+    int         i_track_skipping;
+    int         i_track;
+
+    msg_Dbg( p_input, "seek request to %lld (%d%%)", i_date, i_percent );
+
+    for( i_index = 0; i_index < p_sys->i_index; i_index++ )
+    {
+        if( p_sys->index[i_index].i_time >= i_date )
+        {
+            break;
+        }
+    }
+
+    if( i_index > 0 )
+    {
+        i_index--;
+    }
+
+    msg_Dbg( p_input, "seek got %lld (%d%%)",
+             p_sys->index[i_index].i_time, (int)(100 * p_sys->index[i_index].i_position /p_input->stream.p_selected_area->i_size ) );
+
+    delete p_sys->ep;
+
+    p_sys->in->setFilePointer( p_sys->index[i_index].i_position, seek_beginning );
+
+    p_sys->ep = new EbmlParser( p_sys->es, p_sys->segment );
+
+    /* now parse until key frame */
+#define tk  p_sys->track[i_track]
+    i_track_skipping = 0;
+    for( i_track = 0; i_track < p_sys->i_track; i_track++ )
+    {
+        if( tk.i_cat == VIDEO_ES )
+        {
+            tk.b_search_keyframe = VLC_TRUE;
+            i_track_skipping++;
         }
     }
 
+
+    while( i_track_skipping > 0 )
+    {
+        if( BlockGet( p_input, &block, &i_block_ref1, &i_block_ref2, &i_block_duration ) )
+        {
+            msg_Warn( p_input, "cannot get block EOF?" );
+
+            return;
+        }
+
+        p_sys->i_pts = block->GlobalTimecode() * (mtime_t) 1000 / p_sys->i_timescale;
+
+        for( i_track = 0; i_track < p_sys->i_track; i_track++ )
+        {
+            if( tk.i_number == block->TrackNum() )
+            {
+                break;
+            }
+        }
+
+        if( i_track < p_sys->i_track )
+        {
+            if( tk.i_cat == VIDEO_ES && i_block_ref1 == -1 && tk.b_search_keyframe )
+            {
+                tk.b_search_keyframe = VLC_FALSE;
+                i_track_skipping--;
+            }
+            if( tk.i_cat == VIDEO_ES && !tk.b_search_keyframe )
+            {
+                BlockDecode( p_input, block, 0, 0 );
+            }
+        }
+
+        delete block;
+    }
+#undef tk
+}
+
+/*****************************************************************************
+ * Demux: reads and demuxes data packets
+ *****************************************************************************
+ * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
+ *****************************************************************************/
+static int Demux( input_thread_t * p_input )
+{
+    demux_sys_t    *p_sys   = p_input->p_demux_data;
+    mtime_t        i_start_pts;
+    KaxBlock *block;
+    int64_t i_block_duration;
+    int64_t i_block_ref1;
+    int64_t i_block_ref2;
+
+    if( p_input->stream.p_selected_program->i_synchro_state == SYNCHRO_REINIT )
+    {
+        mtime_t i_duration = (mtime_t)( p_sys->f_duration / 1000 );
+        mtime_t i_date;
+        int i_percent;
+
+        i_date = (mtime_t)1000000 *
+                 (mtime_t)i_duration*
+                 (mtime_t)p_sys->in->getFilePointer() /
+                 (mtime_t)p_input->stream.p_selected_area->i_size;
+
+        i_percent = 100 * p_sys->in->getFilePointer() /
+                        p_input->stream.p_selected_area->i_size;
+
+        Seek( p_input, i_date, i_percent);
+    }
+
+    i_start_pts = p_sys->i_pts;
+
+    for( ;; )
+    {
+        mtime_t i_pts;
+
+        if( BlockGet( p_input, &block, &i_block_ref1, &i_block_ref2, &i_block_duration ) )
+        {
+            msg_Warn( p_input, "cannot get block EOF?" );
+
+            return 0;
+        }
+
+        p_sys->i_pts = block->GlobalTimecode() * (mtime_t) 1000 / p_sys->i_timescale;
+
+        if( p_sys->i_pts > 0 )
+        {
+            input_ClockManageRef( p_input,
+                                  p_input->stream.p_selected_program,
+                                  p_sys->i_pts * 9 / 100 );
+        }
+
+        i_pts = input_ClockGetTS( p_input,
+                                  p_input->stream.p_selected_program,
+                                  p_sys->i_pts * 9 / 100 );
+
+        BlockDecode( p_input, block, i_pts, i_block_duration );
+
+        delete block;
+
+        if( i_start_pts == -1 )
+        {
+            i_start_pts = p_sys->i_pts;
+        }
+        else if( p_sys->i_pts > i_start_pts + (mtime_t)100000)
+        {
+            return 1;
+        }
+    }
 }