1 /*****************************************************************************
2 * matroska_segment.hpp : matroska demuxer
3 *****************************************************************************
4 * Copyright (C) 2016 VLC authors and VideoLAN
7 * Authors: Filip Roséen <filip@videolabs.io>
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.
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.
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 *****************************************************************************/
24 #include "matroska_segment_seeker.hpp"
25 #include "matroska_segment.hpp"
27 #include "Ebml_parser.hpp"
28 #include "Ebml_dispatcher.hpp"
30 #include "stream_io_callback.hpp"
36 template<class It
, class T
>
37 It
greatest_lower_bound( It beg
, It end
, T
const& value
)
39 It it
= std::upper_bound( beg
, end
, value
);
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.
48 template<class It
> It
prev_( It it
) { return --it
; }
49 template<class It
> It
next_( It it
) { return ++it
; }
54 SegmentSeeker::cluster_positions_t::iterator
55 SegmentSeeker::add_cluster_position( fptr_t fpos
)
57 cluster_positions_t::iterator insertion_point
= std::upper_bound(
58 _cluster_positions
.begin(),
59 _cluster_positions
.end(),
63 return _cluster_positions
.insert( insertion_point
, fpos
);
66 SegmentSeeker::cluster_map_t::iterator
67 SegmentSeeker::add_cluster( KaxCluster
* const p_cluster
)
70 /* fpos */ p_cluster
->GetElementPosition(),
71 /* pts */ vlc_tick_t( p_cluster
->GlobalTimecode() / INT64_C( 1000 ) ),
72 /* duration */ vlc_tick_t( -1 ),
73 /* size */ p_cluster
->IsFiniteSize()
74 ? p_cluster
->GetEndPosition() - p_cluster
->GetElementPosition()
78 add_cluster_position( cinfo
.fpos
);
80 cluster_map_t::iterator it
= _clusters
.lower_bound( cinfo
.pts
);
82 if( it
!= _clusters
.end() && it
->second
.pts
== cinfo
.pts
)
84 // cluster already known
88 it
= _clusters
.insert( cluster_map_t::value_type( cinfo
.pts
, cinfo
) ).first
;
91 // ------------------------------------------------------------------
92 // IF we have two adjecent clusters, update duration where applicable
93 // ------------------------------------------------------------------
96 static void fix( Cluster
& prev
, Cluster
& next
)
98 if( ( prev
.fpos
+ prev
.size
) == next
.fpos
)
99 prev
.duration
= next
.pts
- prev
.pts
;
103 if( it
!= _clusters
.begin() )
105 Duration::fix( prev_( it
)->second
, it
->second
);
108 if( it
!= _clusters
.end() && next_( it
) != _clusters
.end() )
110 Duration::fix( it
->second
, next_( it
)->second
);
117 SegmentSeeker::add_seekpoint( track_id_t track_id
, Seekpoint sp
)
119 seekpoints_t
& seekpoints
= _tracks_seekpoints
[ track_id
];
120 seekpoints_t::iterator it
= std::lower_bound( seekpoints
.begin(), seekpoints
.end(), sp
);
122 if( it
!= seekpoints
.end() && it
->pts
== sp
.pts
)
124 if (sp
.trust_level
<= it
->trust_level
)
131 seekpoints
.insert( it
, sp
);
135 SegmentSeeker::tracks_seekpoint_t
136 SegmentSeeker::find_greatest_seekpoints_in_range( fptr_t start_fpos
, vlc_tick_t end_pts
, track_ids_t
const& filter_tracks
)
138 tracks_seekpoint_t tpoints
;
140 for( tracks_seekpoints_t::const_iterator it
= _tracks_seekpoints
.begin(); it
!= _tracks_seekpoints
.end(); ++it
)
142 if ( std::find( filter_tracks
.begin(), filter_tracks
.end(), it
->first
) == filter_tracks
.end() )
145 Seekpoint sp
= get_first_seekpoint_around( end_pts
, it
->second
);
147 if( sp
.fpos
< start_fpos
)
150 if( sp
.pts
> end_pts
)
153 tpoints
.insert( tracks_seekpoint_t::value_type( it
->first
, sp
) );
159 for( tracks_seekpoints_t::const_iterator it
= _tracks_seekpoints
.begin(); it
!= _tracks_seekpoints
.end(); ++it
)
161 if ( std::find( filter_tracks
.begin(), filter_tracks
.end(), it
->first
) == filter_tracks
.end() )
164 Seekpoint sp
= get_first_seekpoint_around( end_pts
, it
->second
);
166 if( sp
.fpos
< start_fpos
)
169 tpoints
.insert( tracks_seekpoint_t::value_type( it
->first
, sp
) );
176 SegmentSeeker::Seekpoint
177 SegmentSeeker::get_first_seekpoint_around( vlc_tick_t pts
, seekpoints_t
const& seekpoints
,
178 Seekpoint::TrustLevel trust_level
)
180 if( seekpoints
.empty() )
185 typedef seekpoints_t::const_iterator iterator
;
187 Seekpoint
const needle ( std::numeric_limits
<fptr_t
>::max(), pts
);
189 iterator
const it_begin
= seekpoints
.begin();
190 iterator
const it_end
= seekpoints
.end();
191 iterator
const it_middle
= greatest_lower_bound( it_begin
, it_end
, needle
);
195 // rewrind to _previous_ seekpoint with appropriate trust
196 for( it_before
= it_middle
; it_before
!= it_begin
; --it_before
)
198 if( it_before
->trust_level
>= trust_level
)
204 SegmentSeeker::seekpoint_pair_t
205 SegmentSeeker::get_seekpoints_around( vlc_tick_t pts
, seekpoints_t
const& seekpoints
)
207 if( seekpoints
.empty() )
209 return seekpoint_pair_t();
212 typedef seekpoints_t::const_iterator iterator
;
214 Seekpoint
const needle ( std::numeric_limits
<fptr_t
>::max(), pts
);
216 iterator
const it_begin
= seekpoints
.begin();
217 iterator
const it_end
= seekpoints
.end();
218 iterator
const it_middle
= greatest_lower_bound( it_begin
, it_end
, needle
);
220 if ( it_middle
!= it_end
&& (*it_middle
).pts
> pts
)
221 // found nothing low enough, use the first one
222 return seekpoint_pair_t( *it_begin
, Seekpoint() );
224 iterator it_before
= it_middle
;
225 iterator it_after
= it_middle
== it_end
? it_middle
: next_( it_middle
) ;
227 return seekpoint_pair_t( *it_before
,
228 it_after
== it_end
? Seekpoint() : *it_after
232 SegmentSeeker::seekpoint_pair_t
233 SegmentSeeker::get_seekpoints_around( vlc_tick_t target_pts
, track_ids_t
const& priority_tracks
)
235 seekpoint_pair_t points
;
237 if( _tracks_seekpoints
.empty() )
240 { // locate the max/min seekpoints for priority_tracks //
242 typedef track_ids_t::const_iterator track_iterator
;
244 track_iterator
const begin
= priority_tracks
.begin();
245 track_iterator
const end
= priority_tracks
.end();
247 for( track_iterator it
= begin
; it
!= end
; ++it
)
249 seekpoint_pair_t track_points
= get_seekpoints_around( target_pts
, _tracks_seekpoints
[ *it
] );
252 points
= track_points
;
256 if( track_points
.first
.trust_level
> Seekpoint::DISABLED
&&
257 points
.first
.fpos
> track_points
.first
.fpos
)
258 points
.first
= track_points
.first
;
260 if( track_points
.second
.trust_level
> Seekpoint::DISABLED
&&
261 points
.second
.fpos
< track_points
.second
.fpos
)
262 points
.second
= track_points
.second
;
266 { // check if we got a cluster which is closer to target_pts than the found cues //
268 cluster_map_t::iterator it
= _clusters
.lower_bound( target_pts
);
270 if( it
!= _clusters
.begin() && --it
!= _clusters
.end() )
272 Cluster
const& cluster
= it
->second
;
274 if( cluster
.fpos
> points
.first
.fpos
)
276 points
.first
= Seekpoint( cluster
.fpos
, cluster
.pts
);
278 // do we need to update the max point? //
280 if( points
.second
.fpos
< points
.first
.fpos
)
281 points
.second
= Seekpoint( cluster
.fpos
+ cluster
.size
, cluster
.pts
+ cluster
.duration
);
289 SegmentSeeker::tracks_seekpoint_t
290 SegmentSeeker::get_seekpoints( matroska_segment_c
& ms
, vlc_tick_t target_pts
,
291 track_ids_t
const& priority_tracks
, track_ids_t
const& filter_tracks
)
293 struct contains_all_of_t
{
294 bool operator()( tracks_seekpoint_t
const& haystack
, track_ids_t
const& track_ids
)
296 for( track_ids_t::const_iterator it
= track_ids
.begin(); it
!= track_ids
.end(); ++it
) {
297 if( haystack
.find( *it
) == haystack
.end() )
305 for( vlc_tick_t needle_pts
= target_pts
; ; )
307 seekpoint_pair_t seekpoints
= get_seekpoints_around( needle_pts
, priority_tracks
);
309 Seekpoint
const& start
= seekpoints
.first
;
310 Seekpoint
const& end
= seekpoints
.second
;
312 if ( start
.fpos
== std::numeric_limits
<fptr_t
>::max() )
313 return tracks_seekpoint_t();
315 if ( end
.fpos
!= std::numeric_limits
<fptr_t
>::max() || !ms
.b_cues
)
316 // do not read the whole (infinite?) file to get seek indexes
317 index_range( ms
, Range( start
.fpos
, end
.fpos
), needle_pts
);
319 tracks_seekpoint_t tpoints
= find_greatest_seekpoints_in_range( start
.fpos
, target_pts
, filter_tracks
);
321 if( contains_all_of_t() ( tpoints
, priority_tracks
) )
324 needle_pts
= start
.pts
- 1;
327 vlc_assert_unreachable();
331 SegmentSeeker::index_range( matroska_segment_c
& ms
, Range search_area
, vlc_tick_t max_pts
)
333 ranges_t areas_to_search
= get_search_areas( search_area
.start
, search_area
.end
);
335 for( ranges_t::const_iterator range_it
= areas_to_search
.begin(); range_it
!= areas_to_search
.end(); ++range_it
)
336 index_unsearched_range( ms
, *range_it
, max_pts
);
340 SegmentSeeker::index_unsearched_range( matroska_segment_c
& ms
, Range search_area
, vlc_tick_t max_pts
)
342 mkv_jump_to( ms
, search_area
.start
);
344 search_area
.start
= ms
.es
.I_O().getFilePointer();
346 fptr_t block_pos
= search_area
.start
;
347 vlc_tick_t block_pts
;
349 while( block_pos
< search_area
.end
)
352 KaxSimpleBlock
* simpleblock
;
355 bool b_discardable_picture
;
356 int64_t i_block_duration
;
359 if( ms
.BlockGet( block
, simpleblock
, &b_key_picture
, &b_discardable_picture
, &i_block_duration
) )
362 KaxInternalBlock
& internal_block
= simpleblock
363 ? static_cast<KaxInternalBlock
&>( *simpleblock
)
364 : static_cast<KaxInternalBlock
&>( *block
);
366 block_pos
= internal_block
.GetElementPosition();
367 block_pts
= internal_block
.GlobalTimecode() / 1000;
368 track_id
= internal_block
.TrackNum();
370 bool const b_valid_track
= ms
.FindTrackByBlock( block
, simpleblock
) != NULL
;
377 add_seekpoint( track_id
, Seekpoint( block_pos
, block_pts
) );
379 if( max_pts
< block_pts
)
384 search_area
.end
= ms
.es
.I_O().getFilePointer();
386 mark_range_as_searched( search_area
);
390 SegmentSeeker::mark_range_as_searched( Range data
)
392 /* TODO: this is utterly ugly, we should do the insertion in-place */
394 _ranges_searched
.insert( std::upper_bound( _ranges_searched
.begin(), _ranges_searched
.end(), data
), data
);
399 for( ranges_t::iterator it
= _ranges_searched
.begin(); it
!= _ranges_searched
.end(); ++it
)
403 Range
& last_entry
= *merged
.rbegin();
405 if( last_entry
.end
+1 >= it
->start
&& last_entry
.end
< it
->end
)
407 last_entry
.end
= it
->end
;
411 if( it
->start
>= last_entry
.start
&& it
->end
<= last_entry
.end
)
413 last_entry
.end
= std::max( last_entry
.end
, it
->end
);
418 merged
.push_back( *it
);
421 _ranges_searched
= merged
;
426 SegmentSeeker::ranges_t
427 SegmentSeeker::get_search_areas( fptr_t start
, fptr_t end
) const
429 ranges_t areas_to_search
;
430 Range
needle ( start
, end
);
432 ranges_t::const_iterator it
= greatest_lower_bound( _ranges_searched
.begin(), _ranges_searched
.end(), needle
);
434 for( ; it
!= _ranges_searched
.end() && needle
.start
< needle
.end
; ++it
)
436 if( needle
.start
< it
->start
)
438 areas_to_search
.push_back( Range( needle
.start
, it
->start
) );
441 if( needle
.start
<= it
->end
)
442 needle
.start
= it
->end
+ 1;
445 needle
.start
= std::max( needle
.start
, start
);
446 if( it
== _ranges_searched
.end() && needle
.start
< needle
.end
)
448 areas_to_search
.push_back( needle
);
451 return areas_to_search
;
455 SegmentSeeker::mkv_jump_to( matroska_segment_c
& ms
, fptr_t fpos
)
457 fptr_t i_cluster_pos
= -1;
459 if ( fpos
!= std::numeric_limits
<SegmentSeeker::fptr_t
>::max() )
462 if ( !_cluster_positions
.empty() )
464 cluster_positions_t::iterator cluster_it
= greatest_lower_bound(
465 _cluster_positions
.begin(), _cluster_positions
.end(), fpos
468 ms
.es
.I_O().setFilePointer( *cluster_it
);
469 ms
.ep
.reconstruct( &ms
.es
, ms
.segment
, &ms
.sys
.demuxer
);
472 while( ms
.cluster
== NULL
|| (
473 ms
.cluster
->IsFiniteSize() && ms
.cluster
->GetEndPosition() < fpos
) )
475 if( !( ms
.cluster
= static_cast<KaxCluster
*>( ms
.ep
.Get() ) ) )
477 msg_Err( &ms
.sys
.demuxer
, "unable to read KaxCluster during seek, giving up" );
481 i_cluster_pos
= ms
.cluster
->GetElementPosition();
483 add_cluster_position( i_cluster_pos
);
485 mark_range_as_searched( Range( i_cluster_pos
, ms
.es
.I_O().getFilePointer() ) );
488 else if (ms
.cluster
!= NULL
)
490 // make sure we start reading after the Cluster start
491 ms
.es
.I_O().setFilePointer(ms
.cluster
->GetDataStart());
496 /* read until cluster/timecode to initialize cluster */
498 while( EbmlElement
* el
= ms
.ep
.Get() )
500 if( MKV_CHECKED_PTR_DECL( p_tc
, KaxClusterTimecode
, el
) )
502 p_tc
->ReadData( ms
.es
.I_O(), SCOPE_ALL_DATA
);
503 ms
.cluster
->InitTimecode( static_cast<uint64
>( *p_tc
), ms
.i_timescale
);
504 add_cluster(ms
.cluster
);
507 else if( MKV_CHECKED_PTR_DECL( p_tc
, EbmlCrc32
, el
) )
509 p_tc
->ReadData( ms
.es
.I_O(), SCOPE_ALL_DATA
); /* avoid a skip that may fail */
513 /* TODO: add error handling; what if we never get a KaxCluster and/or KaxClusterTimecode? */
515 mark_range_as_searched( Range( i_cluster_pos
, ms
.es
.I_O().getFilePointer() ) );
517 /* jump to desired position */
519 if ( fpos
!= std::numeric_limits
<SegmentSeeker::fptr_t
>::max() )
520 ms
.es
.I_O().setFilePointer( fpos
);