* Steve Lhomme <steve.lhomme@free.fr>
* Denis Charmet <typx@dinauz.org>
*
- * 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 <vector>
{
for( size_t i = 0; i < (*segments).size(); i++ )
{
- if( *p_uid == *((*segments)[i]->p_segment_uid) )
+ if( (*segments)[i]->p_segment_uid &&
+ *p_uid == *((*segments)[i]->p_segment_uid) )
return (*segments)[i];
}
return NULL;
int64_t start = ( b_ordered )? *usertime_offset : p_chap->i_start_time;
int64_t stop = ( b_ordered )? ( *usertime_offset + p_chap->i_end_time - p_chap->i_start_time ) : p_chap->i_end_time;
- if( p_chap->p_segment_uid &&
+ if( p_chap->p_segment_uid &&
( !( p_segment = getSegmentbyUID( (KaxSegmentUID*) p_chap->p_segment_uid,segments ) ) || !b_ordered ) )
{
msg_Warn( &p_main_segment->sys.demuxer,
*usertime_offset = tmp;
msg_Dbg( &p_main_segment->sys.demuxer,
- "Virtual chapter %s from %"PRId64" to %"PRId64" - " ,
+ "Virtual chapter %s from %" PRId64 " to %" PRId64 " - " ,
p_chap->psz_name.c_str(), p_vchap->i_virtual_start_time, p_vchap->i_virtual_stop_time );
return p_vchap;
virtual_edition_c::virtual_edition_c( chapter_edition_c * p_edit, std::vector<matroska_segment_c*> *opened_segments)
{
+ bool b_fake_ordered = false;
matroska_segment_c *p_main_segment = (*opened_segments)[0];
p_edition = p_edit;
+ b_ordered = false;
int64_t usertime_offset = 0;
if( p_vchap )
chapters.push_back( p_vchap );
}
- i_duration = chapters[ chapters.size() - 1 ]->i_virtual_stop_time;
+ if( chapters.size() )
+ i_duration = chapters[ chapters.size() - 1 ]->i_virtual_stop_time;
+ else
+ i_duration = 0; /* Empty ordered editions will be ignored */
}
else /* Not ordered or no edition at all */
{
- b_ordered = false;
matroska_segment_c * p_cur = p_main_segment;
virtual_chapter_c * p_vchap = NULL;
int64_t tmp = 0;
/* check for prev linked segments */
- /* FIXME to avoid infinite recursion we limit to 5 prev sould be better as parameter */
- for( int limit = 0; limit < 5 && p_cur->p_prev_segment_uid ; limit++ )
+ /* FIXME to avoid infinite recursion we limit to 10 prev should be better as parameter */
+ for( int limit = 0; limit < 10 && p_cur->p_prev_segment_uid ; limit++ )
{
matroska_segment_c * p_prev = NULL;
if( ( p_prev = getSegmentbyUID( p_cur->p_prev_segment_uid, opened_segments ) ) )
msg_Dbg( &p_main_segment->sys.demuxer, "Prev segment 0x%x found\n",
*(int32_t*)p_cur->p_prev_segment_uid->GetBuffer() );
+ /* Preload segment */
+ if ( !p_prev->b_preloaded )
+ p_prev->Preload();
+
/* Create virtual_chapter from the first edition if any */
chapter_item_c * p_chap = ( p_prev->stored_editions.size() > 0 )? ((chapter_item_c *)p_prev->stored_editions[0]) : NULL;
chapters.insert( chapters.begin(), p_vchap );
p_cur = p_prev;
+ b_fake_ordered = true;
}
else /* segment not found */
break;
chapters.push_back( p_vchap );
/* Append next linked segments */
- for( int limit = 0; limit < 5 && p_cur->p_next_segment_uid; limit++ )
+ for( int limit = 0; limit < 10 && p_cur->p_next_segment_uid; limit++ )
{
matroska_segment_c * p_next = NULL;
if( ( p_next = getSegmentbyUID( p_cur->p_next_segment_uid, opened_segments ) ) )
msg_Dbg( &p_main_segment->sys.demuxer, "Next segment 0x%x found\n",
*(int32_t*) p_cur->p_next_segment_uid->GetBuffer() );
+ /* Preload segment */
+ if ( !p_next->b_preloaded )
+ p_next->Preload();
+
/* Create virtual_chapter from the first edition if any */
chapter_item_c * p_chap = ( p_next->stored_editions.size() > 0 )?( (chapter_item_c *)p_next->stored_editions[0] ) : NULL;
p_cur = p_next;
+ b_fake_ordered = true;
}
else /* segment not found */
break;
/* Retime chapters */
retimeChapters();
+ if(b_fake_ordered)
+ b_ordered = true;
}
#if MKV_DEBUG
i_duration = 0;
- /* Sort by start time */
- if( chapters.size() > 1 )
- std::sort( chapters.begin(), chapters.end(), virtual_chapter_c::CompareTimecode );
-
/* On non ordered editions we have one top chapter == one segment */
for( size_t i = 0; i < chapters.size(); i++ )
{
virtual_segment_c::virtual_segment_c( std::vector<matroska_segment_c*> * p_opened_segments )
{
/* Main segment */
+ std::vector<chapter_edition_c*>::size_type i;
matroska_segment_c *p_segment = (*p_opened_segments)[0];
i_current_edition = 0;
i_sys_title = 0;
p_current_chapter = NULL;
- for( size_t i = 0; i < p_segment->stored_editions.size(); i++ )
- {
- /* Get the default edition, if non use the first one */
- if( p_segment->stored_editions[i]->b_default )
- i_current_edition = i;
+ i_current_edition = p_segment->i_default_edition;
+ for( i = 0; i < p_segment->stored_editions.size(); i++ )
+ {
/* Create a virtual edition from opened */
virtual_edition_c * p_vedition = new virtual_edition_c( p_segment->stored_editions[i], p_opened_segments );
- /*FIXME if p_vedition failed...*/
+ /* Ordered empty edition can happen when all chapters are
+ * on an other segment which couldn't be found... ignore it */
+ if(p_vedition->b_ordered && p_vedition->i_duration == 0)
+ {
- editions.push_back( p_vedition );
+ msg_Warn( &p_segment->sys.demuxer,
+ "Edition %s (%zu) links to other segments not found and is empty... ignoring it",
+ p_vedition->GetMainName().c_str(), i );
+ if(i_current_edition == i)
+ {
+ msg_Warn( &p_segment->sys.demuxer,
+ "Empty edition was the default... defaulting to 0");
+ i_current_edition = 0;
+ }
+ delete p_vedition;
+ }
+ else
+ editions.push_back( p_vedition );
}
/*if we don't have edition create a dummy one*/
if( !p_segment->stored_editions.size() )
editions.push_back( p_vedition );
}
+ /* Get the default edition, if there is none, use the first one */
+ for( i = 0; i < editions.size(); i++)
+ {
+ if( editions[i]->p_edition && editions[i]->p_edition->b_default )
+ {
+ i_current_edition = i;
+ break;
+ }
+ }
/* Set current chapter */
p_current_chapter = editions[i_current_edition]->getChapterbyTimecode(0);
/* we have moved to a new chapter */
if ( p_cur_chapter != NULL && p_current_chapter != p_cur_chapter )
{
- msg_Dbg( &demux, "NEW CHAPTER %"PRId64, sys.i_pts );
+ msg_Dbg( &demux, "NEW CHAPTER %" PRId64, sys.i_pts );
if ( p_cur_edition->b_ordered )
{
/* FIXME EnterAndLeave has probably been broken for a long time */
{
// only physically seek if necessary
if ( p_current_chapter == NULL ||
- ( p_current_chapter->p_chapter->i_end_time != p_cur_chapter->p_chapter->i_start_time ) ||
- ( p_current_chapter && p_current_chapter->p_segment != p_cur_chapter->p_segment ) )
+ ( p_current_chapter && p_current_chapter->p_segment != p_cur_chapter->p_segment ) ||
+ ( p_current_chapter->p_chapter->i_end_time != p_cur_chapter->p_chapter->i_start_time ))
{
- /* hack : we have to use input to seek in order to clean buffers */
- var_SetTime( demux.p_sys->p_input, "time", p_cur_chapter->i_virtual_start_time );
+ Seek( demux, p_cur_chapter->i_virtual_start_time, 0, p_cur_chapter, -1 );
return true;
}
}
+ sys.i_start_pts = p_cur_chapter->i_virtual_start_time;;
}
p_current_chapter = p_cur_chapter;
return p_chapter->EnterAndLeave( p_item->p_chapter, b_enter );
}
-void virtual_segment_c::Seek( demux_t & demuxer, mtime_t i_date, mtime_t i_time_offset,
+void virtual_segment_c::Seek( demux_t & demuxer, mtime_t i_date, mtime_t i_time_offset,
virtual_chapter_c *p_chapter, int64_t i_global_position )
{
demux_sys_t *p_sys = demuxer.p_sys;
if ( p_chapter != NULL )
{
- p_sys->i_chapter_time =
- i_time_offset = p_chapter->i_virtual_start_time - ( ( p_chapter->p_chapter )? p_chapter->p_chapter->i_start_time : 0 );
+ i_time_offset = p_chapter->i_virtual_start_time - ( ( p_chapter->p_chapter )? p_chapter->p_chapter->i_start_time : 0 );
+ p_sys->i_chapter_time = i_time_offset - p_chapter->p_segment->i_start_time;
if ( p_chapter->p_chapter && p_chapter->i_seekpoint_num > 0 )
{
demuxer.info.i_update |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
}
if( p_current_chapter->p_segment != p_chapter->p_segment )
- {
- p_current_chapter->p_segment->UnSelect();
- es_out_Control( demuxer.out, ES_OUT_RESET_PCR );
- p_chapter->p_segment->Select( i_date );
- }
+ ChangeSegment( p_current_chapter->p_segment, p_chapter->p_segment, i_date );
p_current_chapter = p_chapter;
p_chapter->p_segment->Seek( i_date, i_time_offset, i_global_position );
}
#if MKV_DEBUG
-void virtual_chapter_c::print()
+void virtual_chapter_c::print()
{
msg_Dbg( &p_segment->sys.demuxer, "*** chapter %"PRId64" - %"PRId64" (%u)",
i_virtual_start_time, i_virtual_stop_time, sub_chapters.size() );
sub_chapters[i]->print();
}
#endif
+
+void virtual_segment_c::ChangeSegment( matroska_segment_c * p_old, matroska_segment_c * p_new, mtime_t i_start_time )
+{
+ size_t i, j;
+ char *sub_lang = NULL, *aud_lang = NULL;
+ for( i = 0; i < p_old->tracks.size(); i++)
+ {
+ mkv_track_t *p_tk = p_old->tracks[i];
+ es_format_t *p_ofmt = &p_tk->fmt;
+ if( p_tk->p_es )
+ {
+ bool state = false;
+ es_out_Control( p_old->sys.demuxer.out, ES_OUT_GET_ES_STATE, p_tk->p_es, &state );
+ if( state )
+ {
+ if( p_ofmt->i_cat == AUDIO_ES )
+ aud_lang = p_tk->fmt.psz_language;
+ else if( p_ofmt->i_cat == SPU_ES )
+ sub_lang = p_tk->fmt.psz_language;
+ }
+ }
+ }
+ for( i = 0; i < p_new->tracks.size(); i++)
+ {
+ mkv_track_t *p_tk = p_new->tracks[i];
+ es_format_t *p_nfmt = &p_tk->fmt;
+
+ /* Let's only do that for audio and video for now */
+ if( p_nfmt->i_cat == AUDIO_ES || p_nfmt->i_cat == VIDEO_ES )
+ {
+
+ /* check for a similar elementary stream */
+ for( j = 0; j < p_old->tracks.size(); j++)
+ {
+ es_format_t * p_ofmt = &p_old->tracks[j]->fmt;
+
+ if( !p_old->tracks[j]->p_es )
+ continue;
+
+ if( ( p_nfmt->i_cat == p_ofmt->i_cat ) &&
+ ( p_nfmt->i_codec == p_ofmt->i_codec ) &&
+ ( p_nfmt->i_priority == p_ofmt->i_priority ) &&
+ ( p_nfmt->i_bitrate == p_ofmt->i_bitrate ) &&
+ ( p_nfmt->i_extra == p_ofmt->i_extra ) &&
+ ( p_nfmt->i_extra == 0 ||
+ !memcmp( p_nfmt->p_extra, p_ofmt->p_extra, p_nfmt->i_extra ) ) &&
+ !strcasecmp( p_nfmt->psz_language, p_ofmt->psz_language ) &&
+ ( ( p_nfmt->i_cat == AUDIO_ES &&
+ !memcmp( &p_nfmt->audio, &p_ofmt->audio, sizeof(audio_format_t) ) ) ||
+ ( p_nfmt->i_cat == VIDEO_ES &&
+ !memcmp( &p_nfmt->video, &p_ofmt->video, sizeof(video_format_t) ) ) ) )
+ {
+ /* FIXME handle video palettes... */
+ msg_Warn( &p_old->sys.demuxer, "Reusing decoder of old track %zu for track %zu", j, i);
+ p_tk->p_es = p_old->tracks[j]->p_es;
+ p_old->tracks[j]->p_es = NULL;
+ break;
+ }
+ }
+ }
+ p_tk->fmt.i_priority &= ~(0x10);
+ if( ( sub_lang && p_nfmt->i_cat == SPU_ES && !strcasecmp(sub_lang, p_nfmt->psz_language) ) ||
+ ( aud_lang && p_nfmt->i_cat == AUDIO_ES && !strcasecmp(aud_lang, p_nfmt->psz_language) ) )
+ {
+ msg_Warn( &p_old->sys.demuxer, "Since previous segment used lang %s forcing track %zu",
+ p_nfmt->psz_language, i);
+ p_tk->fmt.i_priority |= 0x10;
+ p_tk->b_forced = true;
+ }
+ }
+ p_new->Select( i_start_time );
+ p_old->UnSelect();
+}