@@ -190,24 +190,52 @@ impl<Reader: Read + Seek + Send + 'static> Track<Reader> {
190190 /// `start_index` is the last sync sample before seek (for decoder warmup).
191191 /// `present_from_index` is the first sample at or after seek.
192192 /// Returns `None` if seek is past the end.
193- fn find_seek_start_sample ( & mut self , seek : Duration ) -> Option < ( u32 , u32 ) > {
193+ fn find_seek_start_sample ( & self , seek : Duration ) -> Option < ( u32 , u32 ) > {
194194 let seek_timescale = ( ( seek + self . offset ) . as_secs_f64 ( ) * self . timescale as f64 ) as u64 ;
195- let mut best_sync_index = 1u32 ;
196- // TODO: improve performance
197- for i in 1 ..=self . sample_count {
198- match self . reader . read_sample ( self . track_id , i) {
199- Ok ( Some ( sample) ) => {
200- if sample. is_sync {
201- best_sync_index = i
202- }
203- if sample. start_time >= seek_timescale {
204- return Some ( ( best_sync_index, i) ) ;
205- }
206- }
207- _ => return None ,
195+ let track = & self . reader . tracks ( ) [ & self . track_id ] ;
196+
197+ // The STTS box maps samples to batches of the same sample length
198+ let stts = & track. trak . mdia . minf . stbl . stts ;
199+
200+ let mut first_batch_sample_id = 1u32 ;
201+ let mut elapsed = 0u64 ;
202+ let mut present_from_index = None ;
203+
204+ // Finds the first sample after the provided seek point.
205+ for entry in & stts. entries {
206+ let batch_end_time = elapsed + entry. sample_count as u64 * entry. sample_delta as u64 ;
207+
208+ if seek_timescale < batch_end_time {
209+ let offset_in_batch = {
210+ let time_into_batch = seek_timescale - elapsed;
211+ time_into_batch. div_ceil ( entry. sample_delta as u64 ) as u32
212+ } ;
213+ present_from_index = Some ( first_batch_sample_id + offset_in_batch) ;
214+ break ;
208215 }
216+
217+ elapsed = batch_end_time;
218+ first_batch_sample_id += entry. sample_count ;
209219 }
210- None
220+
221+ let present_from_index = present_from_index?;
222+
223+ // The STSS box contains indices of sync samples (e.g. key frames).
224+ // `None` means all samples are sync samples.
225+ let stss = & track. trak . mdia . minf . stbl . stss ;
226+
227+ let sync_index = match & stss {
228+ Some ( stss) => {
229+ let pos = stss. entries . partition_point ( |& s| s <= present_from_index) ;
230+
231+ // `pos == 0` means no sync sample was found before the seek time.
232+ // Fall back to the first sample.
233+ if pos == 0 { 1 } else { stss. entries [ pos - 1 ] }
234+ }
235+ None => present_from_index,
236+ } ;
237+
238+ Some ( ( sync_index, present_from_index) )
211239 }
212240}
213241
0 commit comments