]> git.sesse.net Git - vlc/blobdiff - modules/demux/mkv.cpp
mkv.cpp: add a mutex to lock demuxing from another thread
[vlc] / modules / demux / mkv.cpp
index 10e4d736289694044224889e43c10235966255cd..ba83742214e13d017be140c4a9734403bb8a2f3a 100644 (file)
@@ -99,6 +99,11 @@ extern "C" {
 #define MKVD_TIMECODESCALE 1000000
 
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#ifdef VSLHC1
+#define DVDNAV_COMPILE
+#include <dvdnav.h>
+#else /* VSLHC */
+
 #undef ATTRIBUTE_PACKED
 #undef PRAGMA_PACK_BEGIN 
 #undef PRAGMA_PACK_END
@@ -138,65 +143,65 @@ typedef struct {
  */
 typedef struct {
 #ifdef WORDS_BIGENDIAN
-  unsigned int zero                           : 7; /* 25-31 */
-  unsigned int video_pres_mode_change         : 1; /* 24 */
+  unsigned char zero                           : 7; /* 25-31 */
+  unsigned char video_pres_mode_change         : 1; /* 24 */
   
-  unsigned int karaoke_audio_pres_mode_change : 1; /* 23 */
-  unsigned int angle_change                   : 1;
-  unsigned int subpic_stream_change           : 1;
-  unsigned int audio_stream_change            : 1;
-  unsigned int pause_on                       : 1;
-  unsigned int still_off                      : 1;
-  unsigned int button_select_or_activate      : 1;
-  unsigned int resume                         : 1; /* 16 */
+  unsigned char karaoke_audio_pres_mode_change : 1; /* 23 */
+  unsigned char angle_change                   : 1;
+  unsigned char subpic_stream_change           : 1;
+  unsigned char audio_stream_change            : 1;
+  unsigned char pause_on                       : 1;
+  unsigned char still_off                      : 1;
+  unsigned char button_select_or_activate      : 1;
+  unsigned char resume                         : 1; /* 16 */
   
-  unsigned int chapter_menu_call              : 1; /* 15 */
-  unsigned int angle_menu_call                : 1;
-  unsigned int audio_menu_call                : 1;
-  unsigned int subpic_menu_call               : 1;
-  unsigned int root_menu_call                 : 1;
-  unsigned int title_menu_call                : 1;
-  unsigned int backward_scan                  : 1;
-  unsigned int forward_scan                   : 1; /* 8 */
+  unsigned char chapter_menu_call              : 1; /* 15 */
+  unsigned char angle_menu_call                : 1;
+  unsigned char audio_menu_call                : 1;
+  unsigned char subpic_menu_call               : 1;
+  unsigned char root_menu_call                 : 1;
+  unsigned char title_menu_call                : 1;
+  unsigned char backward_scan                  : 1;
+  unsigned char forward_scan                   : 1; /* 8 */
   
-  unsigned int next_pg_search                 : 1; /* 7 */
-  unsigned int prev_or_top_pg_search          : 1;
-  unsigned int time_or_chapter_search         : 1;
-  unsigned int go_up                          : 1;
-  unsigned int stop                           : 1;
-  unsigned int title_play                     : 1;
-  unsigned int chapter_search_or_play         : 1;
-  unsigned int title_or_time_play             : 1; /* 0 */
+  unsigned char next_pg_search                 : 1; /* 7 */
+  unsigned char prev_or_top_pg_search          : 1;
+  unsigned char time_or_chapter_search         : 1;
+  unsigned char go_up                          : 1;
+  unsigned char stop                           : 1;
+  unsigned char title_play                     : 1;
+  unsigned char chapter_search_or_play         : 1;
+  unsigned char title_or_time_play             : 1; /* 0 */
 #else
-  unsigned int video_pres_mode_change         : 1; /* 24 */
-  unsigned int zero                           : 7; /* 25-31 */
+  unsigned char video_pres_mode_change         : 1; /* 24 */
+  unsigned char zero                           : 7; /* 25-31 */
   
-  unsigned int resume                         : 1; /* 16 */
-  unsigned int button_select_or_activate      : 1;
-  unsigned int still_off                      : 1;
-  unsigned int pause_on                       : 1;
-  unsigned int audio_stream_change            : 1;
-  unsigned int subpic_stream_change           : 1;
-  unsigned int angle_change                   : 1;
-  unsigned int karaoke_audio_pres_mode_change : 1; /* 23 */
+  unsigned char resume                         : 1; /* 16 */
+  unsigned char button_select_or_activate      : 1;
+  unsigned char still_off                      : 1;
+  unsigned char pause_on                       : 1;
+  unsigned char audio_stream_change            : 1;
+  unsigned char subpic_stream_change           : 1;
+  unsigned char angle_change                   : 1;
+  unsigned char karaoke_audio_pres_mode_change : 1; /* 23 */
   
-  unsigned int forward_scan                   : 1; /* 8 */
-  unsigned int backward_scan                  : 1;
-  unsigned int title_menu_call                : 1;
-  unsigned int root_menu_call                 : 1;
-  unsigned int subpic_menu_call               : 1;
-  unsigned int audio_menu_call                : 1;
-  unsigned int angle_menu_call                : 1;
-  unsigned int chapter_menu_call              : 1; /* 15 */
+  unsigned char forward_scan                   : 1; /* 8 */
+  unsigned char backward_scan                  : 1;
+  unsigned char title_menu_call                : 1;
+  unsigned char root_menu_call                 : 1;
+  unsigned char subpic_menu_call               : 1;
+  unsigned char audio_menu_call                : 1;
+  unsigned char angle_menu_call                : 1;
+  unsigned char chapter_menu_call              : 1; /* 15 */
   
-  unsigned int title_or_time_play             : 1; /* 0 */
-  unsigned int chapter_search_or_play         : 1;
-  unsigned int title_play                     : 1;
-  unsigned int stop                           : 1;
-  unsigned int go_up                          : 1;
-  unsigned int time_or_chapter_search         : 1;
-  unsigned int prev_or_top_pg_search          : 1;
-  unsigned int next_pg_search                 : 1; /* 7 */
+  unsigned char title_or_time_play             : 1; /* 0 */
+  unsigned char chapter_search_or_play         : 1;
+  unsigned char title_play                     : 1;
+  unsigned char stop                           : 1;
+  unsigned char go_up                          : 1;
+  unsigned char time_or_chapter_search         : 1;
+  unsigned char prev_or_top_pg_search          : 1;
+  unsigned char next_pg_search                 : 1; /* 7 */
 #endif
 } ATTRIBUTE_PACKED user_ops_t;
 
@@ -293,41 +298,41 @@ typedef struct {
  */
 typedef struct {
 #ifdef WORDS_BIGENDIAN
-  unsigned int btn_coln         : 2;  /**< button color number */
-  unsigned int x_start          : 10; /**< x start offset within the overlay */
-  unsigned int zero1            : 2;  /**< reserved */
-  unsigned int x_end            : 10; /**< x end offset within the overlay */
+  uint32        btn_coln         : 2;  /**< button color number */
+  uint32        x_start          : 10; /**< x start offset within the overlay */
+  uint32        zero1            : 2;  /**< reserved */
+  uint32        x_end            : 10; /**< x end offset within the overlay */
 
-  unsigned int zero3            : 2;  /**< reserved */
-  unsigned int up               : 6;  /**< button index when pressing up */
+  uint32        zero3            : 2;  /**< reserved */
+  uint32        up               : 6;  /**< button index when pressing up */
 
-  unsigned int auto_action_mode : 2;  /**< 0: no, 1: activated if selected */
-  unsigned int y_start          : 10; /**< y start offset within the overlay */
-  unsigned int zero2            : 2;  /**< reserved */
-  unsigned int y_end            : 10; /**< y end offset within the overlay */
+  uint32        auto_action_mode : 2;  /**< 0: no, 1: activated if selected */
+  uint32        y_start          : 10; /**< y start offset within the overlay */
+  uint32        zero2            : 2;  /**< reserved */
+  uint32        y_end            : 10; /**< y end offset within the overlay */
 
-  unsigned int zero4            : 2;  /**< reserved */
-  unsigned int down             : 6;  /**< button index when pressing down */
+  uint32        zero4            : 2;  /**< reserved */
+  uint32        down             : 6;  /**< button index when pressing down */
   unsigned char zero5            : 2;  /**< reserved */
   unsigned char left             : 6;  /**< button index when pressing left */
   unsigned char zero6            : 2;  /**< reserved */
   unsigned char right            : 6;  /**< button index when pressing right */
 #else
-  unsigned int x_end            : 10;
-  unsigned int zero1            : 2;
-  unsigned int x_start          : 10;
-  unsigned int btn_coln         : 2;
+  uint32        x_end            : 10;
+  uint32        zero1            : 2;
+  uint32        x_start          : 10;
+  uint32        btn_coln         : 2;
 
-  unsigned int up               : 6;
-  unsigned int zero3            : 2;
+  uint32        up               : 6;
+  uint32        zero3            : 2;
 
-  unsigned int y_end            : 10;
-  unsigned int zero2            : 2;
-  unsigned int y_start          : 10;
-  unsigned int auto_action_mode : 2;
+  uint32        y_end            : 10;
+  uint32        zero2            : 2;
+  uint32        y_start          : 10;
+  uint32        auto_action_mode : 2;
 
-  unsigned int down             : 6;
-  unsigned int zero4            : 2;
+  uint32        down             : 6;
+  uint32        zero4            : 2;
   unsigned char left             : 6;
   unsigned char zero5            : 2;
   unsigned char right            : 6;
@@ -360,6 +365,7 @@ typedef struct {
 #pragma pack()
 #endif
 
+#endif /* VSLHC */
 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
 
@@ -659,7 +665,6 @@ public:
     
     bool Interpret( const binary * p_command, size_t i_size = 8 );
     
-protected:
     uint16 GetPRM( size_t index ) const
     {
         if ( index < 256 )
@@ -712,6 +717,7 @@ protected:
         return false;
     }
 
+protected:
     std::string GetRegTypeName( bool b_value, uint16 value ) const
     {
         std::string result;
@@ -791,6 +797,8 @@ protected:
     // 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 MatchPgcType    ( const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size );
+    static bool MatchPgcNumber  ( const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size );
+    static bool MatchCellNumber ( 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
@@ -798,15 +806,11 @@ class dvd_chapter_codec_c : public chapter_codec_cmds_c
 public:
     dvd_chapter_codec_c( demux_sys_t & sys )
     :chapter_codec_cmds_c( sys, 1 )
-    ,interpretor( sys )
     {}
 
     bool Enter();
     bool Leave();
     std::string GetCodecName( bool f_for_title = false ) const;
-
-protected:
-    dvd_command_interpretor_c interpretor; 
 };
 
 class matroska_script_interpretor_c
@@ -1216,12 +1220,15 @@ public:
         ,meta(NULL)
         ,i_current_title(0)
         ,p_current_segment(NULL)
+        ,dvd_interpretor( *this )
         ,f_duration(-1.0)
         ,b_ui_hooked(false)
         ,p_input(NULL)
-        ,p_pci_packet(NULL)
+        ,i_curr_button(0)
         ,p_ev(NULL)
-    {}
+    {
+        vlc_mutex_init( &demuxer, &lock_demuxer );
+    }
 
     virtual ~demux_sys_t()
     {
@@ -1233,6 +1240,7 @@ public:
             delete opened_segments[i];
         for ( i=0; i<used_segments.size(); i++ )
             delete used_segments[i];
+        vlc_mutex_destroy( &lock_demuxer );
     }
 
     /* current data */
@@ -1252,6 +1260,8 @@ public:
     std::vector<virtual_segment_c*>  used_segments;
     virtual_segment_c                *p_current_segment;
 
+    dvd_command_interpretor_c        dvd_interpretor;
+
     /* duration of the stream */
     float                   f_duration;
 
@@ -1271,10 +1281,14 @@ public:
     void StartUiThread();
     void StopUiThread();
     bool b_ui_hooked;
+    inline void SwapButtons();
 
     /* for spu variables */
     input_thread_t *p_input;
-    block_t        *p_pci_packet;
+    pci_t          pci_packet;
+    int16          i_curr_button;
+    uint8_t        alpha[4];
+    vlc_mutex_t    lock_demuxer;
 
     /* event */
     event_thread_t *p_ev;
@@ -1586,8 +1600,9 @@ int matroska_segment_c::BlockGet( KaxBlock **pp_block, int64_t *pi_ref1, int64_t
         }
 
         /* do parsing */
-        if( i_level == 1 )
+        switch ( i_level )
         {
+        case 1:
             if( MKV_IS_ID( el, KaxCluster ) )
             {
                 cluster = (KaxCluster*)el;
@@ -1616,9 +1631,8 @@ int matroska_segment_c::BlockGet( KaxBlock **pp_block, int64_t *pi_ref1, int64_t
             {
                 msg_Dbg( &sys.demuxer, "unknown (%s)", typeid( el ).name() );
             }
-        }
-        else if( i_level == 2 )
-        {
+            break;
+        case 2:
             if( MKV_IS_ID( el, KaxClusterTimecode ) )
             {
                 KaxClusterTimecode &ctc = *(KaxClusterTimecode*)el;
@@ -1634,9 +1648,8 @@ int matroska_segment_c::BlockGet( KaxBlock **pp_block, int64_t *pi_ref1, int64_t
             {
                 ep->Down();
             }
-        }
-        else if( i_level == 3 )
-        {
+            break;
+        case 3:
             if( MKV_IS_ID( el, KaxBlock ) )
             {
                 *pp_block = (KaxBlock*)el;
@@ -1681,9 +1694,8 @@ int matroska_segment_c::BlockGet( KaxBlock **pp_block, int64_t *pi_ref1, int64_t
                     }
                 }
             }
-        }
-        else
-        {
+            break;
+        default:
             msg_Err( &sys.demuxer, "invalid level = %d", i_level );
             return VLC_EGENERIC;
         }
@@ -1795,10 +1807,10 @@ static void BlockDecode( demux_t *p_demux, KaxBlock *block, mtime_t i_pts,
                 if ( p_sys->b_ui_hooked )
                 {
                     vlc_mutex_lock( &p_sys->p_ev->lock );
-                    if ( p_sys->p_pci_packet != NULL )
-                        block_Release( p_sys->p_pci_packet );
-                    p_sys->p_pci_packet = p_block;
+                    memcpy( &p_sys->pci_packet, &p_block->p_buffer[1], sizeof(pci_t) );
+                    p_sys->SwapButtons();
                     vlc_mutex_unlock( &p_sys->p_ev->lock );
+                    block_Release( p_block );
                 }
                 return;
             }
@@ -2249,6 +2261,7 @@ void demux_sys_t::StartUiThread()
         /* Now create our event thread catcher */
         p_ev = (event_thread_t *) vlc_object_create( &demuxer, sizeof( event_thread_t ) );
         p_ev->p_demux = &demuxer;
+        p_ev->b_die = VLC_FALSE;
         vlc_mutex_init( p_ev, &p_ev->lock );
         vlc_thread_create( p_ev, "mkv event thread handler", EventThread,
                         VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
@@ -2261,6 +2274,11 @@ void demux_sys_t::StopUiThread()
     {
         p_ev->b_die = VLC_TRUE;
 
+        vlc_thread_join( p_ev );
+        vlc_object_destroy( p_ev );
+
+        p_ev = NULL;
+
         var_Destroy( p_input, "highlight-mutex" );
         var_Destroy( p_input, "highlight" );
         var_Destroy( p_input, "x-start" );
@@ -2270,9 +2288,6 @@ void demux_sys_t::StopUiThread()
         var_Destroy( p_input, "color" );
         var_Destroy( p_input, "menu-contrast" );
 
-        vlc_thread_join( p_ev );
-        vlc_object_destroy( p_ev );
-
         msg_Dbg( &demuxer, "Stopping the UI Hook" );
     }
     b_ui_hooked = false;
@@ -2330,7 +2345,7 @@ int demux_sys_t::EventThread( vlc_object_t *p_this )
 
             vlc_mutex_lock( &p_ev->lock );
 
-            pci_t *pci = (pci_t *) &p_sys->p_pci_packet->p_buffer[1];
+            pci_t *pci = (pci_t *) &p_sys->pci_packet;
 
             var_Get( p_ev->p_vlc, "key-pressed", &valk );
             for( i = 0; p_hotkeys[i].psz_action != NULL; i++ )
@@ -2366,31 +2381,109 @@ int demux_sys_t::EventThread( vlc_object_t *p_this )
             vlc_mutex_unlock( &p_ev->lock );
         }
 
-        /* VOUT part */
+        /* MOUSE part */
         if( p_vout && ( p_ev->b_moved || p_ev->b_clicked ) )
         {
             vlc_value_t valx, valy;
 
             vlc_mutex_lock( &p_ev->lock );
-            pci_t *pci = (pci_t *) &p_sys->p_pci_packet->p_buffer[1];
+            pci_t *pci = (pci_t *) &p_sys->pci_packet;
             var_Get( p_vout, "mouse-x", &valx );
             var_Get( p_vout, "mouse-y", &valy );
 
-            if( p_ev->b_moved )
-            {
-//                dvdnav_mouse_select( NULL, pci, valx.i_int, valy.i_int );
-                p_ev->b_moved = 0;
-            }
             if( p_ev->b_clicked )
             {
+                int32_t button;
+                int32_t best,dist,d;
+                int32_t mx,my,dx,dy;
+
                 b_activated = VLC_TRUE;
-//                dvdnav_mouse_activate( NULL, pci, valx.i_int, valy.i_int );
+                // get current button
+                best = 0;
+                dist = 0x08000000; /* >> than  (720*720)+(567*567); */
+                for(button = 1; button <= pci->hli.hl_gi.btn_ns; button++) 
+                {
+                    btni_t *button_ptr = &(pci->hli.btnit[button-1]);
+
+                    if((valx.i_int >= button_ptr->x_start) && (valx.i_int <= button_ptr->x_end) &&
+                       (valy.i_int >= button_ptr->y_start) && (valy.i_int <= button_ptr->y_end)) 
+                    {
+                        mx = (button_ptr->x_start + button_ptr->x_end)/2;
+                        my = (button_ptr->y_start + button_ptr->y_end)/2;
+                        dx = mx - valx.i_int;
+                        dy = my - valy.i_int;
+                        d = (dx*dx) + (dy*dy);
+                        /* If the mouse is within the button and the mouse is closer
+                        * to the center of this button then it is the best choice. */
+                        if(d < dist) {
+                            dist = d;
+                            best = button;
+                        }
+                    }
+                }
+
+                if ( best != 0)
+                {
+                    btni_t button_ptr = pci->hli.btnit[best-1];
+
+                    msg_Dbg( &p_sys->demuxer, "Clicked button %d", best );
+                    vlc_mutex_unlock( &p_ev->lock );
+                    vlc_mutex_lock( &p_sys->lock_demuxer );
+
+                    // process the button action
+                    p_sys->dvd_interpretor.SetSPRM( 0x88, best );
+                    p_sys->dvd_interpretor.Interpret( button_ptr.cmd.bytes, 8 );
+
+                    // select new button
+                    if ( best != p_sys->i_curr_button )
+                    {
+                        vlc_value_t val;
+
+                        if( var_Get( p_sys->p_input, "highlight-mutex", &val ) == VLC_SUCCESS )
+                        {
+                            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;
+                            }
+                            p_sys->alpha[0] = i_palette      & 0x0f;
+                            p_sys->alpha[1] = (i_palette>>4) & 0x0f;
+                            p_sys->alpha[2] = (i_palette>>8) & 0x0f;
+                            p_sys->alpha[3] = (i_palette>>12)& 0x0f;
+
+                            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->alpha;
+                            var_Set( p_sys->p_input, "menu-contrast", val );
+
+                            val.b_bool = VLC_TRUE; var_Set( p_sys->p_input, "highlight", val );
+                            vlc_mutex_unlock( p_mutex );
+
+                            p_sys->i_curr_button = best;
+                        }
+                    }
+                    vlc_mutex_unlock( &p_sys->lock_demuxer );
+                    vlc_mutex_lock( &p_ev->lock );
+                }
+            }
+            else if( p_ev->b_moved )
+            {
+//                dvdnav_mouse_select( NULL, pci, valx.i_int, valy.i_int );
             }
 
             p_ev->b_moved = VLC_FALSE;
             p_ev->b_clicked = VLC_FALSE;
             vlc_mutex_unlock( &p_ev->lock );
         }
+
+        /* VOUT part */
         if( p_vout && p_vout->b_die )
         {
             var_DelCallback( p_vout, "mouse-moved", EventMouse, p_ev );
@@ -2398,7 +2491,8 @@ int demux_sys_t::EventThread( vlc_object_t *p_this )
             vlc_object_release( p_vout );
             p_vout = NULL;
         }
-        if( p_vout == NULL )
+
+        else if( p_vout == NULL )
         {
             p_vout = (vlc_object_t*) vlc_object_find( p_sys->p_input, VLC_OBJECT_VOUT,
                                       FIND_CHILD );
@@ -2409,7 +2503,7 @@ int demux_sys_t::EventThread( vlc_object_t *p_this )
             }
         }
 
-        /* Wait a bit */
+        /* Wait a bit, 10ms */
         msleep( 10000 );
     }
 
@@ -2552,6 +2646,15 @@ bool virtual_segment_c::UpdateCurrentToChapter( demux_t & demux )
 
             return true;
         }
+        else if (psz_curr_chapter == NULL)
+        {
+            // out of the scope of the data described by chapters, leave the edition
+            if ( (*p_editions)[i_current_edition]->b_ordered && psz_current_chapter != NULL )
+            {
+                if ( !(*p_editions)[i_current_edition]->EnterAndLeave( psz_current_chapter ) )
+                    psz_current_chapter = NULL;
+            }
+        }
     }
     return false;
 }
@@ -2799,45 +2902,51 @@ static int Demux( demux_t *p_demux)
 {
     demux_sys_t        *p_sys = p_demux->p_sys;
     virtual_segment_c  *p_vsegment = p_sys->p_current_segment;
-    matroska_segment_c *p_segmet = p_vsegment->Segment();
-    if ( p_segmet == NULL ) return 0;
+    matroska_segment_c *p_segment = p_vsegment->Segment();
+    if ( p_segment == NULL ) return 0;
     int                i_block_count = 0;
+    int                i_return = 0;
 
     KaxBlock *block;
     int64_t i_block_duration;
     int64_t i_block_ref1;
     int64_t i_block_ref2;
 
+    vlc_mutex_lock( &p_sys->lock_demuxer );
+
     for( ;; )
     {
         if ( p_sys->demuxer.b_die )
-            return 0;
+            break;
 
         if( p_sys->i_pts >= p_sys->i_start_pts  )
             if ( p_vsegment->UpdateCurrentToChapter( *p_demux ) )
-                return 1;
+            {
+                i_return = 1;
+                break;
+            }
         
         if ( p_vsegment->Edition() && p_vsegment->Edition()->b_ordered && p_vsegment->CurrentChapter() == NULL )
         {
             /* nothing left to read in this ordered edition */
             if ( !p_vsegment->SelectNext() )
-                return 0;
-            p_segmet->UnSelect( );
+                break;
+            p_segment->UnSelect( );
             
             es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
 
             /* switch to the next segment */
-            p_segmet = p_vsegment->Segment();
-            if ( !p_segmet->Select( 0 ) )
+            p_segment = p_vsegment->Segment();
+            if ( !p_segment->Select( 0 ) )
             {
                 msg_Err( p_demux, "Failed to select new segment" );
-                return 0;
+                break;
             }
             continue;
         }
 
 
-        if( p_segmet->BlockGet( &block, &i_block_ref1, &i_block_ref2, &i_block_duration ) )
+        if( p_segment->BlockGet( &block, &i_block_ref1, &i_block_ref2, &i_block_duration ) )
         {
             if ( p_vsegment->Edition() && p_vsegment->Edition()->b_ordered )
             {
@@ -2852,25 +2961,25 @@ static int Demux( demux_t *p_demux)
                     p_sys->i_pts = p_chap->i_user_end_time;
                     p_sys->i_pts++; // trick to avoid staying on segments with no duration and no content
 
-                    return 1;
+                    i_return = 1;
                 }
 
-                return 0;
+                break;
             }
             msg_Warn( p_demux, "cannot get block EOF?" );
-            p_segmet->UnSelect( );
+            p_segment->UnSelect( );
             
             es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
 
             /* switch to the next segment */
             if ( !p_vsegment->SelectNext() )
                 // no more segments in this stream
-                return 0;
-            p_segmet = p_vsegment->Segment();
-            if ( !p_segmet->Select( 0 ) )
+                break;
+            p_segment = p_vsegment->Segment();
+            if ( !p_segment->Select( 0 ) )
             {
                 msg_Err( p_demux, "Failed to select new segment" );
-                return 0;
+                break;
             }
 
             continue;
@@ -2891,9 +3000,14 @@ static int Demux( demux_t *p_demux)
         // TODO optimize when there is need to leave or when seeking has been called
         if( i_block_count > 5 )
         {
-            return 1;
+            i_return = 1;
+            break;
         }
     }
+
+    vlc_mutex_unlock( &p_sys->lock_demuxer );
+
+    return i_return;
 }
 
 
@@ -4562,6 +4676,8 @@ void demux_sys_t::PreloadLinked( matroska_segment_c *p_segment )
             titles.push_back( *p_title );
         }
     }
+
+    // TODO decide which segment should be first used (VMG for DVD)
 }
 
 bool demux_sys_t::IsUsedSegment( matroska_segment_c &segment ) const
@@ -5119,7 +5235,7 @@ bool dvd_chapter_codec_c::Enter()
             for ( ; i_size > 0; i_size--, p_data += 8 )
             {
                 msg_Dbg( &sys.demuxer, "Matroska DVD enter command" );
-                f_result |= interpretor.Interpret( p_data );
+                f_result |= sys.dvd_interpretor.Interpret( p_data );
             }
         }
         index++;
@@ -5142,7 +5258,7 @@ bool dvd_chapter_codec_c::Leave()
             for ( ; i_size > 0; i_size--, p_data += 8 )
             {
                 msg_Dbg( &sys.demuxer, "Matroska DVD leave command" );
-                f_result |= interpretor.Interpret( p_data );
+                f_result |= sys.dvd_interpretor.Interpret( p_data );
             }
         }
         index++;
@@ -5328,14 +5444,33 @@ bool dvd_command_interpretor_c::Interpret( const binary * p_command, size_t i_si
             uint16 i_pgcn = (p_command[6] << 8) + p_command[7];
             
             msg_Dbg( &sys.demuxer, "Link PGCN(%d)", i_pgcn );
-            // TODO
+            p_chapter = sys.p_current_segment->BrowseCodecPrivate( 1, MatchPgcNumber, &i_pgcn, 2 );
+            if ( p_chapter != NULL )
+            {
+                p_chapter->Enter( true );
+                
+                // jump to the location in the found segment
+                sys.p_current_segment->Seek( sys.demuxer, p_chapter->i_user_start_time, -1, p_chapter );
+                f_result = true;
+            }
             break;
         }
     case CMD_DVD_LINKCN:
         {
-            msg_Dbg( &sys.demuxer, "LinkCN (cell %d)", (p_command[6] << 8) + p_command[7] );
-            // TODO
-            f_result = true;
+            uint8 i_cn = p_command[7];
+            
+            p_chapter = sys.p_current_segment->CurrentChapter();
+
+            msg_Dbg( &sys.demuxer, "LinkCN (cell %d)", i_cn );
+            p_chapter = p_chapter->BrowseCodecPrivate( 1, MatchCellNumber, &i_cn, 1 );
+            if ( p_chapter != NULL )
+            {
+                p_chapter->Enter( true );
+                
+                // jump to the location in the found segment
+                sys.p_current_segment->Seek( sys.demuxer, p_chapter->i_user_start_time, -1, p_chapter );
+                f_result = true;
+            }
             break;
         }
     case CMD_DVD_GOTO_LINE:
@@ -5378,7 +5513,7 @@ bool dvd_command_interpretor_c::MatchTitleNumber( const chapter_codec_cmds_c &da
 
 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 )
+    if ( i_cookie_size != 1 || data.m_private_data.GetSize() < 8 )
         return false;
     
     if ( data.m_private_data.GetBuffer()[0] != MATROSKA_DVD_LEVEL_PGC )
@@ -5390,6 +5525,34 @@ bool dvd_command_interpretor_c::MatchPgcType( const chapter_codec_cmds_c &data,
     return (i_pgc_type == i_pgc);
 }
 
+bool dvd_command_interpretor_c::MatchPgcNumber( const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size )
+{
+    if ( i_cookie_size != 2 || data.m_private_data.GetSize() < 8 )
+        return false;
+    
+    if ( data.m_private_data.GetBuffer()[0] != MATROSKA_DVD_LEVEL_PGC )
+        return false;
+
+    uint16 *i_pgc_n = (uint16 *)p_cookie;
+    uint16 i_pgc_num = (data.m_private_data.GetBuffer()[1] << 8) + data.m_private_data.GetBuffer()[2];
+
+    return (i_pgc_num == *i_pgc_n);
+}
+
+bool dvd_command_interpretor_c::MatchCellNumber( 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() < 5 )
+        return false;
+    
+    if ( data.m_private_data.GetBuffer()[0] != MATROSKA_DVD_LEVEL_CN )
+        return false;
+
+    uint8 *i_cell_n = (uint8 *)p_cookie;
+    uint8 i_cell_num = data.m_private_data.GetBuffer()[3];
+
+    return (i_cell_num == *i_cell_n);
+}
+
 bool matroska_script_codec_c::Enter()
 {
     bool f_result = false;
@@ -5437,7 +5600,11 @@ bool matroska_script_interpretor_c::Interpret( const binary * p_command, size_t
 
     msg_Dbg( &sys.demuxer, "command : %s", sz_command.c_str() );
 
+#if defined(__GNUC__) && (__GNUC__ < 3)
+    if ( sz_command.compare( CMD_MS_GOTO_AND_PLAY, 0, CMD_MS_GOTO_AND_PLAY.size() ) == 0 )
+#else
     if ( sz_command.compare( 0, CMD_MS_GOTO_AND_PLAY.size(), CMD_MS_GOTO_AND_PLAY ) == 0 )
+#endif
     {
         size_t i,j;
 
@@ -5478,3 +5645,32 @@ bool matroska_script_interpretor_c::Interpret( const binary * p_command, size_t
 
     return b_result;
 }
+
+void demux_sys_t::SwapButtons()
+{
+#ifndef WORDS_BIGENDIAN
+    uint8_t button, i, j;
+
+    for( button = 1; button <= pci_packet.hli.hl_gi.btn_ns; button++) {
+        btni_t *button_ptr = &(pci_packet.hli.btnit[button-1]);
+        binary *p_data = (binary*) button_ptr;
+
+        uint16 i_x_start = ((p_data[0] & 0x3F) << 4 ) + ( p_data[1] >> 4 );
+        uint16 i_x_end   = ((p_data[1] & 0x03) << 8 ) + p_data[2];
+        uint16 i_y_start = ((p_data[3] & 0x3F) << 4 ) + ( p_data[4] >> 4 );
+        uint16 i_y_end   = ((p_data[4] & 0x03) << 8 ) + p_data[5];
+        button_ptr->x_start = i_x_start;
+        button_ptr->x_end   = i_x_end;
+        button_ptr->y_start = i_y_start;
+        button_ptr->y_end   = i_y_end;
+
+    }
+    for ( i = 0; i<3; i++ )
+    {
+        for ( j = 0; j<2; j++ )
+        {
+            pci_packet.hli.btn_colit.btn_coli[i][j] = U32_AT( &pci_packet.hli.btn_colit.btn_coli[i][j] );
+        }
+    }
+#endif
+}