Commit e89b2807 authored by Steve Lhomme's avatar Steve Lhomme

mkv.cpp: support the CallSS VTSM DVD command and cleaner jump between chapters & segments

parent eeceed77
...@@ -332,6 +332,14 @@ typedef struct ...@@ -332,6 +332,14 @@ typedef struct
class demux_sys_t; class demux_sys_t;
const binary MATROSKA_DVD_LEVEL_SS = 0x30;
const binary MATROSKA_DVD_LEVEL_LU = 0x2A;
const binary MATROSKA_DVD_LEVEL_TT = 0x28;
const binary MATROSKA_DVD_LEVEL_PGC = 0x20;
const binary MATROSKA_DVD_LEVEL_PG = 0x18;
const binary MATROSKA_DVD_LEVEL_PTT = 0x10;
const binary MATROSKA_DVD_LEVEL_CN = 0x08;
class chapter_codec_cmds_c class chapter_codec_cmds_c
{ {
public: public:
...@@ -429,6 +437,7 @@ protected: ...@@ -429,6 +437,7 @@ protected:
// callbacks when browsing inside CodecPrivate // callbacks when browsing inside CodecPrivate
static bool MatchTitleNumber( const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ); static bool MatchTitleNumber( const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size );
static bool MatchPgcType ( const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size );
}; };
class dvd_chapter_codec_c : public chapter_codec_cmds_c class dvd_chapter_codec_c : public chapter_codec_cmds_c
...@@ -499,6 +508,7 @@ public: ...@@ -499,6 +508,7 @@ public:
const void *p_cookie, const void *p_cookie,
size_t i_cookie_size ); size_t i_cookie_size );
std::string GetCodecName( bool f_for_title = false ) const; std::string GetCodecName( bool f_for_title = false ) const;
bool ParentOf( const chapter_item_c & item ) const;
int64_t i_start_time, i_end_time; int64_t i_start_time, i_end_time;
int64_t i_user_start_time, i_user_end_time; /* the time in the stream when an edition is ordered */ int64_t i_user_start_time, i_user_end_time; /* the time in the stream when an edition is ordered */
...@@ -517,8 +527,9 @@ public: ...@@ -517,8 +527,9 @@ public:
return ( i_user_start_time < item.i_user_start_time || (i_user_start_time == item.i_user_start_time && i_user_end_time < item.i_user_end_time) ); return ( i_user_start_time < item.i_user_start_time || (i_user_start_time == item.i_user_start_time && i_user_end_time < item.i_user_end_time) );
} }
bool Enter(); bool Enter( bool b_do_subchapters );
bool Leave(); bool Leave( bool b_do_subchapters );
bool EnterAndLeave( chapter_item_c *p_item );
}; };
class chapter_edition_c : public chapter_item_c class chapter_edition_c : public chapter_item_c
...@@ -752,7 +763,7 @@ public: ...@@ -752,7 +763,7 @@ public:
return false; return false;
} }
void UpdateCurrentToChapter( demux_t & demux ); bool UpdateCurrentToChapter( demux_t & demux );
void PrepareChapters( ); void PrepareChapters( );
chapter_item_c *BrowseCodecPrivate( unsigned int codec_id, chapter_item_c *BrowseCodecPrivate( unsigned int codec_id,
...@@ -1861,7 +1872,7 @@ int chapter_item_c::PublishChapters( input_title_t & title, int & i_user_chapter ...@@ -1861,7 +1872,7 @@ int chapter_item_c::PublishChapters( input_title_t & title, int & i_user_chapter
return i_user_chapters; return i_user_chapters;
} }
void virtual_segment_c::UpdateCurrentToChapter( demux_t & demux ) bool virtual_segment_c::UpdateCurrentToChapter( demux_t & demux )
{ {
demux_sys_t & sys = *demux.p_sys; demux_sys_t & sys = *demux.p_sys;
chapter_item_c *psz_curr_chapter; chapter_item_c *psz_curr_chapter;
...@@ -1875,12 +1886,10 @@ void virtual_segment_c::UpdateCurrentToChapter( demux_t & demux ) ...@@ -1875,12 +1886,10 @@ void virtual_segment_c::UpdateCurrentToChapter( demux_t & demux )
/* we have moved to a new chapter */ /* we have moved to a new chapter */
if (psz_curr_chapter != NULL && psz_current_chapter != psz_curr_chapter) if (psz_curr_chapter != NULL && psz_current_chapter != psz_curr_chapter)
{ {
if ( psz_current_chapter != NULL )
psz_current_chapter->Leave();
if ( (*p_editions)[i_current_edition]->b_ordered ) if ( (*p_editions)[i_current_edition]->b_ordered )
{ {
if ( !psz_curr_chapter->Enter() ) // Leave/Enter up to the link point
if ( !psz_curr_chapter->EnterAndLeave( psz_current_chapter ) )
{ {
// only seek if necessary // only seek if necessary
if ( psz_current_chapter == NULL || (psz_current_chapter->i_end_time != psz_curr_chapter->i_start_time) ) if ( psz_current_chapter == NULL || (psz_current_chapter->i_end_time != psz_curr_chapter->i_start_time) )
...@@ -1895,8 +1904,10 @@ void virtual_segment_c::UpdateCurrentToChapter( demux_t & demux ) ...@@ -1895,8 +1904,10 @@ void virtual_segment_c::UpdateCurrentToChapter( demux_t & demux )
} }
psz_current_chapter = psz_curr_chapter; psz_current_chapter = psz_curr_chapter;
return true;
} }
} }
return false;
} }
chapter_item_c *virtual_segment_c::BrowseCodecPrivate( unsigned int codec_id, chapter_item_c *virtual_segment_c::BrowseCodecPrivate( unsigned int codec_id,
...@@ -1999,7 +2010,7 @@ std::string dvd_chapter_codec_c::GetCodecName( bool f_for_title ) const ...@@ -1999,7 +2010,7 @@ std::string dvd_chapter_codec_c::GetCodecName( bool f_for_title ) const
if ( m_private_data.GetSize() >= 3) if ( m_private_data.GetSize() >= 3)
{ {
const binary* p_data = m_private_data.GetBuffer(); const binary* p_data = m_private_data.GetBuffer();
/* if ( p_data[0] == 0x28 ) /* if ( p_data[0] == MATROSKA_DVD_LEVEL_TT )
{ {
uint16_t i_title = (p_data[1] << 8) + p_data[2]; uint16_t i_title = (p_data[1] << 8) + p_data[2];
char psz_str[11]; char psz_str[11];
...@@ -2007,14 +2018,14 @@ std::string dvd_chapter_codec_c::GetCodecName( bool f_for_title ) const ...@@ -2007,14 +2018,14 @@ std::string dvd_chapter_codec_c::GetCodecName( bool f_for_title ) const
result = N_("--- DVD Title"); result = N_("--- DVD Title");
result += psz_str; result += psz_str;
} }
else */ if ( p_data[0] == 0x2A ) else */ if ( p_data[0] == MATROSKA_DVD_LEVEL_LU )
{ {
char psz_str[11]; char psz_str[11];
sprintf( psz_str, " (%c%c) ---", p_data[1], p_data[2] ); sprintf( psz_str, " (%c%c) ---", p_data[1], p_data[2] );
result = N_("--- DVD Menu"); result = N_("--- DVD Menu");
result += psz_str; result += psz_str;
} }
else if ( p_data[0] == 0x30 && f_for_title ) else if ( p_data[0] == MATROSKA_DVD_LEVEL_SS && f_for_title )
{ {
if ( p_data[1] == 0x00 ) if ( p_data[1] == 0x00 )
result = N_("First Played"); result = N_("First Played");
...@@ -2138,7 +2149,8 @@ static int Demux( demux_t *p_demux) ...@@ -2138,7 +2149,8 @@ static int Demux( demux_t *p_demux)
return 0; return 0;
if( p_sys->i_pts >= p_sys->i_start_pts ) if( p_sys->i_pts >= p_sys->i_start_pts )
p_vsegment->UpdateCurrentToChapter( *p_demux ); if ( p_vsegment->UpdateCurrentToChapter( *p_demux ) )
return 1;
if ( p_vsegment->Edition() && p_vsegment->Edition()->b_ordered && p_vsegment->CurrentChapter() == NULL ) if ( p_vsegment->Edition() && p_vsegment->Edition()->b_ordered && p_vsegment->CurrentChapter() == NULL )
{ {
...@@ -3777,6 +3789,22 @@ chapter_item_c *chapter_item_c::FindTimecode( mtime_t i_user_timecode ) ...@@ -3777,6 +3789,22 @@ chapter_item_c *chapter_item_c::FindTimecode( mtime_t i_user_timecode )
return psz_result; return psz_result;
} }
bool chapter_item_c::ParentOf( const chapter_item_c & item ) const
{
if ( &item == this )
return true;
std::vector<chapter_item_c*>::const_iterator index = sub_chapters.begin();
while ( index != sub_chapters.end() )
{
if ( (*index)->ParentOf( item ) )
return true;
index++;
}
return false;
}
void demux_sys_t::PreloadFamily( const matroska_segment_c & of_segment ) void demux_sys_t::PreloadFamily( const matroska_segment_c & of_segment )
{ {
for (size_t i=0; i<opened_segments.size(); i++) for (size_t i=0; i<opened_segments.size(); i++)
...@@ -4308,7 +4336,7 @@ void chapter_codec_cmds_c::AddCommand( const KaxChapterProcessCommand & command ...@@ -4308,7 +4336,7 @@ void chapter_codec_cmds_c::AddCommand( const KaxChapterProcessCommand & command
} }
} }
bool chapter_item_c::Enter() bool chapter_item_c::Enter( bool b_do_subs )
{ {
bool f_result = false; bool f_result = false;
std::vector<chapter_codec_cmds_c*>::iterator index = codecs.begin(); std::vector<chapter_codec_cmds_c*>::iterator index = codecs.begin();
...@@ -4317,16 +4345,21 @@ bool chapter_item_c::Enter() ...@@ -4317,16 +4345,21 @@ bool chapter_item_c::Enter()
f_result |= (*index)->Enter(); f_result |= (*index)->Enter();
index++; index++;
} }
std::vector<chapter_item_c*>::iterator index_ = sub_chapters.begin();
while ( index_ != sub_chapters.end() ) if ( b_do_subs )
{ {
f_result |= (*index_)->Enter(); // sub chapters
index_++; std::vector<chapter_item_c*>::iterator index_ = sub_chapters.begin();
while ( index_ != sub_chapters.end() )
{
f_result |= (*index_)->Enter( true );
index_++;
}
} }
return f_result; return f_result;
} }
bool chapter_item_c::Leave() bool chapter_item_c::Leave( bool b_do_subs )
{ {
bool f_result = false; bool f_result = false;
std::vector<chapter_codec_cmds_c*>::iterator index = codecs.begin(); std::vector<chapter_codec_cmds_c*>::iterator index = codecs.begin();
...@@ -4335,15 +4368,57 @@ bool chapter_item_c::Leave() ...@@ -4335,15 +4368,57 @@ bool chapter_item_c::Leave()
f_result |= (*index)->Leave(); f_result |= (*index)->Leave();
index++; index++;
} }
std::vector<chapter_item_c*>::iterator index_ = sub_chapters.begin();
while ( index_ != sub_chapters.end() ) if ( b_do_subs )
{ {
f_result |= (*index_)->Leave(); // sub chapters
index_++; std::vector<chapter_item_c*>::iterator index_ = sub_chapters.begin();
while ( index_ != sub_chapters.end() )
{
f_result |= (*index_)->Leave( true );
index_++;
}
} }
return f_result; return f_result;
} }
bool chapter_item_c::EnterAndLeave( chapter_item_c *p_item )
{
chapter_item_c *p_common_parent = p_item;
// leave, up to a common parent
while ( p_common_parent != NULL && !p_common_parent->ParentOf( *this ) )
{
if ( p_common_parent->Leave( false ) )
return true;
p_common_parent = p_common_parent->psz_parent;
}
// enter from the parent to <this>
if ( p_common_parent != NULL )
{
do
{
for ( size_t i = 0; i<p_common_parent->sub_chapters.size(); i++ )
{
if ( p_common_parent->sub_chapters[i]->ParentOf( *this ) )
{
p_common_parent = p_common_parent->sub_chapters[i];
break;
}
}
if ( p_common_parent == this )
break;
if ( p_common_parent->Enter( false ) )
return true;
} while ( 1 );
}
return Enter( true );
}
bool dvd_chapter_codec_c::Enter() bool dvd_chapter_codec_c::Enter()
{ {
bool f_result = false; bool f_result = false;
...@@ -4394,6 +4469,8 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si ...@@ -4394,6 +4469,8 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si
if ( i_size != 8 ) if ( i_size != 8 )
return false; return false;
virtual_segment_c *p_segment;
chapter_item_c *p_chapter;
bool f_result = false; bool f_result = false;
uint16 i_command = ( p_command[0] << 8 ) + p_command[1]; uint16 i_command = ( p_command[0] << 8 ) + p_command[1];
...@@ -4405,8 +4482,6 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si ...@@ -4405,8 +4482,6 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si
msg_Dbg( &sys.demuxer, "DVD command: JumpTT %d", i_title ); msg_Dbg( &sys.demuxer, "DVD command: JumpTT %d", i_title );
// find in the ChapProcessPrivate matching this Title level // find in the ChapProcessPrivate matching this Title level
virtual_segment_c *p_segment;
chapter_item_c *p_chapter;
p_chapter = sys.BrowseCodecPrivate( 1, MatchTitleNumber, &i_title, sizeof(i_title), p_segment ); p_chapter = sys.BrowseCodecPrivate( 1, MatchTitleNumber, &i_title, sizeof(i_title), p_segment );
if ( p_chapter != NULL ) if ( p_chapter != NULL )
{ {
...@@ -4416,10 +4491,10 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si ...@@ -4416,10 +4491,10 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si
sys.PreparePlayback( p_segment ); sys.PreparePlayback( p_segment );
} }
p_chapter->Enter();
// jump to the location in the found segment // jump to the location in the found segment
p_segment->Seek( sys.demuxer, p_chapter->i_user_start_time, -1, p_chapter ); p_segment->Seek( sys.demuxer, p_chapter->i_user_start_time, -1, NULL );
p_chapter->Enter( true );
f_result = true; f_result = true;
} }
...@@ -4428,6 +4503,61 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si ...@@ -4428,6 +4503,61 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si
case CMD_CALLSS_VTSM: case CMD_CALLSS_VTSM:
{ {
msg_Dbg( &sys.demuxer, "DVD command: CallSS VTSM" ); msg_Dbg( &sys.demuxer, "DVD command: CallSS VTSM" );
switch( (p_command[6] & 0xC0) >> 6 ) {
case 0:
switch ( p_command[5] )
{
case 0x00:
msg_Dbg( &sys.demuxer, "CallSS PGC (rsm_cell %x)", p_command[5]);
break;
case 0x82:
msg_Dbg( &sys.demuxer, "CallSS Title Entry (rsm_cell %x)", p_command[5]);
break;
case 0x83:
msg_Dbg( &sys.demuxer, "CallSS Root Menu (rsm_cell %x)", p_command[5]);
break;
case 0x84:
msg_Dbg( &sys.demuxer, "CallSS Subpicture Menu (rsm_cell %x)", p_command[5]);
break;
case 0x85:
msg_Dbg( &sys.demuxer, "CallSS Audio Menu (rsm_cell %x)", p_command[5]);
break;
case 0x86:
msg_Dbg( &sys.demuxer, "CallSS Angle Menu (rsm_cell %x)", p_command[5]);
break;
case 0x87:
msg_Dbg( &sys.demuxer, "CallSS Chapter Menu (rsm_cell %x)", p_command[5]);
break;
default:
msg_Dbg( &sys.demuxer, "CallSS <unknown> (rsm_cell %x)", p_command[5]);
break;
}
p_chapter = sys.BrowseCodecPrivate( 1, MatchPgcType, &p_command[5], 1, p_segment );
if ( p_chapter != NULL )
{
// if the segment is not part of the current segment, select the new one
if ( p_segment != sys.p_current_segment )
{
sys.PreparePlayback( p_segment );
}
p_chapter->Enter( true );
// jump to the location in the found segment
p_segment->Seek( sys.demuxer, p_chapter->i_user_start_time, -1, p_chapter );
f_result = true;
}
break;
case 1:
msg_Dbg( &sys.demuxer, "CallSS VMGM (menu %d, rsm_cell %x)", p_command[6] & 0x0F, p_command[5]);
break;
case 2:
msg_Dbg( &sys.demuxer, "CallSS VTSM (menu %d, rsm_cell %x)", p_command[6] & 0x0F, p_command[5]);
break;
case 3:
msg_Dbg( &sys.demuxer, "CallSS VMGM (pgc %d, rsm_cell %x)", (p_command[3] << 8) + p_command[4], p_command[5]);
break;
}
break; break;
} }
default: default:
...@@ -4453,7 +4583,7 @@ bool dvd_command_interpretor_c::MatchTitleNumber( const chapter_codec_cmds_c &da ...@@ -4453,7 +4583,7 @@ bool dvd_command_interpretor_c::MatchTitleNumber( const chapter_codec_cmds_c &da
if ( i_cookie_size != 1 || data.m_private_data.GetSize() < 4 ) if ( i_cookie_size != 1 || data.m_private_data.GetSize() < 4 )
return false; return false;
if ( data.m_private_data.GetBuffer()[0] != 0x28 ) if ( data.m_private_data.GetBuffer()[0] != MATROSKA_DVD_LEVEL_TT )
return false; return false;
uint16 i_gtitle = (data.m_private_data.GetBuffer()[1] << 8 ) + data.m_private_data.GetBuffer()[2]; uint16 i_gtitle = (data.m_private_data.GetBuffer()[1] << 8 ) + data.m_private_data.GetBuffer()[2];
...@@ -4461,3 +4591,17 @@ bool dvd_command_interpretor_c::MatchTitleNumber( const chapter_codec_cmds_c &da ...@@ -4461,3 +4591,17 @@ bool dvd_command_interpretor_c::MatchTitleNumber( const chapter_codec_cmds_c &da
return (i_gtitle == i_title); return (i_gtitle == i_title);
} }
bool dvd_command_interpretor_c::MatchPgcType( const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size )
{
if ( i_cookie_size != 1 || data.m_private_data.GetSize() < 7 )
return false;
if ( data.m_private_data.GetBuffer()[0] != MATROSKA_DVD_LEVEL_PGC )
return false;
uint8 i_pgc_type = data.m_private_data.GetBuffer()[3];
uint8 i_pgc = *(uint8*)p_cookie;
return (i_pgc_type == i_pgc);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment