1 
2 /*****************************************************************************
3  * mkv.cpp : matroska demuxer
4  *****************************************************************************
5  * Copyright (C) 2003-2004 VLC authors and VideoLAN
6  * $Id: 6fa2418dd2b8f0544ec0b00fe54b71fcbc03ac11 $
7  *
8  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9  *          Steve Lhomme <steve.lhomme@free.fr>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25 
26 #include "demux.hpp"
27 #include "stream_io_callback.hpp"
28 #include "Ebml_parser.hpp"
29 
30 #include <vlc_actions.h>
31 
event_thread_t(demux_t * p_demux)32 event_thread_t::event_thread_t(demux_t *p_demux) : p_demux(p_demux)
33 {
34     vlc_mutex_init( &lock );
35     vlc_cond_init( &wait );
36     is_running = false;
37 }
~event_thread_t()38 event_thread_t::~event_thread_t()
39 {
40     ResetPci();
41     vlc_cond_destroy( &wait );
42     vlc_mutex_destroy( &lock );
43 }
44 
SetPci(const pci_t * data)45 void event_thread_t::SetPci(const pci_t *data)
46 {
47     vlc_mutex_locker l(&lock);
48 
49     memcpy(&pci_packet, data, sizeof(pci_packet));
50 
51 #ifndef WORDS_BIGENDIAN
52     for( uint8_t button = 1; button <= pci_packet.hli.hl_gi.btn_ns &&
53          button < ARRAY_SIZE(pci_packet.hli.btnit); button++) {
54         btni_t *button_ptr = &(pci_packet.hli.btnit[button-1]);
55         binary *p_data = (binary*) button_ptr;
56 
57         uint16 i_x_start = ((p_data[0] & 0x3F) << 4 ) + ( p_data[1] >> 4 );
58         uint16 i_x_end   = ((p_data[1] & 0x03) << 8 ) + p_data[2];
59         uint16 i_y_start = ((p_data[3] & 0x3F) << 4 ) + ( p_data[4] >> 4 );
60         uint16 i_y_end   = ((p_data[4] & 0x03) << 8 ) + p_data[5];
61         button_ptr->x_start = i_x_start;
62         button_ptr->x_end   = i_x_end;
63         button_ptr->y_start = i_y_start;
64         button_ptr->y_end   = i_y_end;
65 
66     }
67     for ( uint8_t i = 0; i<3; i++ )
68         for ( uint8_t j = 0; j<2; j++ )
69             pci_packet.hli.btn_colit.btn_coli[i][j] = U32_AT( &pci_packet.hli.btn_colit.btn_coli[i][j] );
70 #endif
71     if( !is_running )
72     {
73         b_abort = false;
74         is_running = !vlc_clone( &thread, EventThread, this, VLC_THREAD_PRIORITY_LOW );
75     }
76 }
ResetPci()77 void event_thread_t::ResetPci()
78 {
79     if( !is_running )
80         return;
81 
82     vlc_mutex_lock( &lock );
83     b_abort = true;
84     vlc_cond_signal( &wait );
85     vlc_mutex_unlock( &lock );
86 
87     vlc_join( thread, NULL );
88     is_running = false;
89 }
EventMouse(vlc_object_t * p_this,char const * psz_var,vlc_value_t,vlc_value_t,void * p_data)90 int event_thread_t::EventMouse( vlc_object_t *p_this, char const *psz_var,
91                                 vlc_value_t, vlc_value_t, void *p_data )
92 {
93     event_thread_t *p_ev = (event_thread_t *) p_data;
94     vlc_mutex_lock( &p_ev->lock );
95     if( psz_var[6] == 'c' )
96     {
97         p_ev->b_clicked = true;
98         msg_Dbg( p_this, "Event Mouse: clicked");
99     }
100     else if( psz_var[6] == 'm' )
101         p_ev->b_moved = true;
102     vlc_cond_signal( &p_ev->wait );
103     vlc_mutex_unlock( &p_ev->lock );
104 
105     return VLC_SUCCESS;
106 }
107 
EventKey(vlc_object_t * p_this,char const *,vlc_value_t,vlc_value_t newval,void * p_data)108 int event_thread_t::EventKey( vlc_object_t *p_this, char const *,
109                               vlc_value_t, vlc_value_t newval, void *p_data )
110 {
111     event_thread_t *p_ev = (event_thread_t *) p_data;
112     vlc_mutex_lock( &p_ev->lock );
113     p_ev->i_key_action = newval.i_int;
114     vlc_cond_signal( &p_ev->wait );
115     vlc_mutex_unlock( &p_ev->lock );
116     msg_Dbg( p_this, "Event Key");
117 
118     return VLC_SUCCESS;
119 }
120 
EventInput(vlc_object_t * p_this,char const *,vlc_value_t,vlc_value_t newval,void * p_data)121 int event_thread_t::EventInput( vlc_object_t *p_this, char const *,
122                                 vlc_value_t, vlc_value_t newval, void *p_data )
123 {
124     VLC_UNUSED( p_this );
125     event_thread_t *p_ev = (event_thread_t *) p_data;
126     vlc_mutex_lock( &p_ev->lock );
127     if( newval.i_int == INPUT_EVENT_VOUT )
128     {
129         p_ev->b_vout |= true;
130         vlc_cond_signal( &p_ev->wait );
131     }
132     vlc_mutex_unlock( &p_ev->lock );
133 
134     return VLC_SUCCESS;
135 }
136 
EventThread()137 void event_thread_t::EventThread()
138 {
139     demux_sys_t    *p_sys = p_demux->p_sys;
140     vlc_object_t   *p_vout = NULL;
141     int canc = vlc_savecancel ();
142 
143     b_moved      = false;
144     b_clicked    = false;
145     i_key_action = 0;
146     b_vout       = true;
147 
148     /* catch all key event */
149     var_AddCallback( p_demux->obj.libvlc, "key-action", EventKey, this );
150     /* catch input event */
151     var_AddCallback( p_demux->p_input, "intf-event", EventInput, this );
152 
153     /* main loop */
154     for( ;; )
155     {
156         vlc_mutex_lock( &lock );
157         while( !b_abort && !i_key_action && !b_moved && !b_clicked && !b_vout)
158             vlc_cond_wait( &wait, &lock );
159 
160         if( b_abort )
161         {
162             vlc_mutex_unlock( &lock );
163             break;
164         }
165 
166         /* KEY part */
167         if( i_key_action )
168         {
169             msg_Dbg( p_demux, "Handle Key Event");
170 
171             pci_t *pci = &pci_packet;
172 
173             uint16 i_curr_button = p_sys->dvd_interpretor.GetSPRM( 0x88 );
174 
175             switch( i_key_action )
176             {
177             case ACTIONID_NAV_LEFT:
178                 if ( i_curr_button > 0 && i_curr_button <= pci->hli.hl_gi.btn_ns )
179                 {
180                     btni_t *p_button_ptr = &(pci->hli.btnit[i_curr_button-1]);
181                     if ( p_button_ptr->left > 0 && p_button_ptr->left <= pci->hli.hl_gi.btn_ns )
182                     {
183                         i_curr_button = p_button_ptr->left;
184                         p_sys->dvd_interpretor.SetSPRM( 0x88, i_curr_button );
185                         btni_t button_ptr = pci->hli.btnit[i_curr_button-1];
186                         if ( button_ptr.auto_action_mode )
187                         {
188                             vlc_mutex_unlock( &lock );
189                             vlc_mutex_lock( &p_sys->lock_demuxer );
190 
191                             // process the button action
192                             p_sys->dvd_interpretor.Interpret( button_ptr.cmd.bytes, 8 );
193 
194                             vlc_mutex_unlock( &p_sys->lock_demuxer );
195                             vlc_mutex_lock( &lock );
196                         }
197                     }
198                 }
199                 break;
200             case ACTIONID_NAV_RIGHT:
201                 if ( i_curr_button > 0 && i_curr_button <= pci->hli.hl_gi.btn_ns )
202                 {
203                     btni_t *p_button_ptr = &(pci->hli.btnit[i_curr_button-1]);
204                     if ( p_button_ptr->right > 0 && p_button_ptr->right <= pci->hli.hl_gi.btn_ns )
205                     {
206                         i_curr_button = p_button_ptr->right;
207                         p_sys->dvd_interpretor.SetSPRM( 0x88, i_curr_button );
208                         btni_t button_ptr = pci->hli.btnit[i_curr_button-1];
209                         if ( button_ptr.auto_action_mode )
210                         {
211                             vlc_mutex_unlock( &lock );
212                             vlc_mutex_lock( &p_sys->lock_demuxer );
213 
214                             // process the button action
215                             p_sys->dvd_interpretor.Interpret( button_ptr.cmd.bytes, 8 );
216 
217                             vlc_mutex_unlock( &p_sys->lock_demuxer );
218                             vlc_mutex_lock( &lock );
219                         }
220                     }
221                 }
222                 break;
223             case ACTIONID_NAV_UP:
224                 if ( i_curr_button > 0 && i_curr_button <= pci->hli.hl_gi.btn_ns )
225                 {
226                     btni_t *p_button_ptr = &(pci->hli.btnit[i_curr_button-1]);
227                     if ( p_button_ptr->up > 0 && p_button_ptr->up <= pci->hli.hl_gi.btn_ns )
228                     {
229                         i_curr_button = p_button_ptr->up;
230                         p_sys->dvd_interpretor.SetSPRM( 0x88, i_curr_button );
231                         btni_t button_ptr = pci->hli.btnit[i_curr_button-1];
232                         if ( button_ptr.auto_action_mode )
233                         {
234                             vlc_mutex_unlock( &lock );
235                             vlc_mutex_lock( &p_sys->lock_demuxer );
236 
237                             // process the button action
238                             p_sys->dvd_interpretor.Interpret( button_ptr.cmd.bytes, 8 );
239 
240                             vlc_mutex_unlock( &p_sys->lock_demuxer );
241                             vlc_mutex_lock( &lock );
242                         }
243                     }
244                 }
245                 break;
246             case ACTIONID_NAV_DOWN:
247                 if ( i_curr_button > 0 && i_curr_button <= pci->hli.hl_gi.btn_ns )
248                 {
249                     btni_t *p_button_ptr = &(pci->hli.btnit[i_curr_button-1]);
250                     if ( p_button_ptr->down > 0 && p_button_ptr->down <= pci->hli.hl_gi.btn_ns )
251                     {
252                         i_curr_button = p_button_ptr->down;
253                         p_sys->dvd_interpretor.SetSPRM( 0x88, i_curr_button );
254                         btni_t button_ptr = pci->hli.btnit[i_curr_button-1];
255                         if ( button_ptr.auto_action_mode )
256                         {
257                             vlc_mutex_unlock( &lock );
258                             vlc_mutex_lock( &p_sys->lock_demuxer );
259 
260                             // process the button action
261                             p_sys->dvd_interpretor.Interpret( button_ptr.cmd.bytes, 8 );
262 
263                             vlc_mutex_unlock( &p_sys->lock_demuxer );
264                             vlc_mutex_lock( &lock );
265                         }
266                     }
267                 }
268                 break;
269             case ACTIONID_NAV_ACTIVATE:
270                 if ( i_curr_button > 0 && i_curr_button <= pci->hli.hl_gi.btn_ns )
271                 {
272                     btni_t button_ptr = pci->hli.btnit[i_curr_button-1];
273 
274                     vlc_mutex_unlock( &lock );
275                     vlc_mutex_lock( &p_sys->lock_demuxer );
276 
277                     // process the button action
278                     p_sys->dvd_interpretor.Interpret( button_ptr.cmd.bytes, 8 );
279 
280                     vlc_mutex_unlock( &p_sys->lock_demuxer );
281                     vlc_mutex_lock( &lock );
282                 }
283                 break;
284             default:
285                 break;
286             }
287             i_key_action = 0;
288         }
289 
290         /* MOUSE part */
291         if( p_vout && ( b_moved || b_clicked ) )
292         {
293             int x, y;
294 
295             var_GetCoords( p_vout, "mouse-moved", &x, &y );
296             pci_t *pci = &pci_packet;
297 
298             if( b_clicked )
299             {
300                 int32_t button;
301                 int32_t best,dist,d;
302                 int32_t mx,my,dx,dy;
303 
304                 msg_Dbg( p_demux, "Handle Mouse Event: Mouse clicked x(%d)*y(%d)", x, y);
305 
306                 // get current button
307                 best = 0;
308                 dist = 0x08000000; /* >> than  (720*720)+(567*567); */
309                 for(button = 1; button <= pci->hli.hl_gi.btn_ns; button++)
310                 {
311                     btni_t *button_ptr = &(pci->hli.btnit[button-1]);
312 
313                     if(((unsigned)x >= button_ptr->x_start)
314                      && ((unsigned)x <= button_ptr->x_end)
315                      && ((unsigned)y >= button_ptr->y_start)
316                      && ((unsigned)y <= button_ptr->y_end))
317                     {
318                         mx = (button_ptr->x_start + button_ptr->x_end)/2;
319                         my = (button_ptr->y_start + button_ptr->y_end)/2;
320                         dx = mx - x;
321                         dy = my - y;
322                         d = (dx*dx) + (dy*dy);
323                         /* If the mouse is within the button and the mouse is closer
324                         * to the center of this button then it is the best choice. */
325                         if(d < dist) {
326                             dist = d;
327                             best = button;
328                         }
329                     }
330                 }
331 
332                 if ( best != 0)
333                 {
334                     btni_t button_ptr = pci->hli.btnit[best-1];
335                     uint16 i_curr_button = p_sys->dvd_interpretor.GetSPRM( 0x88 );
336 
337                     msg_Dbg( &p_sys->demuxer, "Clicked button %d", best );
338                     vlc_mutex_unlock( &lock );
339                     vlc_mutex_lock( &p_sys->lock_demuxer );
340 
341                     // process the button action
342                     p_sys->dvd_interpretor.SetSPRM( 0x88, best );
343                     p_sys->dvd_interpretor.Interpret( button_ptr.cmd.bytes, 8 );
344 
345                     msg_Dbg( &p_sys->demuxer, "Processed button %d", best );
346 
347                     // select new button
348                     if ( best != i_curr_button )
349                     {
350                         uint32_t i_palette;
351 
352                         if(button_ptr.btn_coln != 0) {
353                             i_palette = pci->hli.btn_colit.btn_coli[button_ptr.btn_coln-1][1];
354                         } else {
355                             i_palette = 0;
356                         }
357 
358                         for( int i = 0; i < 4; i++ )
359                         {
360                             uint32_t i_yuv = 0xFF;//p_sys->clut[(hl.palette>>(16+i*4))&0x0f];
361                             uint8_t i_alpha = (i_palette>>(i*4))&0x0f;
362                             i_alpha = i_alpha == 0xf ? 0xff : i_alpha << 4;
363 
364                             p_sys->palette[i][0] = (i_yuv >> 16) & 0xff;
365                             p_sys->palette[i][1] = (i_yuv >> 0) & 0xff;
366                             p_sys->palette[i][2] = (i_yuv >> 8) & 0xff;
367                             p_sys->palette[i][3] = i_alpha;
368                         }
369 
370                         vlc_global_lock( VLC_HIGHLIGHT_MUTEX );
371                         var_SetInteger( p_demux->p_input, "x-start",
372                                         button_ptr.x_start );
373                         var_SetInteger( p_demux->p_input, "x-end",
374                                         button_ptr.x_end );
375                         var_SetInteger( p_demux->p_input, "y-start",
376                                         button_ptr.y_start );
377                         var_SetInteger( p_demux->p_input, "y-end",
378                                         button_ptr.y_end );
379                         var_SetAddress( p_demux->p_input, "menu-palette",
380                                         p_sys->palette );
381                         var_SetBool( p_demux->p_input, "highlight", true );
382                         vlc_global_unlock( VLC_HIGHLIGHT_MUTEX );
383                     }
384                     vlc_mutex_unlock( &p_sys->lock_demuxer );
385                     vlc_mutex_lock( &lock );
386                 }
387             }
388             else if( b_moved )
389             {
390 //                dvdnav_mouse_select( NULL, pci, x, y );
391             }
392 
393             b_moved = false;
394             b_clicked = false;
395         }
396 
397         b_vout = false;
398         vlc_mutex_unlock( &lock );
399 
400         /* Always check vout */
401         if( p_vout == NULL )
402         {
403             p_vout = (vlc_object_t*) input_GetVout(p_demux->p_input);
404             if( p_vout)
405             {
406                 var_AddCallback( p_vout, "mouse-moved", EventMouse, this );
407                 var_AddCallback( p_vout, "mouse-clicked", EventMouse, this );
408             }
409         }
410     }
411 
412     /* Release callback */
413     if( p_vout )
414     {
415         var_DelCallback( p_vout, "mouse-moved", EventMouse, this );
416         var_DelCallback( p_vout, "mouse-clicked", EventMouse, this );
417         vlc_object_release( p_vout );
418     }
419     var_DelCallback( p_demux->p_input, "intf-event", EventInput, this );
420     var_DelCallback( p_demux->obj.libvlc, "key-action", EventKey, this );
421 
422     vlc_restorecancel (canc);
423 }
424 
EventThread(void * data)425 void *event_thread_t::EventThread(void *data)
426 {
427     static_cast<event_thread_t*>(data)->EventThread();
428     return NULL;
429 }
430 
431 
~demux_sys_t()432 demux_sys_t::~demux_sys_t()
433 {
434     CleanUi();
435     size_t i;
436     for ( i=0; i<streams.size(); i++ )
437         delete streams[i];
438     for ( i=0; i<opened_segments.size(); i++ )
439         delete opened_segments[i];
440     for ( i=0; i<used_vsegments.size(); i++ )
441         delete used_vsegments[i];
442     for ( i=0; i<stored_attachments.size(); i++ )
443         delete stored_attachments[i];
444     if( meta ) vlc_meta_Delete( meta );
445 
446     while( titles.size() )
447     { vlc_input_title_Delete( titles.back() ); titles.pop_back();}
448 
449     vlc_mutex_destroy( &lock_demuxer );
450 }
451 
452 
AnalyseAllSegmentsFound(demux_t * p_demux,matroska_stream_c * p_stream1,bool b_initial)453 bool demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, matroska_stream_c *p_stream1, bool b_initial )
454 {
455     int i_upper_lvl = 0;
456     EbmlElement *p_l0;
457     bool b_keep_stream = false, b_keep_segment = false;
458 
459     /* verify the EBML Header... it shouldn't be bigger than 1kB */
460     p_l0 = p_stream1->estream.FindNextID(EBML_INFO(EbmlHead), 1024);
461     if (p_l0 == NULL)
462     {
463         msg_Err( p_demux, "No EBML header found" );
464         return false;
465     }
466 
467     /* verify we can read this Segment */
468     try
469     {
470         p_l0->Read( p_stream1->estream, EBML_CLASS_CONTEXT(EbmlHead), i_upper_lvl, p_l0, true);
471     }
472     catch(...)
473     {
474         msg_Err(p_demux, "EBML Header Read failed");
475         return false;
476     }
477 
478     EDocType doc_type = GetChild<EDocType>(*static_cast<EbmlHead*>(p_l0));
479     if (std::string(doc_type) != "matroska" && std::string(doc_type) != "webm" )
480     {
481         msg_Err( p_demux, "Not a Matroska file : DocType = %s ", std::string(doc_type).c_str());
482         return false;
483     }
484 
485     EDocTypeReadVersion doc_read_version = GetChild<EDocTypeReadVersion>(*static_cast<EbmlHead*>(p_l0));
486     if (uint64(doc_read_version) > 2)
487     {
488         msg_Err( p_demux, "matroska file needs version %" PRId64 " but only versions 1 & 2 supported", uint64(doc_read_version));
489         return false;
490     }
491 
492     delete p_l0;
493 
494 
495     // find all segments in this file
496     p_l0 = p_stream1->estream.FindNextID(EBML_INFO(KaxSegment), UINT64_MAX);
497     if (p_l0 == NULL)
498     {
499         msg_Err( p_demux, "No segment found" );
500         return false;
501     }
502 
503     while (p_l0 != 0)
504     {
505         bool b_l0_handled = false;
506 
507         if ( MKV_IS_ID( p_l0, KaxSegment) )
508         {
509             matroska_segment_c *p_segment1 = new matroska_segment_c( *this, p_stream1->estream, (KaxSegment*)p_l0 );
510 
511             p_segment1->Preload();
512 
513             if ( !p_segment1->p_segment_uid ||
514                  (b_keep_segment = (FindSegment( *p_segment1->p_segment_uid ) == NULL)))
515             {
516                 opened_segments.push_back( p_segment1 );
517                 b_keep_stream = true;
518                 p_stream1->segments.push_back( p_segment1 );
519             }
520             else
521             {
522                 p_segment1->segment = NULL;
523                 delete p_segment1;
524             }
525 
526             b_l0_handled = true;
527         }
528 
529         EbmlElement* p_l0_prev = p_l0;
530 
531         bool b_seekable;
532         vlc_stream_Control( demuxer.s, STREAM_CAN_SEEK, &b_seekable );
533 
534         if (p_l0->IsFiniteSize() && b_seekable )
535         {
536             p_l0->SkipData(p_stream1->estream, KaxMatroska_Context);
537             p_l0 = p_stream1->estream.FindNextID(EBML_INFO(KaxSegment), UINT64_MAX);
538         }
539         else
540         {
541             p_l0 = NULL;
542         }
543 
544         if( b_l0_handled == false )
545             delete p_l0_prev;
546     }
547 
548     if ( !b_keep_stream )
549         return false;
550 
551     return true;
552 }
553 
InitUi()554 void demux_sys_t::InitUi()
555 {
556     msg_Dbg( &demuxer, "Starting the UI Hook" );
557 
558     /* FIXME hack hack hack hack FIXME */
559     /* Get p_input and create variable */
560     p_input = demuxer.p_input;
561     if( p_input )
562     {
563         var_Create( p_input, "x-start", VLC_VAR_INTEGER );
564         var_Create( p_input, "y-start", VLC_VAR_INTEGER );
565         var_Create( p_input, "x-end", VLC_VAR_INTEGER );
566         var_Create( p_input, "y-end", VLC_VAR_INTEGER );
567         var_Create( p_input, "color", VLC_VAR_ADDRESS );
568         var_Create( p_input, "menu-palette", VLC_VAR_ADDRESS );
569         var_Create( p_input, "highlight", VLC_VAR_BOOL );
570     }
571 
572     /* Now create our event thread catcher */
573     p_ev = new event_thread_t(&demuxer);
574 }
575 
CleanUi()576 void demux_sys_t::CleanUi()
577 {
578     delete p_ev;
579     p_ev = NULL;
580 
581     if( p_input )
582     {
583         var_Destroy( p_input, "highlight" );
584         var_Destroy( p_input, "x-start" );
585         var_Destroy( p_input, "x-end" );
586         var_Destroy( p_input, "y-start" );
587         var_Destroy( p_input, "y-end" );
588         var_Destroy( p_input, "color" );
589         var_Destroy( p_input, "menu-palette" );
590     }
591 
592     msg_Dbg( &demuxer, "Stopping the UI Hook" );
593 }
594 
PreloadFamily(const matroska_segment_c & of_segment)595 void demux_sys_t::PreloadFamily( const matroska_segment_c & of_segment )
596 {
597     for (size_t i=0; i<opened_segments.size(); i++)
598     {
599         opened_segments[i]->PreloadFamily( of_segment );
600     }
601 }
602 
603 // preload all the linked segments for all preloaded segments
PreloadLinked()604 bool demux_sys_t::PreloadLinked()
605 {
606     size_t i, j, ij = 0;
607     virtual_segment_c *p_vseg;
608 
609     if ( unlikely(opened_segments.size() == 0) )
610         return false;
611 
612     p_current_vsegment = new (std::nothrow) virtual_segment_c( *(opened_segments[0]), opened_segments );
613     if ( !p_current_vsegment )
614         return false;
615 
616     if ( unlikely(p_current_vsegment->CurrentEdition() == NULL) )
617         return false;
618 
619     /* Set current chapter */
620     p_current_vsegment->p_current_vchapter = p_current_vsegment->CurrentEdition()->getChapterbyTimecode(0);
621     msg_Dbg( &demuxer, "NEW START CHAPTER uid=%" PRId64, p_current_vsegment->p_current_vchapter && p_current_vsegment->p_current_vchapter->p_chapter ?
622                  p_current_vsegment->p_current_vchapter->p_chapter->i_uid : 0 );
623 
624     used_vsegments.push_back( p_current_vsegment );
625 
626     for ( i=1; i< opened_segments.size(); i++ )
627     {
628         /* add segments from the same family to used_segments */
629         if ( opened_segments[0]->SameFamily( *(opened_segments[i]) ) )
630         {
631             virtual_segment_c *p_vsegment = new (std::nothrow) virtual_segment_c( *(opened_segments[i]), opened_segments );
632             if ( likely(p_vsegment != NULL) )
633                 used_vsegments.push_back( p_vsegment );
634         }
635     }
636 
637     // publish all editions of all usable segment
638     for ( i=0; i< used_vsegments.size(); i++ )
639     {
640         p_vseg = used_vsegments[i];
641         if ( p_vseg->Editions() != NULL )
642         {
643             for ( j=0; j<p_vseg->Editions()->size(); j++ )
644             {
645                 virtual_edition_c * p_ved = (*p_vseg->Editions())[j];
646                 input_title_t *p_title = vlc_input_title_New();
647                 int i_chapters;
648 
649                 // TODO use a name for each edition, let the TITLE deal with a codec name
650                 if ( p_title->psz_name == NULL )
651                 {
652                     if( p_ved->GetMainName().length() )
653                         p_title->psz_name = strdup( p_ved->GetMainName().c_str() );
654                     else
655                     {
656                         /* Check in tags if the edition has a name */
657 
658                         /* We use only the tags of the first segment as it contains the edition */
659                         matroska_segment_c::tags_t const& tags = opened_segments[0]->tags;
660                         uint64_t i_ed_uid = 0;
661                         if( p_ved->p_edition )
662                             i_ed_uid = (uint64_t) p_ved->p_edition->i_uid;
663 
664                         for( size_t k = 0; k < tags.size(); k++ )
665                         {
666                             if( tags[k].i_tag_type == EDITION_UID && tags[k].i_uid == i_ed_uid )
667                                 for( size_t l = 0; l < tags[k].simple_tags.size(); l++ )
668                                 {
669                                     SimpleTag const& st = tags[k].simple_tags[l];
670                                     if ( st.tag_name == "TITLE" )
671                                     {
672                                         msg_Dbg( &demuxer, "Using title \"%s\" from tag for edition %" PRIu64, st.value.c_str (), i_ed_uid );
673                                         p_title->psz_name = strdup( st.value.c_str () );
674                                         break;
675                                     }
676                                 }
677                         }
678 
679                         if( !p_title->psz_name &&
680                             asprintf(&(p_title->psz_name), "%s %d", "Segment", (int)ij) == -1 )
681                             p_title->psz_name = NULL;
682                     }
683                 }
684 
685                 ij++;
686                 i_chapters = 0;
687                 p_ved->PublishChapters( *p_title, i_chapters, 0 );
688 
689                 // Input duration into i_length
690                 p_title->i_length = p_ved->i_duration;
691 
692                 titles.push_back( p_title );
693             }
694         }
695         p_vseg->i_sys_title = p_vseg->i_current_edition;
696     }
697 
698     // TODO decide which segment should be first used (VMG for DVD)
699 
700     return true;
701 }
702 
FreeUnused()703 bool demux_sys_t::FreeUnused()
704 {
705     auto sIt = std::remove_if(begin(streams), end(streams), [](const matroska_stream_c* p_s) {
706         return !p_s->isUsed();
707     });
708     for (auto it = sIt; it != end(streams); ++it)
709         delete *it;
710     streams.erase(sIt, end(streams));
711 
712     auto sgIt = std::remove_if(begin(opened_segments), end(opened_segments),
713                 [](const matroska_segment_c* p_sg) {
714         return !p_sg->b_preloaded;
715     });
716     for (auto it = sgIt; it != end(opened_segments); ++it)
717         delete *it;
718     opened_segments.erase(sgIt, end(opened_segments));
719 
720     return !streams.empty() && !opened_segments.empty();
721 }
722 
PreparePlayback(virtual_segment_c & new_vsegment,mtime_t i_mk_date)723 bool demux_sys_t::PreparePlayback( virtual_segment_c & new_vsegment, mtime_t i_mk_date )
724 {
725     if ( p_current_vsegment != &new_vsegment )
726     {
727         if ( p_current_vsegment->CurrentSegment() != NULL )
728             p_current_vsegment->CurrentSegment()->ESDestroy();
729 
730         p_current_vsegment = &new_vsegment;
731         p_current_vsegment->CurrentSegment()->ESCreate();
732         i_current_title = p_current_vsegment->i_sys_title;
733     }
734     if( !p_current_vsegment->CurrentSegment() )
735         return false;
736     if( !p_current_vsegment->CurrentSegment()->b_cues )
737         msg_Warn( &p_current_vsegment->CurrentSegment()->sys.demuxer, "no cues/empty cues found->seek won't be precise" );
738 
739     f_duration = p_current_vsegment->Duration();
740 
741     /* add information */
742     p_current_vsegment->CurrentSegment()->InformationCreate( );
743     p_current_vsegment->CurrentSegment()->ESCreate( );
744 
745     /* Seek to the beginning */
746     p_current_vsegment->Seek(p_current_vsegment->CurrentSegment()->sys.demuxer,
747                              i_mk_date, p_current_vsegment->p_current_vchapter );
748 
749     return true;
750 }
751 
JumpTo(virtual_segment_c & vsegment,virtual_chapter_c & vchapter)752 void demux_sys_t::JumpTo( virtual_segment_c & vsegment, virtual_chapter_c & vchapter )
753 {
754     if ( !vchapter.p_chapter || !vchapter.p_chapter->Enter( true ) )
755     {
756         // jump to the location in the found segment
757         vsegment.Seek( demuxer, vchapter.i_mk_virtual_start_time, &vchapter );
758     }
759 }
760 
FindSegment(const EbmlBinary & uid) const761 matroska_segment_c *demux_sys_t::FindSegment( const EbmlBinary & uid ) const
762 {
763     for (size_t i=0; i<opened_segments.size(); i++)
764     {
765         if ( opened_segments[i]->p_segment_uid && *opened_segments[i]->p_segment_uid == uid )
766             return opened_segments[i];
767     }
768     return NULL;
769 }
770 
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_vsegment_found)771 virtual_chapter_c *demux_sys_t::BrowseCodecPrivate( unsigned int codec_id,
772                                         bool (*match)(const chapter_codec_cmds_c &data, const void *p_cookie, size_t i_cookie_size ),
773                                         const void *p_cookie,
774                                         size_t i_cookie_size,
775                                         virtual_segment_c * &p_vsegment_found )
776 {
777     virtual_chapter_c *p_result = NULL;
778     for (size_t i=0; i<used_vsegments.size(); i++)
779     {
780         p_result = used_vsegments[i]->BrowseCodecPrivate( codec_id, match, p_cookie, i_cookie_size );
781         if ( p_result != NULL )
782         {
783             p_vsegment_found = used_vsegments[i];
784             break;
785         }
786     }
787     return p_result;
788 }
789 
FindChapter(int64_t i_find_uid,virtual_segment_c * & p_vsegment_found)790 virtual_chapter_c *demux_sys_t::FindChapter( int64_t i_find_uid, virtual_segment_c * & p_vsegment_found )
791 {
792     virtual_chapter_c *p_result = NULL;
793     for (size_t i=0; i<used_vsegments.size(); i++)
794     {
795         p_result = used_vsegments[i]->FindChapter( i_find_uid );
796         if ( p_result != NULL )
797         {
798             p_vsegment_found = used_vsegments[i];
799             break;
800         }
801     }
802     return p_result;
803 }
804 
805