Coverage Report

Created: 2025-07-23 07:11

/src/vlc/modules/demux/mkv/virtual_segment.cpp
Line
Count
Source (jump to first uncovered line)
1
/*****************************************************************************
2
 * virtual_segment.cpp : virtual segment implementation in the MKV demuxer
3
 *****************************************************************************
4
 * Copyright © 2003-2011 VideoLAN and VLC authors
5
 *
6
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
7
 *          Steve Lhomme <steve.lhomme@free.fr>
8
 *          Denis Charmet <typx@dinauz.org>
9
 *
10
 * This program is free software; you can redistribute it and/or modify it
11
 * under the terms of the GNU Lesser General Public License as published by
12
 * the Free Software Foundation; either version 2.1 of the License, or
13
 * (at your option) any later version.
14
 *
15
 * This program is distributed in the hope that it will be useful,
16
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
 * GNU Lesser General Public License for more details.
19
 *
20
 * You should have received a copy of the GNU Lesser General Public License
21
 * along with this program; if not, write to the Free Software Foundation,
22
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23
 *****************************************************************************/
24
25
#include "virtual_segment.hpp"
26
27
#include <new>
28
29
namespace mkv {
30
31
/* FIXME move this, it's demux_sys_t::SegmentIsOpened */
32
template<typename T>
33
matroska_segment_c * getSegmentbyUID( T * p_uid, const std::vector<matroska_segment_c*> & opened_segments )
34
0
{
35
0
    for( size_t i = 0; i < opened_segments.size(); i++ )
36
0
    {
37
0
        if( opened_segments[i]->p_segment_uid &&
38
0
            *p_uid == *(opened_segments[i]->p_segment_uid) )
39
0
            return opened_segments[i];
40
0
    }
41
0
    return NULL;
42
0
}
Unexecuted instantiation: mkv::matroska_segment_c* mkv::getSegmentbyUID<libmatroska::KaxChapterSegmentUID>(libmatroska::KaxChapterSegmentUID*, std::__1::vector<mkv::matroska_segment_c*, std::__1::allocator<mkv::matroska_segment_c*> > const&)
Unexecuted instantiation: mkv::matroska_segment_c* mkv::getSegmentbyUID<libmatroska::KaxPrevUID>(libmatroska::KaxPrevUID*, std::__1::vector<mkv::matroska_segment_c*, std::__1::allocator<mkv::matroska_segment_c*> > const&)
Unexecuted instantiation: mkv::matroska_segment_c* mkv::getSegmentbyUID<libmatroska::KaxNextUID>(libmatroska::KaxNextUID*, std::__1::vector<mkv::matroska_segment_c*, std::__1::allocator<mkv::matroska_segment_c*> > const&)
43
44
virtual_chapter_c * virtual_chapter_c::CreateVirtualChapter( chapter_item_c * p_chap,
45
                                                             matroska_segment_c & main_segment,
46
                                                             const std::vector<matroska_segment_c*> & opened_segments,
47
                                                             vlc_tick_t & usertime_offset, bool b_ordered)
48
17
{
49
17
    std::vector<virtual_chapter_c *> sub_chapters;
50
17
    if( !p_chap )
51
17
    {
52
        /* Dummy chapter use the whole segment */
53
17
        return new (std::nothrow) virtual_chapter_c( main_segment, NULL, 0, main_segment.i_duration, sub_chapters );
54
17
    }
55
56
0
    if( b_ordered && !p_chap->i_end_time )
57
0
    {
58
0
        msg_Warn( &main_segment.sys.demuxer,
59
0
                  "Missing end time in ordered chapter - ignoring chapter %s",
60
0
                  p_chap->str_name.c_str() );
61
0
        return NULL;
62
0
    }
63
64
0
    matroska_segment_c * p_segment = &main_segment;
65
0
    if( p_chap->p_segment_uid &&
66
0
       ( !( p_segment = getSegmentbyUID( p_chap->p_segment_uid, opened_segments ) ) || !b_ordered ) )
67
0
    {
68
0
        msg_Warn( &main_segment.sys.demuxer,
69
0
                  "Couldn't find segment 0x%x or not ordered... - ignoring chapter %s",
70
0
                  *( (uint32_t *) p_chap->p_segment_uid->GetBuffer() ),p_chap->str_name.c_str() );
71
0
        return NULL;
72
0
    }
73
74
0
    p_segment->Preload();
75
76
0
    vlc_tick_t start = ( b_ordered )? usertime_offset : p_chap->i_start_time;
77
0
    vlc_tick_t tmp = usertime_offset;
78
79
0
    for( size_t i = 0; i < p_chap->sub_chapters.size(); i++ )
80
0
    {
81
0
        virtual_chapter_c * p_vsubchap = CreateVirtualChapter( p_chap->sub_chapters[i], *p_segment, opened_segments, tmp, b_ordered );
82
83
0
        if( p_vsubchap )
84
0
            sub_chapters.push_back( p_vsubchap );
85
0
    }
86
0
    vlc_tick_t stop = ( b_ordered )?
87
0
            (((!p_chap->i_end_time ||
88
0
               (*p_chap->i_end_time - p_chap->i_start_time) < (tmp - usertime_offset) )) ? tmp :
89
0
             *p_chap->i_end_time - p_chap->i_start_time + usertime_offset )
90
0
            : (p_chap->i_end_time ? *p_chap->i_end_time : -1);
91
92
0
    virtual_chapter_c * p_vchap = new (std::nothrow) virtual_chapter_c( *p_segment, p_chap, start, stop, sub_chapters );
93
0
    if( !p_vchap )
94
0
    {
95
0
        for( size_t i = 0 ; i < sub_chapters.size(); i++ )
96
0
            delete sub_chapters[i];
97
0
        return NULL;
98
0
    }
99
100
0
    if ( p_chap->i_end_time && *p_chap->i_end_time >= p_chap->i_start_time )
101
0
        usertime_offset += *p_chap->i_end_time - p_chap->i_start_time;
102
0
    else
103
0
        usertime_offset = tmp;
104
105
0
    msg_Dbg( &main_segment.sys.demuxer,
106
0
             "Virtual chapter %s from %" PRId64 " to %" PRId64 " - " ,
107
0
             p_chap->str_name.c_str(), p_vchap->i_mk_virtual_start_time, p_vchap->i_mk_virtual_stop_time );
108
109
0
    return p_vchap;
110
0
}
111
112
virtual_chapter_c::~virtual_chapter_c()
113
17
{
114
17
    for( size_t i = 0 ; i < sub_vchapters.size(); i++ )
115
0
        delete sub_vchapters[i];
116
17
}
117
118
119
virtual_edition_c::virtual_edition_c( chapter_edition_c * p_edit, matroska_segment_c & main_segment,
120
                                      const std::vector<matroska_segment_c*> & opened_segments )
121
17
{
122
17
    bool b_fake_ordered = false;
123
17
    p_edition = p_edit;
124
17
    b_ordered = false;
125
126
17
    vlc_tick_t usertime_offset = 0; // microseconds
127
128
    /* ordered chapters */
129
17
    if( p_edition && p_edition->b_ordered )
130
0
    {
131
0
        b_ordered = true;
132
0
        for( size_t i = 0; i < p_edition->sub_chapters.size(); i++ )
133
0
        {
134
0
            virtual_chapter_c * p_vchap = virtual_chapter_c::CreateVirtualChapter( p_edition->sub_chapters[i],
135
0
                                                                                   main_segment, opened_segments,
136
0
                                                                                   usertime_offset, b_ordered );
137
0
            if( p_vchap )
138
0
                vchapters.push_back( p_vchap );
139
0
        }
140
0
        if( vchapters.size() )
141
0
            i_duration = vchapters[ vchapters.size() - 1 ]->i_mk_virtual_stop_time;
142
0
        else
143
0
            i_duration = 0; /* Empty ordered editions will be ignored */
144
0
    }
145
17
    else /* Not ordered or no edition at all */
146
17
    {
147
17
        matroska_segment_c * p_cur = &main_segment;
148
17
        virtual_chapter_c * p_vchap = NULL;
149
17
        vlc_tick_t tmp = 0;
150
151
        /* check for prev linked segments */
152
        /* FIXME to avoid infinite recursion we limit to 10 prev should be better as parameter */
153
17
        for( int limit = 0; limit < 10 && p_cur->p_prev_segment_uid ; limit++ )
154
0
        {
155
0
            matroska_segment_c * p_prev = NULL;
156
0
            if( ( p_prev = getSegmentbyUID( p_cur->p_prev_segment_uid, opened_segments ) ) )
157
0
            {
158
0
                tmp = 0;
159
0
                msg_Dbg( &main_segment.sys.demuxer, "Prev segment 0x%x found\n",
160
0
                         *(int32_t*)p_cur->p_prev_segment_uid->GetBuffer() );
161
162
0
                p_prev->Preload();
163
164
                /* Create virtual_chapter from the first edition if any */
165
0
                chapter_item_c * p_chap = ( p_prev->stored_editions.size() > 0 )? ((chapter_item_c *)p_prev->stored_editions[0]) : NULL;
166
167
0
                p_vchap = virtual_chapter_c::CreateVirtualChapter( p_chap, *p_prev, opened_segments, tmp, b_ordered );
168
169
0
                if( p_vchap )
170
0
                    vchapters.insert( vchapters.begin(), p_vchap );
171
172
0
                p_cur = p_prev;
173
0
                b_fake_ordered = true;
174
0
            }
175
0
            else /* segment not found */
176
0
                break;
177
0
        }
178
179
17
        tmp = 0;
180
181
        /* Append the main segment */
182
17
        p_vchap = virtual_chapter_c::CreateVirtualChapter( p_edit, main_segment,
183
17
                                                           opened_segments, tmp, b_ordered );
184
17
        if( p_vchap )
185
17
            vchapters.push_back( p_vchap );
186
187
        /* Append next linked segments */
188
17
        for( int limit = 0; limit < 10 && p_cur->p_next_segment_uid; limit++ )
189
0
        {
190
0
            matroska_segment_c * p_next = NULL;
191
0
            if( ( p_next = getSegmentbyUID( p_cur->p_next_segment_uid, opened_segments ) ) )
192
0
            {
193
0
                tmp = 0;
194
0
                msg_Dbg( &main_segment.sys.demuxer, "Next segment 0x%x found\n",
195
0
                         *(int32_t*) p_cur->p_next_segment_uid->GetBuffer() );
196
197
0
                p_next->Preload();
198
199
                /* Create virtual_chapter from the first edition if any */
200
0
                chapter_item_c * p_chap = ( p_next->stored_editions.size() > 0 )?( (chapter_item_c *)p_next->stored_editions[0] ) : NULL;
201
202
0
                 p_vchap = virtual_chapter_c::CreateVirtualChapter( p_chap, *p_next, opened_segments, tmp, b_ordered );
203
204
0
                if( p_vchap )
205
0
                    vchapters.push_back( p_vchap );
206
207
0
                p_cur = p_next;
208
0
                b_fake_ordered = true;
209
0
            }
210
0
            else /* segment not found */
211
0
                break;
212
0
        }
213
214
        /* Retime chapters */
215
17
        retimeChapters();
216
17
        if(b_fake_ordered)
217
0
            b_ordered = true;
218
17
    }
219
220
#ifdef MKV_DEBUG
221
    msg_Dbg( &main_segment.sys.demuxer, "-- RECAP-BEGIN --" );
222
    print();
223
    msg_Dbg( &main_segment.sys.demuxer, "-- RECAP-END --" );
224
#endif
225
17
}
226
227
virtual_edition_c::~virtual_edition_c()
228
17
{
229
34
    for( size_t i = 0; i < vchapters.size(); i++ )
230
17
        delete vchapters[i];
231
17
}
232
233
void virtual_edition_c::retimeSubChapters( virtual_chapter_c * p_vchap )
234
17
{
235
17
    vlc_tick_t i_mk_stop_time = p_vchap->i_mk_virtual_stop_time;
236
17
    for( size_t i = p_vchap->sub_vchapters.size(); i-- > 0; )
237
0
    {
238
0
        virtual_chapter_c * p_vsubchap = p_vchap->sub_vchapters[i];
239
        //p_vsubchap->i_mk_virtual_start_time += p_vchap->i_mk_virtual_start_time;
240
241
        /*FIXME we artificially extend stop time if they were there before...*/
242
        /* Just for comfort*/
243
0
        p_vsubchap->i_mk_virtual_stop_time = i_mk_stop_time;
244
0
        i_mk_stop_time = p_vsubchap->i_mk_virtual_start_time;
245
246
0
        retimeSubChapters( p_vsubchap );
247
0
    }
248
17
}
249
250
void virtual_edition_c::retimeChapters()
251
17
{
252
    /* This function is just meant to be used on unordered chapters */
253
17
    if( b_ordered )
254
0
        return;
255
256
17
    i_duration = 0;
257
258
    /* On non ordered editions we have one top chapter == one segment */
259
34
    for( size_t i = 0; i < vchapters.size(); i++ )
260
17
    {
261
17
        virtual_chapter_c * p_vchap = vchapters[i];
262
263
17
        p_vchap->i_mk_virtual_start_time = i_duration;
264
17
        i_duration += p_vchap->segment.i_duration;
265
17
        p_vchap->i_mk_virtual_stop_time = i_duration;
266
267
17
        retimeSubChapters( p_vchap );
268
17
    }
269
17
}
270
271
virtual_segment_c::virtual_segment_c( matroska_segment_c & main_segment, std::vector<matroska_segment_c*> & p_opened_segments )
272
17
{
273
    /* Main segment */
274
17
    std::vector<chapter_edition_c*>::size_type i;
275
276
17
    i_current_edition = main_segment.i_default_edition;
277
278
17
    for( i = 0; i < main_segment.stored_editions.size(); i++ )
279
0
    {
280
        /* Create a virtual edition from opened */
281
0
        virtual_edition_c * p_vedition = new virtual_edition_c( main_segment.stored_editions[i], main_segment, p_opened_segments );
282
283
0
        bool b_has_translate = false;
284
0
        for (size_t j=0; i < p_vedition->vchapters.size(); i++)
285
0
        {
286
0
            if ( p_vedition->vchapters[j]->segment.translations.size() != 0 )
287
0
            {
288
0
                b_has_translate = true;
289
0
                break;
290
0
            }
291
0
        }
292
        /* Ordered empty edition can happen when all chapters are
293
         * on an other segment which couldn't be found... ignore it */
294
        /* OK if it has chapters and the translate codec in Matroska */
295
0
        if(p_vedition->b_ordered && p_vedition->i_duration == 0 && !b_has_translate)
296
0
        {
297
298
0
            msg_Warn( &main_segment.sys.demuxer,
299
0
                      "Edition %s (%zu) links to other segments not found and is empty... ignoring it",
300
0
                       p_vedition->GetMainName().c_str(), i );
301
0
            if(i_current_edition == i)
302
0
            {
303
0
                msg_Warn( &main_segment.sys.demuxer,
304
0
                          "Empty edition was the default... defaulting to 0");
305
0
                i_current_edition = 0;
306
0
            }
307
0
            delete p_vedition;
308
0
        }
309
0
        else
310
0
            veditions.push_back( p_vedition );
311
0
    }
312
    /*if we don't have edition create a dummy one*/
313
17
    if( !main_segment.stored_editions.size() )
314
17
    {
315
17
        virtual_edition_c * p_vedition = new virtual_edition_c( NULL, main_segment, p_opened_segments );
316
17
        veditions.push_back( p_vedition );
317
17
    }
318
319
    /* Get the default edition, if there is none, use the first one */
320
34
    for( i = 0; i < veditions.size(); i++)
321
17
    {
322
17
        if( veditions[i]->p_edition && veditions[i]->p_edition->b_default )
323
0
        {
324
0
            i_current_edition = i;
325
0
            break;
326
0
        }
327
17
    }
328
329
17
    if (CurrentEdition())
330
17
        p_current_vchapter = CurrentEdition()->getChapterbyTimecode(0);
331
17
}
332
333
virtual_segment_c::~virtual_segment_c()
334
17
{
335
34
    for( size_t i = 0; i < veditions.size(); i++ )
336
17
        delete veditions[i];
337
17
}
338
339
virtual_chapter_c *virtual_segment_c::BrowseCodecPrivate( chapter_codec_id codec_id,
340
                                                          chapter_cmd_match match )
341
0
{
342
0
    virtual_edition_c * p_ved = CurrentEdition();
343
0
    if( p_ved )
344
0
        return p_ved->BrowseCodecPrivate( codec_id, match );
345
346
0
    return NULL;
347
0
}
348
349
350
virtual_chapter_c * virtual_edition_c::BrowseCodecPrivate( chapter_codec_id codec_id,
351
                                                           chapter_cmd_match match )
352
0
{
353
0
    if( !p_edition )
354
0
        return NULL;
355
356
0
    for( size_t i = 0; i < vchapters.size(); i++ )
357
0
    {
358
0
        virtual_chapter_c * p_result = vchapters[i]->BrowseCodecPrivate( codec_id, match );
359
0
        if( p_result )
360
0
            return p_result;
361
0
    }
362
0
    return NULL;
363
0
}
364
365
366
367
virtual_chapter_c * virtual_chapter_c::BrowseCodecPrivate( chapter_codec_id codec_id,
368
                                                           chapter_cmd_match match )
369
0
{
370
0
    if( !p_chapter )
371
0
        return NULL;
372
373
0
    if( p_chapter->BrowseCodecPrivate( codec_id, match ) )
374
0
        return this;
375
376
0
    for( size_t i = 0; i < sub_vchapters.size(); i++ )
377
0
    {
378
0
        virtual_chapter_c * p_result = sub_vchapters[i]->BrowseCodecPrivate( codec_id, match );
379
0
        if( p_result )
380
0
            return p_result;
381
0
    }
382
0
    return NULL;
383
0
}
384
385
bool virtual_chapter_c::ContainsTimestamp( vlc_tick_t time )
386
1.52k
{
387
    /*with the current implementation only the last chapter can have a negative virtual_stop_time*/
388
1.52k
    return ( time >= i_mk_virtual_start_time && time < i_mk_virtual_stop_time );
389
1.52k
}
390
391
virtual_chapter_c* virtual_chapter_c::getSubChapterbyTimecode( vlc_tick_t time )
392
15
{
393
15
    for( size_t i = 0; i < sub_vchapters.size(); i++ )
394
0
    {
395
0
        if( sub_vchapters[i]->ContainsTimestamp( time ) )
396
0
            return sub_vchapters[i]->getSubChapterbyTimecode( time );
397
0
    }
398
399
15
    return this;
400
15
}
401
402
virtual_chapter_c* virtual_edition_c::getChapterbyTimecode( vlc_tick_t time )
403
287
{
404
559
    for( size_t i = 0; i < vchapters.size(); i++ )
405
287
    {
406
287
        if( vchapters[i]->ContainsTimestamp( time ) )
407
15
            return vchapters[i]->getSubChapterbyTimecode( time );
408
287
    }
409
410
272
    if( vchapters.size() )
411
272
    {
412
272
        virtual_chapter_c* last_chapter = vchapters.back();
413
414
272
        if( last_chapter->i_mk_virtual_start_time <= time &&
415
272
            last_chapter->i_mk_virtual_stop_time < 0 )
416
272
        {
417
272
            return last_chapter;
418
272
        }
419
272
    }
420
421
0
    return NULL;
422
272
}
423
424
bool virtual_segment_c::UpdateCurrentToChapter( demux_t & demux )
425
1.23k
{
426
1.23k
    demux_sys_t & sys = *(demux_sys_t *)demux.p_sys;
427
1.23k
    virtual_chapter_c *p_cur_vchapter = NULL;
428
1.23k
    virtual_edition_c *p_cur_vedition = CurrentEdition();
429
430
1.23k
    bool b_has_seeked = false;
431
432
1.23k
    if ( !b_current_vchapter_entered && p_current_vchapter != NULL )
433
13
    {
434
13
        b_current_vchapter_entered = true;
435
13
        if (p_current_vchapter->Enter( true ))
436
0
            return true;
437
13
    }
438
439
1.23k
    if ( sys.i_pts != VLC_TICK_INVALID )
440
1.23k
    {
441
1.23k
        if ( p_current_vchapter != NULL && p_current_vchapter->ContainsTimestamp( sys.i_pts - VLC_TICK_0 ))
442
964
            p_cur_vchapter = p_current_vchapter;
443
270
        else if (p_cur_vedition != NULL)
444
270
            p_cur_vchapter = p_cur_vedition->getChapterbyTimecode( sys.i_pts - VLC_TICK_0 );
445
1.23k
    }
446
447
    /* we have moved to a new chapter */
448
1.23k
    if ( p_cur_vchapter != NULL && p_current_vchapter != p_cur_vchapter )
449
0
    {
450
0
        msg_Dbg( &demux, "New Chapter %" PRId64 " uid=%" PRIu64, sys.i_pts - VLC_TICK_0,
451
0
                 p_cur_vchapter->p_chapter ? p_cur_vchapter->p_chapter->i_uid : 0 );
452
0
        if ( p_cur_vedition->b_ordered )
453
0
        {
454
            /* FIXME EnterAndLeave has probably been broken for a long time */
455
            // Leave/Enter up to the link point
456
0
            b_has_seeked = p_cur_vchapter->EnterAndLeave( p_current_vchapter );
457
0
            if ( !b_has_seeked )
458
0
            {
459
                // only physically seek if necessary
460
0
                if ( p_current_vchapter == NULL ||
461
0
                    ( p_current_vchapter && &p_current_vchapter->segment != &p_cur_vchapter->segment ) ||
462
0
                    ( p_current_vchapter->p_chapter->i_end_time != p_cur_vchapter->p_chapter->i_start_time ))
463
0
                {
464
                    /* Forcing reset pcr */
465
0
                    es_out_Control( demux.out, ES_OUT_RESET_PCR);
466
0
                    Seek( demux, p_cur_vchapter->i_mk_virtual_start_time, p_cur_vchapter );
467
0
                    return true;
468
0
                }
469
0
                sys.i_start_pts = p_cur_vchapter->i_mk_virtual_start_time + VLC_TICK_0;
470
0
                sys.i_mk_chapter_time = p_cur_vchapter->i_mk_virtual_start_time - p_cur_vchapter->segment.i_mk_start_time - ( ( p_cur_vchapter->p_chapter )? p_cur_vchapter->p_chapter->i_start_time : 0 ) /* + VLC_TICK_0 */;
471
0
            }
472
0
        }
473
474
0
        p_current_vchapter = p_cur_vchapter;
475
0
        if ( p_cur_vchapter->i_seekpoint_num > 0 )
476
0
        {
477
0
            sys.i_updates |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
478
0
            sys.i_current_title = i_sys_title;
479
0
            sys.i_current_seekpoint = p_cur_vchapter->i_seekpoint_num - 1;
480
0
        }
481
482
0
        return b_has_seeked;
483
0
    }
484
1.23k
    else if ( p_cur_vchapter == NULL && p_cur_vedition != NULL )
485
0
    {
486
        /* out of the scope of the data described by chapters, leave the edition */
487
0
        if ( p_cur_vedition->b_ordered && p_current_vchapter != NULL )
488
0
        {
489
0
            if ( !p_current_vchapter->Leave( ) )
490
0
            {
491
0
                p_current_vchapter->segment.ESDestroy();
492
0
                p_current_vchapter = NULL;
493
0
                b_current_vchapter_entered = false;
494
0
            }
495
0
            else
496
0
                return true;
497
0
        }
498
0
    }
499
1.23k
    return false;
500
1.23k
}
501
502
bool virtual_chapter_c::Leave( )
503
0
{
504
0
    if( !p_chapter )
505
0
        return false;
506
507
0
    return p_chapter->Leave( true );
508
0
}
509
510
bool virtual_chapter_c::EnterAndLeave( virtual_chapter_c *p_leaving_vchapter, bool b_enter )
511
0
{
512
0
    if( !p_chapter )
513
0
        return false;
514
515
0
    return p_chapter->EnterAndLeave( p_leaving_vchapter->p_chapter, b_enter );
516
0
}
517
518
bool virtual_segment_c::Seek( demux_t & demuxer, vlc_tick_t i_mk_date,
519
                              virtual_chapter_c *p_vchapter, bool b_precise )
520
17
{
521
17
    demux_sys_t *p_sys = (demux_sys_t *)demuxer.p_sys;
522
523
524
    /* find the actual time for an ordered edition */
525
17
    if ( p_vchapter == NULL && CurrentEdition() )
526
        /* 1st, we need to know in which chapter we are */
527
0
        p_vchapter = CurrentEdition()->getChapterbyTimecode( i_mk_date );
528
529
17
    if ( p_vchapter == NULL || !CurrentEdition() )
530
0
        return false;
531
532
17
    vlc_tick_t i_mk_time_offset = p_vchapter->i_mk_virtual_start_time - ( ( p_vchapter->p_chapter )? p_vchapter->p_chapter->i_start_time : 0 );
533
17
    if (CurrentEdition()->b_ordered)
534
0
        p_sys->i_mk_chapter_time = p_vchapter->i_mk_virtual_start_time - p_vchapter->segment.i_mk_start_time - ( ( p_vchapter->p_chapter )? p_vchapter->p_chapter->i_start_time : 0 ) /* + VLC_TICK_0 */;
535
17
    if ( p_vchapter->p_chapter && p_vchapter->i_seekpoint_num > 0 )
536
0
    {
537
0
        p_sys->i_updates |= INPUT_UPDATE_TITLE | INPUT_UPDATE_SEEKPOINT;
538
0
        p_sys->i_current_title = i_sys_title;
539
0
        p_sys->i_current_seekpoint = p_vchapter->i_seekpoint_num - 1;
540
0
    }
541
542
17
    if( p_current_vchapter == NULL || &p_current_vchapter->segment != &p_vchapter->segment )
543
0
    {
544
        // switch to a new Segment
545
0
        if ( p_current_vchapter )
546
0
        {
547
0
            KeepTrackSelection( p_current_vchapter->segment, p_vchapter->segment );
548
0
            p_current_vchapter->segment.ESDestroy();
549
0
        }
550
0
        msg_Dbg( &demuxer, "SWITCH CHAPTER uid=%" PRId64, p_vchapter->p_chapter ? p_vchapter->p_chapter->i_uid : 0 );
551
0
        p_current_vchapter = p_vchapter;
552
553
        /* only use for soft linking, hard linking should be continuous */
554
0
        es_out_Control( demuxer.out, ES_OUT_RESET_PCR );
555
556
0
        p_sys->PreparePlayback( *this );
557
0
    }
558
17
    else
559
17
    {
560
17
        p_current_vchapter = p_vchapter;
561
17
    }
562
563
17
    return p_current_vchapter->segment.Seek( demuxer, i_mk_date, i_mk_time_offset, b_precise );
564
17
}
565
566
virtual_chapter_c * virtual_chapter_c::FindChapter( chapter_uid i_find_uid )
567
0
{
568
0
    if( p_chapter && ( p_chapter->i_uid == i_find_uid ) )
569
0
        return this;
570
571
0
    for( size_t i = 0; i < sub_vchapters.size(); i++ )
572
0
    {
573
0
        virtual_chapter_c * p_res = sub_vchapters[i]->FindChapter( i_find_uid );
574
0
        if( p_res )
575
0
            return p_res;
576
0
    }
577
578
0
    return NULL;
579
0
}
580
581
virtual_chapter_c * virtual_segment_c::FindChapter( chapter_uid i_find_uid )
582
0
{
583
0
    virtual_edition_c * p_edition = CurrentEdition();
584
0
    if (unlikely(p_edition == NULL))
585
0
        return NULL;
586
587
0
    for( size_t i = 0; i < p_edition->vchapters.size(); i++ )
588
0
    {
589
0
        virtual_chapter_c * p_chapter = p_edition->vchapters[i]->FindChapter( i_find_uid );
590
0
        if( p_chapter )
591
0
            return p_chapter;
592
0
    }
593
0
    return NULL;
594
0
}
595
596
int virtual_chapter_c::PublishChapters( input_title_t & title, int & i_user_chapters, int i_level, bool allow_no_name )
597
17
{
598
17
    if ( p_chapter && p_chapter->b_display_seekpoint )
599
0
    {
600
0
        std::string chap_name;
601
0
        if ( p_chapter->b_user_display )
602
0
            chap_name = p_chapter->str_name;
603
0
        if (chap_name == "")
604
0
            chap_name = p_chapter->GetCodecName();
605
606
0
        if (allow_no_name || chap_name != "")
607
0
        {
608
0
            seekpoint_t *sk = vlc_seekpoint_New();
609
610
0
            if( unlikely( !sk ) )
611
0
                return 0;
612
613
0
            sk->i_time_offset = i_mk_virtual_start_time;
614
0
            if (chap_name != "")
615
0
                sk->psz_name = strdup( chap_name.c_str() );
616
617
            /* A start time of '0' is ok. A missing ChapterTime element is ok, too, because '0' is its default value. */
618
0
            title.i_seekpoint++;
619
0
            title.seekpoint = (seekpoint_t**)xrealloc( title.seekpoint,
620
0
              title.i_seekpoint * sizeof( seekpoint_t* ) );
621
0
            title.seekpoint[title.i_seekpoint-1] = sk;
622
623
0
            i_user_chapters++;
624
0
        }
625
0
    }
626
17
    i_seekpoint_num = i_user_chapters;
627
628
17
    for( size_t i = 0; i < sub_vchapters.size(); i++ )
629
0
        sub_vchapters[i]->PublishChapters( title, i_user_chapters, i_level + 1, true );
630
631
17
    return i_user_chapters;
632
17
}
633
634
635
int virtual_edition_c::PublishChapters( input_title_t & title, int & i_user_chapters, int i_level )
636
17
{
637
638
    /* HACK for now don't expose edition as a seekpoint if its start time is the same than it's first chapter */
639
17
    if( vchapters.size() > 0 &&
640
17
        vchapters[0]->i_mk_virtual_start_time && p_edition && !p_edition->b_hidden )
641
0
    {
642
0
        seekpoint_t *sk = vlc_seekpoint_New();
643
644
0
        if( unlikely( !sk ) )
645
0
            return 0;
646
647
0
        sk->i_time_offset = 0;
648
0
        sk->psz_name = strdup( p_edition->str_name.c_str() );
649
650
0
        title.i_seekpoint++;
651
0
        title.seekpoint = static_cast<seekpoint_t**>( xrealloc( title.seekpoint,
652
0
                             title.i_seekpoint * sizeof( seekpoint_t* ) ) );
653
0
        title.seekpoint[title.i_seekpoint - 1] = sk;
654
0
        i_level++;
655
656
0
        i_user_chapters++;
657
0
        i_seekpoint_num = i_user_chapters;
658
0
    }
659
660
//    if( chapters.size() > 1 )
661
34
        for( size_t i = 0; i < vchapters.size(); i++ )
662
17
            vchapters[i]->PublishChapters( title, i_user_chapters, i_level, false );
663
664
17
    return i_user_chapters;
665
17
}
666
667
std::string virtual_edition_c::GetMainName()
668
17
{
669
17
    if( p_edition )
670
0
        return p_edition->GetMainName();
671
672
17
    return std::string("");
673
17
}
674
675
bool virtual_chapter_c::Enter( bool b_do_subs )
676
13
{
677
13
    if( p_chapter )
678
0
        return p_chapter->Enter( b_do_subs );
679
13
    return false;
680
13
}
681
682
bool virtual_chapter_c::Leave( bool b_do_subs )
683
0
{
684
0
    if( p_chapter )
685
0
        return p_chapter->Leave( b_do_subs );
686
0
    return false;
687
0
}
688
689
#ifdef MKV_DEBUG
690
void virtual_chapter_c::print()
691
{
692
    msg_Dbg( &segment.sys.demuxer, "*** chapter %" PRId64 " - %" PRId64 " (%zu)",
693
             i_mk_virtual_start_time, i_mk_virtual_stop_time, sub_vchapters.size() );
694
    for( size_t i = 0; i < sub_vchapters.size(); i++ )
695
        sub_vchapters[i]->print();
696
}
697
#endif
698
699
void virtual_segment_c::KeepTrackSelection( const matroska_segment_c & old, const matroska_segment_c & next )
700
0
{
701
0
    char *sub_lang = NULL, *aud_lang = NULL;
702
0
    for( const auto & it : old.tracks )
703
0
    {
704
0
        const auto &track = it.second;
705
0
        if( track->p_es )
706
0
        {
707
0
            bool state = false;
708
0
            es_out_Control( old.sys.demuxer.out, ES_OUT_GET_ES_STATE, track->p_es, &state );
709
0
            if( state )
710
0
            {
711
0
                if( track->fmt.i_cat == AUDIO_ES )
712
0
                    aud_lang = track->fmt.psz_language;
713
0
                else if( track->fmt.i_cat == SPU_ES )
714
0
                    sub_lang = track->fmt.psz_language;
715
0
            }
716
0
        }
717
0
    }
718
0
    for( const auto & it : next.tracks )
719
0
    {
720
0
        const auto & new_track = it.second;
721
0
        es_format_t & new_fmt  = new_track->fmt;
722
723
        /* Let's only do that for audio and video for now */
724
0
        if( new_fmt.i_cat == AUDIO_ES || new_fmt.i_cat == VIDEO_ES )
725
0
        {
726
            /* check for a similar elementary stream */
727
0
            for( const auto & old_it : old.tracks )
728
0
            {
729
0
                const auto & old_track = old_it.second;
730
0
                es_format_t& old_fmt = old_track->fmt;
731
732
0
                if( !old_track->p_es )
733
0
                    continue;
734
735
0
                if( ( new_fmt.i_cat == old_fmt.i_cat ) &&
736
0
                    ( new_fmt.i_codec == old_fmt.i_codec ) &&
737
0
                    ( new_fmt.i_priority == old_fmt.i_priority ) &&
738
0
                    ( new_fmt.i_bitrate == old_fmt.i_bitrate ) &&
739
0
                    ( new_fmt.i_extra == old_fmt.i_extra ) &&
740
0
                    ( new_fmt.i_extra == 0 ||
741
0
                      !memcmp( new_fmt.p_extra, old_fmt.p_extra, new_fmt.i_extra ) ) &&
742
0
                    !strcasecmp( new_fmt.psz_language, old_fmt.psz_language ) &&
743
0
                    ( ( new_fmt.i_cat == AUDIO_ES &&
744
0
                        !memcmp( &new_fmt.audio, &old_fmt.audio, sizeof(audio_format_t) ) ) ||
745
0
                      ( new_fmt.i_cat == VIDEO_ES &&
746
0
                        !memcmp( &new_fmt.video, &old_fmt.video, sizeof(video_format_t) ) ) ) )
747
0
                {
748
                    /* FIXME handle video palettes... */
749
0
                    msg_Warn( &old.sys.demuxer, "Reusing decoder of old track %u for track %u", old_track->i_number, new_track->i_number);
750
0
                    new_track->p_es = old_track->p_es;
751
0
                    old_track->p_es = NULL;
752
0
                    break;
753
0
                }
754
0
            }
755
0
        }
756
0
        new_track->fmt.i_priority &= ~(0x10);
757
0
        if( ( sub_lang && new_fmt.i_cat == SPU_ES && !strcasecmp(sub_lang, new_fmt.psz_language) ) ||
758
0
            ( aud_lang && new_fmt.i_cat == AUDIO_ES && !strcasecmp(aud_lang, new_fmt.psz_language) ) )
759
0
        {
760
0
            msg_Warn( &old.sys.demuxer, "Since previous segment used lang %s forcing track %u",
761
0
                      new_fmt.psz_language, new_track->i_number );
762
0
            new_fmt.i_priority |= 0x10;
763
0
            new_track->b_forced = true;
764
0
        }
765
0
    }
766
0
}
767
768
} // namespace