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