/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 |