Coverage Report

Created: 2026-01-17 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/modules/demux/mkv/demux.cpp
Line
Count
Source
1
2
/*****************************************************************************
3
 * mkv.cpp : matroska demuxer
4
 *****************************************************************************
5
 * Copyright (C) 2003-2004 VLC authors and VideoLAN
6
 *
7
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8
 *          Steve Lhomme <steve.lhomme@free.fr>
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 "demux.hpp"
26
#include "stream_io_callback.hpp"
27
#include "Ebml_parser.hpp"
28
#include "virtual_segment.hpp"
29
30
namespace mkv {
31
32
demux_sys_t::~demux_sys_t()
33
7.49k
{
34
7.49k
    size_t i;
35
14.9k
    for ( i=0; i<streams.size(); i++ )
36
7.49k
        delete streams[i];
37
15.0k
    for ( i=0; i<opened_segments.size(); i++ )
38
7.52k
        delete opened_segments[i];
39
11.4k
    for ( i=0; i<used_vsegments.size(); i++ )
40
3.91k
        delete used_vsegments[i];
41
7.49k
    if( meta ) vlc_meta_Delete( meta );
42
43
12.6k
    while( titles.size() )
44
5.16k
    { vlc_input_title_Delete( titles.back() ); titles.pop_back();}
45
7.49k
}
46
47
48
bool demux_sys_t::AnalyseAllSegmentsFound( demux_t *p_demux, matroska_stream_c *p_stream1 )
49
7.49k
{
50
7.49k
    int i_upper_lvl = 0;
51
7.49k
    EbmlElement *p_l0;
52
7.49k
    bool b_keep_stream = false;
53
54
    /* verify the EBML Header... it shouldn't be bigger than 1kB */
55
7.49k
    p_l0 = p_stream1->estream.FindNextID(EBML_INFO(EbmlHead), 1024);
56
7.49k
    if (p_l0 == nullptr || p_l0->IsDummy())
57
1
    {
58
1
        msg_Err( p_demux, "No EBML header found" );
59
1
        delete p_l0;
60
1
        return false;
61
1
    }
62
63
    /* verify we can read this Segment */
64
7.49k
    try
65
7.49k
    {
66
7.49k
        EbmlElement *el = nullptr;
67
7.49k
        p_l0->Read( p_stream1->estream, EBML_CLASS_CONTEXT(EbmlHead), i_upper_lvl, el, true);
68
7.49k
        if (i_upper_lvl != 0)
69
3
        {
70
3
            assert(el != nullptr);
71
3
            delete el;
72
3
        }
73
7.49k
    }
74
7.49k
    catch(...)
75
7.49k
    {
76
0
        msg_Err(p_demux, "EBML Header Read failed");
77
0
        delete p_l0;
78
0
        return false;
79
0
    }
80
81
7.49k
    EDocType doc_type = GetChild<EDocType>(*static_cast<EbmlHead*>(p_l0));
82
7.49k
    if (std::string(doc_type) != "matroska" && std::string(doc_type) != "webm" )
83
51
    {
84
51
        msg_Err( p_demux, "Not a Matroska file : DocType = %s ", std::string(doc_type).c_str());
85
51
        delete p_l0;
86
51
        return false;
87
51
    }
88
89
7.44k
    EDocTypeReadVersion doc_read_version = GetChild<EDocTypeReadVersion>(*static_cast<EbmlHead*>(p_l0));
90
7.44k
    if (uint64_t(doc_read_version) > 5)
91
0
    {
92
0
        msg_Err( p_demux, "matroska file needs version %" PRId64 " but only versions 1 to 4 supported", uint64_t(doc_read_version));
93
0
        delete p_l0;
94
0
        return false;
95
0
    }
96
97
7.44k
    delete p_l0;
98
99
100
    // find all segments in this file
101
7.44k
    p_l0 = p_stream1->estream.FindNextID(EBML_INFO(KaxSegment), UINT64_MAX);
102
7.44k
    if (p_l0 == nullptr || p_l0->IsDummy())
103
48
    {
104
48
        msg_Err( p_demux, "No segment found" );
105
48
        delete p_l0;
106
48
        return false;
107
48
    }
108
109
14.9k
    while (p_l0 != nullptr)
110
7.52k
    {
111
7.52k
        bool b_l0_handled = false;
112
113
7.52k
        if ( MKV_IS_ID( p_l0, KaxSegment) )
114
7.52k
        {
115
7.52k
            matroska_segment_c *p_segment1 = new matroska_segment_c( *this, p_stream1->estream, (KaxSegment*)p_l0 );
116
117
7.52k
            p_segment1->Preload();
118
119
7.52k
            if ( !p_segment1->p_segment_uid ||
120
1.31k
                 !SegmentIsOpened( *p_segment1->p_segment_uid ) )
121
7.52k
            {
122
7.52k
                opened_segments.push_back( p_segment1 );
123
7.52k
                b_keep_stream = true;
124
7.52k
                p_stream1->segments.push_back( p_segment1 );
125
7.52k
            }
126
0
            else
127
0
            {
128
0
                p_segment1->segment = NULL;
129
0
                delete p_segment1;
130
0
            }
131
132
7.52k
            b_l0_handled = true;
133
7.52k
        }
134
135
7.52k
        if ( !b_seekable )
136
0
            break;
137
138
7.52k
        EbmlElement* p_l0_prev = p_l0;
139
140
7.52k
        if (p_l0->IsFiniteSize() )
141
7.20k
        {
142
7.20k
            p_l0->SkipData(p_stream1->estream, Context_KaxMatroska);
143
7.20k
            p_l0 = p_stream1->estream.FindNextID(EBML_INFO(KaxSegment), UINT64_MAX);
144
7.20k
            if (p_l0 != nullptr && p_l0->IsDummy())
145
72
            {
146
72
                delete p_l0;
147
72
                p_l0 = nullptr;
148
72
            }
149
7.20k
        }
150
320
        else
151
320
        {
152
320
            p_l0 = nullptr;
153
320
        }
154
155
7.52k
        if( b_l0_handled == false )
156
0
            delete p_l0_prev;
157
7.52k
    }
158
159
7.39k
    if ( !b_keep_stream )
160
0
        return false;
161
162
7.39k
    return true;
163
7.39k
}
164
165
void demux_sys_t::PreloadFamily( const matroska_segment_c & of_segment )
166
0
{
167
0
    for (size_t i=0; i<opened_segments.size(); i++)
168
0
    {
169
0
        opened_segments[i]->PreloadFamily( of_segment );
170
0
    }
171
0
}
172
173
// preload all the linked segments for all preloaded segments
174
bool demux_sys_t::PreloadLinked()
175
4.01k
{
176
4.01k
    size_t i, j, ij = 0;
177
4.01k
    virtual_segment_c *p_vseg;
178
179
4.01k
    if ( unlikely(opened_segments.size() == 0) )
180
0
        return false;
181
182
4.01k
    p_current_vsegment = new (std::nothrow) virtual_segment_c( *(opened_segments[0]), opened_segments );
183
4.01k
    if ( !p_current_vsegment )
184
0
        return false;
185
186
4.01k
    if ( unlikely(p_current_vsegment->CurrentEdition() == NULL) )
187
95
    {
188
95
        delete p_current_vsegment;
189
95
        return false;
190
95
    }
191
192
    /* Set current chapter */
193
3.91k
    msg_Dbg( &demuxer, "NEW START CHAPTER uid=%" PRId64, p_current_vsegment->CurrentChapter() && p_current_vsegment->CurrentChapter()->p_chapter ?
194
3.91k
                 p_current_vsegment->CurrentChapter()->p_chapter->i_uid : 0 );
195
196
3.91k
    used_vsegments.push_back( p_current_vsegment );
197
198
3.92k
    for ( i=1; i< opened_segments.size(); i++ )
199
4
    {
200
        /* add segments from the same family to used_segments */
201
4
        if ( opened_segments[0]->SameFamily( *(opened_segments[i]) ) )
202
0
        {
203
0
            virtual_segment_c *p_vsegment = new (std::nothrow) virtual_segment_c( *(opened_segments[i]), opened_segments );
204
0
            if ( likely(p_vsegment != NULL) )
205
0
                used_vsegments.push_back( p_vsegment );
206
0
        }
207
4
    }
208
209
    // publish all editions of all usable segment
210
7.83k
    for ( i=0; i< used_vsegments.size(); i++ )
211
3.91k
    {
212
3.91k
        p_vseg = used_vsegments[i];
213
3.91k
        if ( p_vseg->Editions() != NULL )
214
3.91k
        {
215
9.08k
            for ( j=0; j<p_vseg->Editions()->size(); j++ )
216
5.16k
            {
217
5.16k
                virtual_edition_c * p_ved = (*p_vseg->Editions())[j];
218
5.16k
                input_title_t *p_title = vlc_input_title_New();
219
5.16k
                int i_chapters;
220
221
                // TODO use a name for each edition, let the TITLE deal with a codec name
222
5.16k
                if ( p_title->psz_name == NULL )
223
5.16k
                {
224
5.16k
                    if( p_ved->GetMainName().length() )
225
0
                        p_title->psz_name = strdup( p_ved->GetMainName().c_str() );
226
5.16k
                    else
227
5.16k
                    {
228
                        /* Check in tags if the edition has a name */
229
230
                        /* We use only the tags of the first segment as it contains the edition */
231
5.16k
                        matroska_segment_c::tags_t const& tags = opened_segments[0]->tags;
232
5.16k
                        uint64_t i_ed_uid = 0;
233
5.16k
                        if( p_ved->p_edition )
234
2.69k
                            i_ed_uid = (uint64_t) p_ved->p_edition->i_uid;
235
236
6.79k
                        for( size_t k = 0; k < tags.size(); k++ )
237
1.62k
                        {
238
1.62k
                            if( tags[k].i_tag_type == EDITION_UID && tags[k].i_uid == i_ed_uid )
239
36
                                for( size_t l = 0; l < tags[k].simple_tags.size(); l++ )
240
25
                                {
241
25
                                    SimpleTag const& st = tags[k].simple_tags[l];
242
25
                                    if ( st.tag_name == "TITLE" )
243
11
                                    {
244
11
                                        msg_Dbg( &demuxer, "Using title \"%s\" from tag for edition %" PRIu64, st.value.c_str (), i_ed_uid );
245
11
                                        free(p_title->psz_name);
246
11
                                        p_title->psz_name = strdup( st.value.c_str () );
247
11
                                        break;
248
11
                                    }
249
25
                                }
250
1.62k
                        }
251
252
5.16k
                        if( !p_title->psz_name &&
253
5.15k
                            asprintf(&(p_title->psz_name), "%s %d", "Segment", (int)ij) == -1 )
254
0
                            p_title->psz_name = NULL;
255
5.16k
                    }
256
5.16k
                }
257
258
5.16k
                ij++;
259
5.16k
                i_chapters = 0;
260
5.16k
                p_ved->PublishChapters( *p_title, i_chapters, 0 );
261
262
                // Input duration into i_length
263
5.16k
                p_title->i_length = p_ved->i_duration;
264
265
5.16k
                titles.push_back( p_title );
266
5.16k
            }
267
3.91k
        }
268
3.91k
        p_vseg->i_sys_title = p_vseg->i_current_edition;
269
3.91k
    }
270
271
    // TODO decide which segment should be first used (VMG for DVD)
272
273
3.91k
    return true;
274
4.01k
}
275
276
bool demux_sys_t::FreeUnused()
277
3.73k
{
278
3.73k
    auto sIt = std::remove_if(begin(streams), end(streams), [](const matroska_stream_c* p_s) {
279
3.73k
        return !p_s->isUsed();
280
3.73k
    });
281
3.73k
    for (auto it = sIt; it != end(streams); ++it)
282
0
        delete *it;
283
3.73k
    streams.erase(sIt, end(streams));
284
285
3.73k
    auto sgIt = std::remove_if(begin(opened_segments), end(opened_segments),
286
3.74k
                [](const matroska_segment_c* p_sg) {
287
3.74k
        return !p_sg->b_preloaded;
288
3.74k
    });
289
3.73k
    for (auto it = sgIt; it != end(opened_segments); ++it)
290
0
        delete *it;
291
3.73k
    opened_segments.erase(sgIt, end(opened_segments));
292
293
3.73k
    return !streams.empty() && !opened_segments.empty();
294
3.73k
}
295
296
bool demux_sys_t::PreparePlayback( virtual_segment_c & new_vsegment )
297
3.91k
{
298
3.91k
    if ( !new_vsegment.CurrentSegment() )
299
181
        return false;
300
301
3.73k
    if ( p_current_vsegment != &new_vsegment )
302
0
    {
303
0
        if ( p_current_vsegment->CurrentSegment() != NULL )
304
0
            p_current_vsegment->CurrentSegment()->ESDestroy();
305
306
0
        p_current_vsegment = &new_vsegment;
307
0
        p_current_vsegment->CurrentSegment()->ESCreate();
308
0
        i_current_title = p_current_vsegment->i_sys_title;
309
0
    }
310
3.73k
    if( !p_current_vsegment->CurrentSegment()->b_cues )
311
3.73k
        msg_Warn( &p_current_vsegment->CurrentSegment()->sys.demuxer, "no cues/empty cues found->seek won't be precise" );
312
313
3.73k
    i_duration = p_current_vsegment->Duration();
314
315
    /* add information */
316
3.73k
    p_current_vsegment->CurrentSegment()->InformationCreate( );
317
3.73k
    p_current_vsegment->CurrentSegment()->ESCreate( );
318
319
3.73k
    return true;
320
3.91k
}
321
322
void demux_sys_t::JumpTo( virtual_segment_c & vsegment, virtual_chapter_c & vchapter )
323
3
{
324
3
    if ( !vchapter.p_chapter || !vchapter.p_chapter->Enter( true ) )
325
3
    {
326
        // jump to the location in the found segment
327
3
        vsegment.Seek( demuxer, vchapter.i_mk_virtual_start_time, &vchapter );
328
3
    }
329
3
}
330
331
bool demux_sys_t::SegmentIsOpened( const EbmlBinary & uid ) const
332
1.31k
{
333
1.31k
    for (size_t i=0; i<opened_segments.size(); i++)
334
2
    {
335
2
        if ( opened_segments[i]->p_segment_uid && *opened_segments[i]->p_segment_uid == uid )
336
0
            return true;
337
2
    }
338
1.31k
    return false;
339
1.31k
}
340
341
virtual_chapter_c *demux_sys_t::BrowseCodecPrivate( chapter_codec_id codec_id,
342
                                                    chapter_cmd_match match,
343
                                                    virtual_segment_c * &p_vsegment_found )
344
0
{
345
0
    virtual_chapter_c *p_result = NULL;
346
0
    for (size_t i=0; i<used_vsegments.size(); i++)
347
0
    {
348
0
        p_result = used_vsegments[i]->BrowseCodecPrivate( codec_id, match );
349
0
        if ( p_result != NULL )
350
0
        {
351
0
            p_vsegment_found = used_vsegments[i];
352
0
            break;
353
0
        }
354
0
    }
355
0
    return p_result;
356
0
}
357
358
virtual_chapter_c *demux_sys_t::FindVChapter( chapter_uid i_find_uid, virtual_segment_c * & p_vsegment_found )
359
13
{
360
13
    virtual_chapter_c *p_result = NULL;
361
23
    for (size_t i=0; i<used_vsegments.size(); i++)
362
13
    {
363
13
        p_result = used_vsegments[i]->FindChapter( i_find_uid );
364
13
        if ( p_result != NULL )
365
3
        {
366
3
            p_vsegment_found = used_vsegments[i];
367
3
            break;
368
3
        }
369
13
    }
370
13
    return p_result;
371
13
}
372
373
void demux_sys_t::SetHighlight( vlc_spu_highlight_t & spu_hl )
374
0
{
375
0
    ev.SetHighlight( spu_hl );
376
0
}
377
378
} // namespace