/*****************************************************************************
* 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 );
// 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 );
{
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" )
EDocTypeReadVersion doc_read_version = GetChild<EDocTypeReadVersion>(*static_cast<EbmlHead*>(p_l0));
if (uint64(doc_read_version) > 2)
{
- msg_Err( p_demux, "matroska file needs version %"PRId64" but only versions 1 & 2 supported", uint64(doc_read_version));
+ msg_Err( p_demux, "matroska file needs version %" PRId64 " but only versions 1 & 2 supported", uint64(doc_read_version));
return NULL;
}
// 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;
{
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;
{
// 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];
{
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 ) )
{
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 );
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
{
/* 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);
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" );
}
}
// 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, ij = 0;
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)ij) == -1 )
+ p_title->psz_name = NULL;
+ }
+ }
+ ij++;
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;
}
p_current_segment = p_new_segment;
i_current_title = p_new_segment->i_sys_title;
}
+ 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" );
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 )
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 );
}
}
{
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 );
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 );
return p_result;
}
-