]> git.sesse.net Git - vlc/blobdiff - modules/demux/mkv/demux.cpp
demux: mkv: enforce valid frame rate
[vlc] / modules / demux / mkv / demux.cpp
index 2afeceb7621e118c3a329e139fe5f3e9d46669d0..a0e8e89b5979ef349a5524fcf1789a18ea95ac03 100644 (file)
@@ -2,31 +2,33 @@
 /*****************************************************************************
  * mkv.cpp : matroska demuxer
  *****************************************************************************
- * Copyright (C) 2003-2004 the VideoLAN team
+ * Copyright (C) 2003-2004 VLC authors and VideoLAN
  * $Id$
  *
  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
  *          Steve Lhomme <steve.lhomme@free.fr>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
  *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  *****************************************************************************/
 
 #include "demux.hpp"
-
+#include "stream_io_callback.hpp"
 #include "Ebml_parser.hpp"
 
+#include <vlc_keys.h>
+
 event_thread_t::event_thread_t(demux_t *p_demux) : p_demux(p_demux)
 {
     vlc_mutex_init( &lock );
@@ -345,43 +347,39 @@ void event_thread_t::EventThread()
                     // select new button
                     if ( best != i_curr_button )
                     {
-                        vlc_value_t val;
+                        uint32_t i_palette;
+
+                        if(button_ptr.btn_coln != 0) {
+                            i_palette = pci->hli.btn_colit.btn_coli[button_ptr.btn_coln-1][1];
+                        } else {
+                            i_palette = 0;
+                        }
 
-                        if( var_Get( p_sys->p_input, "highlight-mutex", &val ) == VLC_SUCCESS )
+                        for( int i = 0; i < 4; i++ )
                         {
-                            vlc_mutex_t *p_mutex = (vlc_mutex_t *) val.p_address;
-                            uint32_t i_palette;
-
-                            if(button_ptr.btn_coln != 0) {
-                                i_palette = pci->hli.btn_colit.btn_coli[button_ptr.btn_coln-1][1];
-                            } else {
-                                i_palette = 0;
-                            }
-
-                            for( int i = 0; i < 4; i++ )
-                            {
-                                uint32_t i_yuv = 0xFF;//p_sys->clut[(hl.palette>>(16+i*4))&0x0f];
-                                uint8_t i_alpha = (i_palette>>(i*4))&0x0f;
-                                i_alpha = i_alpha == 0xf ? 0xff : i_alpha << 4;
-
-                                p_sys->palette[i][0] = (i_yuv >> 16) & 0xff;
-                                p_sys->palette[i][1] = (i_yuv >> 0) & 0xff;
-                                p_sys->palette[i][2] = (i_yuv >> 8) & 0xff;
-                                p_sys->palette[i][3] = i_alpha;
-                            }
-
-                            vlc_mutex_lock( p_mutex );
-                            val.i_int = button_ptr.x_start; var_Set( p_sys->p_input, "x-start", val );
-                            val.i_int = button_ptr.x_end;   var_Set( p_sys->p_input, "x-end",   val );
-                            val.i_int = button_ptr.y_start; var_Set( p_sys->p_input, "y-start", val );
-                            val.i_int = button_ptr.y_end;   var_Set( p_sys->p_input, "y-end",   val );
-
-                            val.p_address = (void *)p_sys->palette;
-                            var_Set( p_sys->p_input, "menu-palette", val );
-
-                            val.b_bool = true; var_Set( p_sys->p_input, "highlight", val );
-                            vlc_mutex_unlock( p_mutex );
+                            uint32_t i_yuv = 0xFF;//p_sys->clut[(hl.palette>>(16+i*4))&0x0f];
+                            uint8_t i_alpha = (i_palette>>(i*4))&0x0f;
+                            i_alpha = i_alpha == 0xf ? 0xff : i_alpha << 4;
+
+                            p_sys->palette[i][0] = (i_yuv >> 16) & 0xff;
+                            p_sys->palette[i][1] = (i_yuv >> 0) & 0xff;
+                            p_sys->palette[i][2] = (i_yuv >> 8) & 0xff;
+                            p_sys->palette[i][3] = i_alpha;
                         }
+
+                        vlc_global_lock( VLC_HIGHLIGHT_MUTEX );
+                        var_SetInteger( p_sys->p_input, "x-start",
+                                        button_ptr.x_start );
+                        var_SetInteger( p_sys->p_input, "x-end",
+                                        button_ptr.x_end );
+                        var_SetInteger( p_sys->p_input, "y-start",
+                                        button_ptr.y_start );
+                        var_SetInteger( p_sys->p_input, "y-end",
+                                        button_ptr.y_end );
+                        var_SetAddress( p_sys->p_input, "menu-palette",
+                                        p_sys->palette );
+                        var_SetBool( p_sys->p_input, "highlight", true );
+                        vlc_global_unlock( VLC_HIGHLIGHT_MUTEX );
                     }
                     vlc_mutex_unlock( &p_sys->lock_demuxer );
                     vlc_mutex_lock( &lock );
@@ -464,18 +462,26 @@ matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, EbmlS
 {
     int i_upper_lvl = 0;
     EbmlElement *p_l0, *p_l1, *p_l2;
-    bool b_keep_stream = false, b_keep_segment;
+    bool b_keep_stream = false, b_keep_segment = false;
 
-    // verify the EBML Header
-    p_l0 = p_estream->FindNextID(EBML_INFO(EbmlHead), 0xFFFFFFFFL);
+    /* verify the EBML Header... it shouldn't be bigger than 1kB */
+    p_l0 = p_estream->FindNextID(EBML_INFO(EbmlHead), 1024);
     if (p_l0 == NULL)
     {
         msg_Err( p_demux, "No EBML header found" );
         return NULL;
     }
 
-    // verify we can read this Segment, we only support Matroska version 1 for now
-    p_l0->Read(*p_estream, EBML_CLASS_CONTEXT(EbmlHead), i_upper_lvl, p_l0, true);
+    /* verify we can read this Segment */
+    try
+    {
+        p_l0->Read(*p_estream, EBML_CLASS_CONTEXT(EbmlHead), i_upper_lvl, p_l0, true);
+    }
+    catch(...)
+    {
+        msg_Err(p_demux, "EBML Header Read failed");
+        return NULL;
+    }
 
     EDocType doc_type = GetChild<EDocType>(*static_cast<EbmlHead*>(p_l0));
     if (std::string(doc_type) != "matroska" && std::string(doc_type) != "webm" )
@@ -495,13 +501,13 @@ matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, EbmlS
 
 
     // find all segments in this file
-    p_l0 = p_estream->FindNextID(EBML_INFO(KaxSegment), 0xFFFFFFFFFLL);
+    p_l0 = p_estream->FindNextID(EBML_INFO(KaxSegment), UINT64_MAX);
     if (p_l0 == NULL)
     {
         return NULL;
     }
 
-    matroska_stream_c *p_stream1 = new matroska_stream_c( *this );
+    matroska_stream_c *p_stream1 = new matroska_stream_c();
 
     while (p_l0 != 0)
     {
@@ -509,7 +515,6 @@ matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, EbmlS
         {
             EbmlParser  *ep;
             matroska_segment_c *p_segment1 = new matroska_segment_c( *this, *p_estream );
-            b_keep_segment = b_initial;
 
             ep = new EbmlParser(p_estream, p_l0, &demuxer );
             p_segment1->ep = ep;
@@ -521,8 +526,21 @@ matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, EbmlS
                 {
                     // find the families of this segment
                     KaxInfo *p_info = static_cast<KaxInfo*>(p_l1);
-
-                    p_info->Read(*p_estream, EBML_CLASS_CONTEXT(KaxInfo), i_upper_lvl, p_l2, true);
+                    b_keep_segment = b_initial;
+                    if( unlikely( p_info->GetSize() >= SIZE_MAX ) )
+                    {
+                        msg_Err( p_demux, "KaxInfo too big aborting" );
+                        break;
+                    }
+                    try
+                    {
+                        p_info->Read(*p_estream, EBML_CLASS_CONTEXT(KaxInfo), i_upper_lvl, p_l2, true);
+                    }
+                    catch (...)
+                    {
+                        msg_Err( p_demux, "KaxInfo found but corrupted");
+                        break;
+                    }
                     for( size_t i = 0; i < p_info->ListSize(); i++ )
                     {
                         EbmlElement *l = (*p_info)[i];
@@ -531,19 +549,20 @@ matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, EbmlS
                         {
                             KaxSegmentUID *p_uid = static_cast<KaxSegmentUID*>(l);
                             b_keep_segment = (FindSegment( *p_uid ) == NULL);
-                            if ( !b_keep_segment )
-                                break; // this segment is already known
-                            opened_segments.push_back( p_segment1 );
                             delete p_segment1->p_segment_uid;
                             p_segment1->p_segment_uid = new KaxSegmentUID(*p_uid);
+                            if ( !b_keep_segment )
+                                break; // this segment is already known
                         }
                         else if( MKV_IS_ID( l, KaxPrevUID ) )
                         {
                             p_segment1->p_prev_segment_uid = new KaxPrevUID( *static_cast<KaxPrevUID*>(l) );
+                            p_segment1->b_ref_external_segments = true;
                         }
                         else if( MKV_IS_ID( l, KaxNextUID ) )
                         {
                             p_segment1->p_next_segment_uid = new KaxNextUID( *static_cast<KaxNextUID*>(l) );
+                            p_segment1->b_ref_external_segments = true;
                         }
                         else if( MKV_IS_ID( l, KaxSegmentFamily ) )
                         {
@@ -551,10 +570,12 @@ matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, EbmlS
                             p_segment1->families.push_back( p_fam );
                         }
                     }
+                    if( b_keep_segment || !p_segment1->p_segment_uid )
+                        opened_segments.push_back( p_segment1 );
                     break;
                 }
             }
-            if ( b_keep_segment )
+            if ( b_keep_segment || !p_segment1->p_segment_uid )
             {
                 b_keep_stream = true;
                 p_stream1->segments.push_back( p_segment1 );
@@ -568,7 +589,7 @@ matroska_stream_c *demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, EbmlS
         if (p_l0->IsFiniteSize() )
         {
             p_l0->SkipData(*p_estream, KaxMatroska_Context);
-            p_l0 = p_estream->FindNextID(EBML_INFO(KaxSegment), 0xFFFFFFFFL);
+            p_l0 = p_estream->FindNextID(EBML_INFO(KaxSegment), UINT64_MAX);
         }
         else
         {
@@ -592,14 +613,16 @@ void demux_sys_t::InitUi()
     /* FIXME hack hack hack hack FIXME */
     /* Get p_input and create variable */
     p_input = demux_GetParentInput( &demuxer );
-    var_Create( p_input, "x-start", VLC_VAR_INTEGER );
-    var_Create( p_input, "y-start", VLC_VAR_INTEGER );
-    var_Create( p_input, "x-end", VLC_VAR_INTEGER );
-    var_Create( p_input, "y-end", VLC_VAR_INTEGER );
-    var_Create( p_input, "color", VLC_VAR_ADDRESS );
-    var_Create( p_input, "menu-palette", VLC_VAR_ADDRESS );
-    var_Create( p_input, "highlight", VLC_VAR_BOOL );
-    var_Create( p_input, "highlight-mutex", VLC_VAR_MUTEX );
+    if( p_input )
+    {
+        var_Create( p_input, "x-start", VLC_VAR_INTEGER );
+        var_Create( p_input, "y-start", VLC_VAR_INTEGER );
+        var_Create( p_input, "x-end", VLC_VAR_INTEGER );
+        var_Create( p_input, "y-end", VLC_VAR_INTEGER );
+        var_Create( p_input, "color", VLC_VAR_ADDRESS );
+        var_Create( p_input, "menu-palette", VLC_VAR_ADDRESS );
+        var_Create( p_input, "highlight", VLC_VAR_BOOL );
+    }
 
     /* Now create our event thread catcher */
     p_ev = new event_thread_t(&demuxer);
@@ -610,16 +633,18 @@ void demux_sys_t::CleanUi()
     delete p_ev;
     p_ev = NULL;
 
-    var_Destroy( p_input, "highlight-mutex" );
-    var_Destroy( p_input, "highlight" );
-    var_Destroy( p_input, "x-start" );
-    var_Destroy( p_input, "x-end" );
-    var_Destroy( p_input, "y-start" );
-    var_Destroy( p_input, "y-end" );
-    var_Destroy( p_input, "color" );
-    var_Destroy( p_input, "menu-palette" );
-
-    vlc_object_release( p_input );
+    if( p_input )
+    {
+        var_Destroy( p_input, "highlight" );
+        var_Destroy( p_input, "x-start" );
+        var_Destroy( p_input, "x-end" );
+        var_Destroy( p_input, "y-start" );
+        var_Destroy( p_input, "y-end" );
+        var_Destroy( p_input, "color" );
+        var_Destroy( p_input, "menu-palette" );
+
+        vlc_object_release( p_input );
+    }
 
     msg_Dbg( &demuxer, "Stopping the UI Hook" );
 }
@@ -633,84 +658,118 @@ void demux_sys_t::PreloadFamily( const matroska_segment_c & of_segment )
 }
 
 // preload all the linked segments for all preloaded segments
-void demux_sys_t::PreloadLinked( matroska_segment_c *p_segment )
+bool demux_sys_t::PreloadLinked()
 {
-    size_t i_preloaded, i, j;
+    size_t i, j;
     virtual_segment_c *p_seg;
 
-    p_current_segment = VirtualFromSegments( p_segment );
+    p_current_segment = VirtualFromSegments( &opened_segments );
+    if ( !p_current_segment )
+        return false;
 
     used_segments.push_back( p_current_segment );
 
-    // create all the other virtual segments of the family
-    do {
-        i_preloaded = 0;
-        for ( i=0; i< opened_segments.size(); i++ )
-        {
-            if ( opened_segments[i]->b_preloaded && !IsUsedSegment( *opened_segments[i] ) )
-            {
-                p_seg = VirtualFromSegments( opened_segments[i] );
-                used_segments.push_back( p_seg );
-                i_preloaded++;
-            }
-        }
-    } while ( i_preloaded ); // worst case: will stop when all segments are found as family related
-
     // publish all editions of all usable segment
     for ( i=0; i< used_segments.size(); i++ )
     {
         p_seg = used_segments[i];
         if ( p_seg->Editions() != NULL )
         {
-            input_title_t *p_title = vlc_input_title_New();
-            p_seg->i_sys_title = i;
-            int i_chapters;
-
-            // TODO use a name for each edition, let the TITLE deal with a codec name
             for ( j=0; j<p_seg->Editions()->size(); j++ )
             {
+                virtual_edition_c * p_ved = (*p_seg->Editions())[j];
+                input_title_t *p_title = vlc_input_title_New();
+                int i_chapters;
+
+                // TODO use a name for each edition, let the TITLE deal with a codec name
                 if ( p_title->psz_name == NULL )
                 {
-                    const char* psz_tmp = (*p_seg->Editions())[j]->GetMainName().c_str();
-                    if( *psz_tmp != '\0' )
-                        p_title->psz_name = strdup( psz_tmp );
-                }
+                    if( p_ved->GetMainName().length() )
+                        p_title->psz_name = strdup( p_ved->GetMainName().c_str() );
+                    else
+                    {
+                        /* Check in tags if the edition has a name */
+
+                        /* We use only the tags of the first segment as it contains the edition */
+                        std::vector<Tag*> &tags = opened_segments[0]->tags;
+                        uint64_t i_ed_uid = 0;
+                        if( p_ved->p_edition )
+                            i_ed_uid = (uint64_t) p_ved->p_edition->i_uid;
 
-                chapter_edition_c *p_edition = (*p_seg->Editions())[j];
+                        for( size_t k = 0; k < tags.size(); k++ )
+                        {
+                            if( tags[k]->i_tag_type == EDITION_UID && tags[k]->i_uid == i_ed_uid )
+                                for( size_t l = 0; l < tags[k]->simple_tags.size(); l++ )
+                                {
+                                    SimpleTag * p_st = tags[k]->simple_tags[l];
+                                    if( !strcmp(p_st->psz_tag_name,"TITLE") )
+                                    {
+                                        msg_Dbg( &demuxer, "Using title \"%s\" from tag for edition %"PRIu64, p_st->p_value, i_ed_uid );
+                                        p_title->psz_name = strdup( p_st->p_value );
+                                        break;
+                                    }
+                                }
+                        }
+
+                        if( !p_title->psz_name &&
+                            asprintf(&(p_title->psz_name), "%s %d", N_("Segment"), (int)i) == -1 )
+                            p_title->psz_name = NULL;
+                    }
+                }
 
                 i_chapters = 0;
-                p_edition->PublishChapters( *p_title, i_chapters, 0 );
-            }
+                p_ved->PublishChapters( *p_title, i_chapters, 0 );
 
-            // create a name if there is none
-            if ( p_title->psz_name == NULL )
-            {
-                if( asprintf(&(p_title->psz_name), "%s %d", N_("Segment"), (int)i) == -1 )
-                    p_title->psz_name = NULL;
-            }
+                // Input duration into i_length
+                p_title->i_length = p_ved->i_duration;
 
-            titles.push_back( p_title );
+                titles.push_back( p_title );
+            }
         }
+        p_seg->i_sys_title = p_seg->i_current_edition;
     }
 
     // TODO decide which segment should be first used (VMG for DVD)
+
+    return true;
 }
 
-bool demux_sys_t::IsUsedSegment( matroska_segment_c &segment ) const
+void demux_sys_t::FreeUnused()
 {
-    for ( size_t i=0; i< used_segments.size(); i++ )
+    size_t i;
+    for( i = 0; i < streams.size(); i++ )
     {
-        if ( used_segments[i]->FindUID( *segment.p_segment_uid ) )
-            return true;
+        bool used = false;
+        struct matroska_stream_c *p_s = streams[i];
+        for( size_t j = 0; j < p_s->segments.size(); j++ )
+        {
+            if( p_s->segments[j]->b_preloaded )
+            {
+                used = true;
+                break;
+            }
+        }
+        if( !used )
+        {
+            streams[i] = NULL;
+            delete p_s;
+        }
+    }
+    for( i = 0; i < opened_segments.size(); i++)
+    {
+        if( !opened_segments[i]->b_preloaded )
+        {
+            delete opened_segments[i];
+            opened_segments[i] = NULL;
+        }
     }
-    return false;
 }
 
-virtual_segment_c *demux_sys_t::VirtualFromSegments( matroska_segment_c *p_segment ) const
+virtual_segment_c *demux_sys_t::VirtualFromSegments( std::vector<matroska_segment_c*> *p_segments ) const
 {
-    virtual_segment_c *p_result = new virtual_segment_c( p_segment );
-    p_result->AddSegments( opened_segments );
-
+    if ( p_segments->empty() )
+        return NULL;
+    virtual_segment_c *p_result = new virtual_segment_c( p_segments );
     return p_result;
 }
 
@@ -718,25 +777,31 @@ bool demux_sys_t::PreparePlayback( virtual_segment_c *p_new_segment )
 {
     if ( p_new_segment != NULL && p_new_segment != p_current_segment )
     {
-        if ( p_current_segment != NULL && p_current_segment->Segment() != NULL )
-            p_current_segment->Segment()->UnSelect();
+        if ( p_current_segment != NULL && p_current_segment->CurrentSegment() != NULL )
+            p_current_segment->CurrentSegment()->UnSelect();
 
         p_current_segment = p_new_segment;
         i_current_title = p_new_segment->i_sys_title;
     }
-    if( !p_current_segment->Segment()->b_cues )
-        msg_Warn( &p_current_segment->Segment()->sys.demuxer, "no cues/empty cues found->seek won't be precise" );
+    if( !p_current_segment->CurrentSegment() )
+        return false;
+    if( !p_current_segment->CurrentSegment()->b_cues )
+        msg_Warn( &p_current_segment->CurrentSegment()->sys.demuxer, "no cues/empty cues found->seek won't be precise" );
 
     f_duration = p_current_segment->Duration();
 
     /* add information */
-    p_current_segment->Segment()->InformationCreate( );
-    p_current_segment->Segment()->Select( 0 );
+    p_current_segment->CurrentSegment()->InformationCreate( );
+    p_current_segment->CurrentSegment()->Select( 0 );
+
+    /* Seek to the beginning */
+    p_current_segment->Seek(p_current_segment->CurrentSegment()->sys.demuxer,
+                            0, 0, NULL, -1);
 
     return true;
 }
 
-void demux_sys_t::JumpTo( virtual_segment_c & vsegment, chapter_item_c * p_chapter )
+void demux_sys_t::JumpTo( virtual_segment_c & vsegment, virtual_chapter_c * p_chapter )
 {
     // if the segment is not part of the current segment, select the new one
     if ( &vsegment != p_current_segment )
@@ -744,12 +809,12 @@ void demux_sys_t::JumpTo( virtual_segment_c & vsegment, chapter_item_c * p_chapt
         PreparePlayback( &vsegment );
     }
 
-    if ( p_chapter != NULL )
+    if ( p_chapter )
     {
-        if ( !p_chapter->Enter( true ) )
+        if ( !p_chapter->p_chapter || !p_chapter->p_chapter->Enter( true ) )
         {
             // jump to the location in the found segment
-            vsegment.Seek( demuxer, p_chapter->i_user_start_time, -1, p_chapter, -1 );
+            vsegment.Seek( demuxer, p_chapter->i_virtual_start_time, -1, p_chapter, -1 );
         }
     }
 
@@ -759,19 +824,19 @@ matroska_segment_c *demux_sys_t::FindSegment( const EbmlBinary & uid ) const
 {
     for (size_t i=0; i<opened_segments.size(); i++)
     {
-        if ( *opened_segments[i]->p_segment_uid == uid )
+        if ( opened_segments[i]->p_segment_uid && *opened_segments[i]->p_segment_uid == uid )
             return opened_segments[i];
     }
     return NULL;
 }
 
-chapter_item_c *demux_sys_t::BrowseCodecPrivate( unsigned int codec_id,
+virtual_chapter_c *demux_sys_t::BrowseCodecPrivate( unsigned int codec_id,
                                         bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
                                         const void *p_cookie,
                                         size_t i_cookie_size,
                                         virtual_segment_c * &p_segment_found )
 {
-    chapter_item_c *p_result = NULL;
+    virtual_chapter_c *p_result = NULL;
     for (size_t i=0; i<used_segments.size(); i++)
     {
         p_result = used_segments[i]->BrowseCodecPrivate( codec_id, match, p_cookie, i_cookie_size );
@@ -784,9 +849,9 @@ chapter_item_c *demux_sys_t::BrowseCodecPrivate( unsigned int codec_id,
     return p_result;
 }
 
-chapter_item_c *demux_sys_t::FindChapter( int64_t i_find_uid, virtual_segment_c * & p_segment_found )
+virtual_chapter_c *demux_sys_t::FindChapter( int64_t i_find_uid, virtual_segment_c * & p_segment_found )
 {
-    chapter_item_c *p_result = NULL;
+    virtual_chapter_c *p_result = NULL;
     for (size_t i=0; i<used_segments.size(); i++)
     {
         p_result = used_segments[i]->FindChapter( i_find_uid );
@@ -799,4 +864,3 @@ chapter_item_c *demux_sys_t::FindChapter( int64_t i_find_uid, virtual_segment_c
     return p_result;
 }
 
-