1 /*****************************************************************************
2 * matroska_segment.hpp : matroska demuxer
3 *****************************************************************************
4 * Copyright (C) 2016 VLC authors and VideoLAN
5 * $Id: d22c60f444a48838166b42c4d1649094e5197eaa $
6 *
7 * Authors: Filip Roséen <filip@videolabs.io>
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
23
24 #include "matroska_segment_seeker.hpp"
25 #include "matroska_segment.hpp"
26 #include "demux.hpp"
27 #include "Ebml_parser.hpp"
28 #include "Ebml_dispatcher.hpp"
29 #include "util.hpp"
30 #include "stream_io_callback.hpp"
31
32 #include <sstream>
33 #include <limits>
34
35 namespace {
36 template<class It, class T>
37 It greatest_lower_bound( It beg, It end, T const& value )
38 {
39 It it = std::upper_bound( beg, end, value );
40 if( it != beg ) --it;
41 return it;
42 }
43
44 // std::prev and std::next exists in C++11, in order to avoid ambiguity due
45 // to ADL and iterators being defined within namespace std, these two
46 // function-names have been postfixed with an underscore.
47
prev_(It it)48 template<class It> It prev_( It it ) { return --it; }
next_(It it)49 template<class It> It next_( It it ) { return ++it; }
50 }
51
52 SegmentSeeker::cluster_positions_t::iterator
add_cluster_position(fptr_t fpos)53 SegmentSeeker::add_cluster_position( fptr_t fpos )
54 {
55 cluster_positions_t::iterator insertion_point = std::upper_bound(
56 _cluster_positions.begin(),
57 _cluster_positions.end(),
58 fpos
59 );
60
61 return _cluster_positions.insert( insertion_point, fpos );
62 }
63
64 SegmentSeeker::cluster_map_t::iterator
add_cluster(KaxCluster * const p_cluster)65 SegmentSeeker::add_cluster( KaxCluster * const p_cluster )
66 {
67 Cluster cinfo = {
68 /* fpos */ p_cluster->GetElementPosition(),
69 /* pts */ mtime_t( p_cluster->GlobalTimecode() / INT64_C( 1000 ) ),
70 /* duration */ mtime_t( -1 ),
71 /* size */ p_cluster->IsFiniteSize()
72 ? p_cluster->GetEndPosition() - p_cluster->GetElementPosition()
73 : UINT64_MAX
74 };
75
76 add_cluster_position( cinfo.fpos );
77
78 cluster_map_t::iterator it = _clusters.lower_bound( cinfo.pts );
79
80 if( it != _clusters.end() && it->second.pts == cinfo.pts )
81 {
82 // cluster already known
83 }
84 else
85 {
86 it = _clusters.insert( cluster_map_t::value_type( cinfo.pts, cinfo ) ).first;
87 }
88
89 // ------------------------------------------------------------------
90 // IF we have two adjecent clusters, update duration where applicable
91 // ------------------------------------------------------------------
92
93 struct Duration {
94 static void fix( Cluster& prev, Cluster& next )
95 {
96 if( ( prev.fpos + prev.size) == next.fpos )
97 prev.duration = next.pts - prev.pts;
98 }
99 };
100
101 if( it != _clusters.begin() )
102 {
103 Duration::fix( prev_( it )->second, it->second );
104 }
105
106 if( it != _clusters.end() && next_( it ) != _clusters.end() )
107 {
108 Duration::fix( it->second, next_( it )->second );
109 }
110
111 return it;
112 }
113
114 void
add_seekpoint(track_id_t track_id,Seekpoint sp)115 SegmentSeeker::add_seekpoint( track_id_t track_id, Seekpoint sp )
116 {
117 seekpoints_t& seekpoints = _tracks_seekpoints[ track_id ];
118 seekpoints_t::iterator it = std::lower_bound( seekpoints.begin(), seekpoints.end(), sp );
119
120 if( it != seekpoints.end() && it->pts == sp.pts )
121 {
122 if (sp.trust_level <= it->trust_level)
123 return;
124
125 *it = sp;
126 }
127 else
128 {
129 seekpoints.insert( it, sp );
130 }
131 }
132
133 SegmentSeeker::tracks_seekpoint_t
find_greatest_seekpoints_in_range(fptr_t start_fpos,mtime_t end_pts,track_ids_t const & filter_tracks)134 SegmentSeeker::find_greatest_seekpoints_in_range( fptr_t start_fpos, mtime_t end_pts, track_ids_t const& filter_tracks )
135 {
136 tracks_seekpoint_t tpoints;
137
138 for( tracks_seekpoints_t::const_iterator it = _tracks_seekpoints.begin(); it != _tracks_seekpoints.end(); ++it )
139 {
140 if ( std::find( filter_tracks.begin(), filter_tracks.end(), it->first ) == filter_tracks.end() )
141 continue;
142
143 Seekpoint sp = get_first_seekpoint_around( end_pts, it->second );
144
145 if( sp.fpos < start_fpos )
146 continue;
147
148 if( sp.pts > end_pts )
149 continue;
150
151 tpoints.insert( tracks_seekpoint_t::value_type( it->first, sp ) );
152 }
153
154 if (tpoints.empty())
155 {
156 // try a further pts
157 for( tracks_seekpoints_t::const_iterator it = _tracks_seekpoints.begin(); it != _tracks_seekpoints.end(); ++it )
158 {
159 if ( std::find( filter_tracks.begin(), filter_tracks.end(), it->first ) == filter_tracks.end() )
160 continue;
161
162 Seekpoint sp = get_first_seekpoint_around( end_pts, it->second );
163
164 if( sp.fpos < start_fpos )
165 continue;
166
167 tpoints.insert( tracks_seekpoint_t::value_type( it->first, sp ) );
168 }
169 }
170
171 return tpoints;
172 }
173
174 SegmentSeeker::Seekpoint
get_first_seekpoint_around(mtime_t pts,seekpoints_t const & seekpoints,Seekpoint::TrustLevel trust_level)175 SegmentSeeker::get_first_seekpoint_around( mtime_t pts, seekpoints_t const& seekpoints,
176 Seekpoint::TrustLevel trust_level )
177 {
178 if( seekpoints.empty() )
179 {
180 return Seekpoint();
181 }
182
183 typedef seekpoints_t::const_iterator iterator;
184
185 Seekpoint const needle ( std::numeric_limits<fptr_t>::max(), pts );
186
187 iterator const it_begin = seekpoints.begin();
188 iterator const it_end = seekpoints.end();
189 iterator const it_middle = greatest_lower_bound( it_begin, it_end, needle );
190
191 iterator it_before;
192
193 // rewrind to _previous_ seekpoint with appropriate trust
194 for( it_before = it_middle; it_before != it_begin; --it_before )
195 {
196 if( it_before->trust_level >= trust_level )
197 return *it_before;
198 }
199 return *it_begin;
200 }
201
202 SegmentSeeker::seekpoint_pair_t
get_seekpoints_around(mtime_t pts,seekpoints_t const & seekpoints)203 SegmentSeeker::get_seekpoints_around( mtime_t pts, seekpoints_t const& seekpoints )
204 {
205 if( seekpoints.empty() )
206 {
207 return seekpoint_pair_t();
208 }
209
210 typedef seekpoints_t::const_iterator iterator;
211
212 Seekpoint const needle ( std::numeric_limits<fptr_t>::max(), pts );
213
214 iterator const it_begin = seekpoints.begin();
215 iterator const it_end = seekpoints.end();
216 iterator const it_middle = greatest_lower_bound( it_begin, it_end, needle );
217
218 if ( it_middle != it_end && (*it_middle).pts > pts)
219 // found nothing low enough, use the first one
220 return seekpoint_pair_t( *it_begin, Seekpoint() );
221
222 iterator it_before = it_middle;
223 iterator it_after = it_middle == it_end ? it_middle : next_( it_middle ) ;
224
225 return seekpoint_pair_t( *it_before,
226 it_after == it_end ? Seekpoint() : *it_after
227 );
228 }
229
230 SegmentSeeker::seekpoint_pair_t
get_seekpoints_around(mtime_t target_pts,track_ids_t const & priority_tracks)231 SegmentSeeker::get_seekpoints_around( mtime_t target_pts, track_ids_t const& priority_tracks )
232 {
233 seekpoint_pair_t points;
234
235 if( _tracks_seekpoints.empty() )
236 return points;
237
238 { // locate the max/min seekpoints for priority_tracks //
239
240 typedef track_ids_t::const_iterator track_iterator;
241
242 track_iterator const begin = priority_tracks.begin();
243 track_iterator const end = priority_tracks.end();
244
245 for( track_iterator it = begin; it != end; ++it )
246 {
247 seekpoint_pair_t track_points = get_seekpoints_around( target_pts, _tracks_seekpoints[ *it ] );
248
249 if( it == begin ) {
250 points = track_points;
251 continue;
252 }
253
254 if( track_points.first.trust_level > Seekpoint::DISABLED &&
255 points.first.fpos > track_points.first.fpos )
256 points.first = track_points.first;
257
258 if( track_points.second.trust_level > Seekpoint::DISABLED &&
259 points.second.fpos < track_points.second.fpos )
260 points.second = track_points.second;
261 }
262 }
263
264 { // check if we got a cluster which is closer to target_pts than the found cues //
265
266 cluster_map_t::iterator it = _clusters.lower_bound( target_pts );
267
268 if( it != _clusters.begin() && --it != _clusters.end() )
269 {
270 Cluster const& cluster = it->second;
271
272 if( cluster.fpos > points.first.fpos )
273 {
274 points.first.fpos = cluster.fpos;
275 points.first.pts = cluster.pts;
276
277 // do we need to update the max point? //
278
279 if( points.second.fpos < points.first.fpos )
280 {
281 points.second.fpos = cluster.fpos + cluster.size;
282 points.second.pts = cluster.pts + cluster.duration;
283 }
284 }
285 }
286 }
287
288 return points;
289 }
290
291 SegmentSeeker::tracks_seekpoint_t
get_seekpoints(matroska_segment_c & ms,mtime_t target_pts,track_ids_t const & priority_tracks,track_ids_t const & filter_tracks)292 SegmentSeeker::get_seekpoints( matroska_segment_c& ms, mtime_t target_pts,
293 track_ids_t const& priority_tracks, track_ids_t const& filter_tracks )
294 {
295 struct contains_all_of_t {
296 bool operator()( tracks_seekpoint_t const& haystack, track_ids_t const& track_ids )
297 {
298 for( track_ids_t::const_iterator it = track_ids.begin(); it != track_ids.end(); ++it ) {
299 if( haystack.find( *it ) == haystack.end() )
300 return false;
301 }
302
303 return true;
304 }
305 };
306
307 for( mtime_t needle_pts = target_pts; ; )
308 {
309 seekpoint_pair_t seekpoints = get_seekpoints_around( needle_pts, priority_tracks );
310
311 Seekpoint const& start = seekpoints.first;
312 Seekpoint const& end = seekpoints.second;
313
314 if ( start.fpos == std::numeric_limits<fptr_t>::max() )
315 return tracks_seekpoint_t();
316
317 if ( end.fpos != std::numeric_limits<fptr_t>::max() || !ms.b_cues )
318 // do not read the whole (infinite?) file to get seek indexes
319 index_range( ms, Range( start.fpos, end.fpos ), needle_pts );
320
321 tracks_seekpoint_t tpoints = find_greatest_seekpoints_in_range( start.fpos, target_pts, filter_tracks );
322
323 if( contains_all_of_t() ( tpoints, priority_tracks ) )
324 return tpoints;
325
326 needle_pts = start.pts - 1;
327 }
328
329 vlc_assert_unreachable();
330 }
331
332 void
index_range(matroska_segment_c & ms,Range search_area,mtime_t max_pts)333 SegmentSeeker::index_range( matroska_segment_c& ms, Range search_area, mtime_t max_pts )
334 {
335 ranges_t areas_to_search = get_search_areas( search_area.start, search_area.end );
336
337 for( ranges_t::const_iterator range_it = areas_to_search.begin(); range_it != areas_to_search.end(); ++range_it )
338 index_unsearched_range( ms, *range_it, max_pts );
339 }
340
341 void
index_unsearched_range(matroska_segment_c & ms,Range search_area,mtime_t max_pts)342 SegmentSeeker::index_unsearched_range( matroska_segment_c& ms, Range search_area, mtime_t max_pts )
343 {
344 mkv_jump_to( ms, search_area.start );
345
346 search_area.start = ms.es.I_O().getFilePointer();
347
348 fptr_t block_pos = search_area.start;
349 mtime_t block_pts;
350
351 while( block_pos < search_area.end )
352 {
353 KaxBlock * block;
354 KaxSimpleBlock * simpleblock;
355 KaxBlockAdditions *additions;
356
357 bool b_key_picture;
358 bool b_discardable_picture;
359 int64_t i_block_duration;
360 track_id_t track_id;
361
362 if( ms.BlockGet( block, simpleblock, additions,
363 &b_key_picture, &b_discardable_picture, &i_block_duration ) )
364 break;
365
366 if( simpleblock ) {
367 block_pos = simpleblock->GetElementPosition();
368 block_pts = simpleblock->GlobalTimecode() / 1000;
369 track_id = simpleblock->TrackNum();
370 }
371 else {
372 block_pos = block->GetElementPosition();
373 block_pts = block->GlobalTimecode() / 1000;
374 track_id = block->TrackNum();
375 }
376
377 bool const b_valid_track = ms.FindTrackByBlock( block, simpleblock ) != NULL;
378
379 delete block;
380
381 if( b_valid_track )
382 {
383 if( b_key_picture )
384 add_seekpoint( track_id, Seekpoint( block_pos, block_pts ) );
385
386 if( max_pts < block_pts )
387 break;
388 }
389 }
390
391 search_area.end = ms.es.I_O().getFilePointer();
392
393 mark_range_as_searched( search_area );
394 }
395
396 void
mark_range_as_searched(Range data)397 SegmentSeeker::mark_range_as_searched( Range data )
398 {
399 /* TODO: this is utterly ugly, we should do the insertion in-place */
400
401 _ranges_searched.insert( std::upper_bound( _ranges_searched.begin(), _ranges_searched.end(), data ), data );
402
403 {
404 ranges_t merged;
405
406 for( ranges_t::iterator it = _ranges_searched.begin(); it != _ranges_searched.end(); ++it )
407 {
408 if( merged.size() )
409 {
410 Range& last_entry = *merged.rbegin();
411
412 if( last_entry.end+1 >= it->start && last_entry.end < it->end )
413 {
414 last_entry.end = it->end;
415 continue;
416 }
417
418 if( it->start >= last_entry.start && it->end <= last_entry.end )
419 {
420 last_entry.end = std::max( last_entry.end, it->end );
421 continue;
422 }
423 }
424
425 merged.push_back( *it );
426 }
427
428 _ranges_searched = merged;
429 }
430 }
431
432
433 SegmentSeeker::ranges_t
get_search_areas(fptr_t start,fptr_t end) const434 SegmentSeeker::get_search_areas( fptr_t start, fptr_t end ) const
435 {
436 ranges_t areas_to_search;
437 Range needle ( start, end );
438
439 ranges_t::const_iterator it = greatest_lower_bound( _ranges_searched.begin(), _ranges_searched.end(), needle );
440
441 for( ; it != _ranges_searched.end() && needle.start < needle.end; ++it )
442 {
443 if( needle.start < it->start )
444 {
445 areas_to_search.push_back( Range( needle.start, it->start ) );
446 }
447
448 if( needle.start <= it->end )
449 needle.start = it->end + 1;
450 }
451
452 needle.start = std::max( needle.start, start );
453 if( it == _ranges_searched.end() && needle.start < needle.end )
454 {
455 areas_to_search.push_back( needle );
456 }
457
458 return areas_to_search;
459 }
460
461 void
mkv_jump_to(matroska_segment_c & ms,fptr_t fpos)462 SegmentSeeker::mkv_jump_to( matroska_segment_c& ms, fptr_t fpos )
463 {
464 fptr_t i_cluster_pos = -1;
465 ms.cluster = NULL;
466
467 if (!_cluster_positions.empty())
468 {
469 cluster_positions_t::iterator cluster_it = greatest_lower_bound(
470 _cluster_positions.begin(), _cluster_positions.end(), fpos
471 );
472
473 ms.es.I_O().setFilePointer( *cluster_it );
474 ms.ep.reconstruct( &ms.es, ms.segment, &ms.sys.demuxer );
475 }
476
477 while( ms.cluster == NULL || (
478 ms.cluster->IsFiniteSize() && ms.cluster->GetEndPosition() < fpos ) )
479 {
480 if( !( ms.cluster = static_cast<KaxCluster*>( ms.ep.Get() ) ) )
481 {
482 msg_Err( &ms.sys.demuxer, "unable to read KaxCluster during seek, giving up" );
483 return;
484 }
485
486 i_cluster_pos = ms.cluster->GetElementPosition();
487
488 add_cluster_position( i_cluster_pos );
489
490 mark_range_as_searched( Range( i_cluster_pos, ms.es.I_O().getFilePointer() ) );
491 }
492
493 ms.ep.Down();
494
495 /* read until cluster/timecode to initialize cluster */
496
497 while( EbmlElement * el = ms.ep.Get() )
498 {
499 if( MKV_CHECKED_PTR_DECL( p_tc, KaxClusterTimecode, el ) )
500 {
501 p_tc->ReadData( ms.es.I_O(), SCOPE_ALL_DATA );
502 ms.cluster->InitTimecode( static_cast<uint64>( *p_tc ), ms.i_timescale );
503 add_cluster(ms.cluster);
504 break;
505 }
506 else if( MKV_CHECKED_PTR_DECL( p_tc, EbmlCrc32, el ) )
507 {
508 p_tc->ReadData( ms.es.I_O(), SCOPE_ALL_DATA ); /* avoid a skip that may fail */
509 }
510 }
511
512 /* TODO: add error handling; what if we never get a KaxCluster and/or KaxClusterTimecode? */
513
514 mark_range_as_searched( Range( i_cluster_pos, ms.es.I_O().getFilePointer() ) );
515
516 /* jump to desired position */
517
518 ms.es.I_O().setFilePointer( fpos );
519 }
520
521