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