Coverage Report

Created: 2025-08-29 07:30

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