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