Skip to content

Commit

Permalink
flac, isomp4, mkv: optimize length detection by using byte_len() inst…
Browse files Browse the repository at this point in the history
…ead of seeking

Replace seeking to end of file with byte_len() call to get total stream
length. This improves performance when streaming files by avoiding an
unnecessary seek operation. The change maintains the same functionality
while being more efficient, especially for remote/streaming files where
seeks are expensive.

Also fixes some minor code formatting issues with else clauses.
  • Loading branch information
roderickvd committed Jan 16, 2025
1 parent 64171c3 commit 87786c1
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 43 deletions.
22 changes: 10 additions & 12 deletions symphonia-bundle-flac/src/demuxer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ use std::io::{Seek, SeekFrom};
use symphonia_core::support_format;

use symphonia_core::codecs::{CodecParameters, VerificationCheck, CODEC_TYPE_FLAC};
use symphonia_core::errors::{decode_error, seek_error, unsupported_error, Result, SeekErrorKind};
use symphonia_core::errors::{
decode_error, seek_error, unsupported_error, Error, Result, SeekErrorKind,
};
use symphonia_core::formats::prelude::*;
use symphonia_core::formats::util::{SeekIndex, SeekSearchResult};
use symphonia_core::io::*;
Expand Down Expand Up @@ -69,8 +71,7 @@ impl FlacReader {
let mut new_index = SeekIndex::new();
read_seek_table_block(&mut block_stream, header.block_len, &mut new_index)?;
index = Some(new_index);
}
else {
} else {
return decode_error("flac: found more than one seek table block");
}
}
Expand Down Expand Up @@ -204,8 +205,7 @@ impl FormatReader for FlacReader {
// known, the seek cannot be completed.
if let Some(sample_rate) = params.sample_rate {
TimeBase::new(1, sample_rate).calc_timestamp(time)
}
else {
} else {
return seek_error(SeekErrorKind::Unseekable);
}
}
Expand All @@ -229,7 +229,8 @@ impl FormatReader for FlacReader {
// lower bound is set to the byte offset of the first frame, while the upper bound is
// set to the length of the stream.
let mut start_byte_offset = self.first_frame_offset;
let mut end_byte_offset = self.reader.seek(SeekFrom::End(0))?;
let mut end_byte_offset =
self.reader.byte_len().ok_or(Error::SeekError(SeekErrorKind::Unseekable))?;

// If there is an index, use it to refine the binary search range.
if let Some(ref index) = self.index {
Expand Down Expand Up @@ -265,13 +266,11 @@ impl FormatReader for FlacReader {

if ts < sync.ts {
end_byte_offset = mid_byte_offset;
}
else if ts >= sync.ts && ts < sync.ts + sync.dur {
} else if ts >= sync.ts && ts < sync.ts + sync.dur {
debug!("seeked to ts={} (delta={})", sync.ts, sync.ts as i64 - ts as i64);

return Ok(SeekedTo { track_id: 0, actual_ts: sync.ts, required_ts: ts });
}
else {
} else {
start_byte_offset = mid_byte_offset;
}
}
Expand Down Expand Up @@ -366,8 +365,7 @@ fn read_stream_info_block<B: ReadBytes + FiniteStream>(

// Add the track.
tracks.push(Track::new(0, codec_params));
}
else {
} else {
return decode_error("flac: found more than one stream info block");
}

Expand Down
39 changes: 15 additions & 24 deletions symphonia-format-isomp4/src/demuxer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
use symphonia_core::{errors::end_of_stream_error, support_format};

use symphonia_core::codecs::CodecParameters;
use symphonia_core::errors::{decode_error, seek_error, unsupported_error, Result, SeekErrorKind};
use symphonia_core::errors::{
decode_error, seek_error, unsupported_error, Error, Result, SeekErrorKind,
};
use symphonia_core::formats::prelude::*;
use symphonia_core::io::{MediaSource, MediaSourceStream, ReadBytes, SeekBuffered};
use symphonia_core::meta::{Metadata, MetadataLog};
Expand Down Expand Up @@ -166,8 +168,7 @@ impl IsoMp4Reader {
// then the base position is the position of current the sample.
let pos = if sample_data_desc.base_pos > track.next_sample_pos {
sample_data_desc.base_pos
}
else {
} else {
track.next_sample_pos
};

Expand Down Expand Up @@ -212,8 +213,7 @@ impl IsoMp4Reader {

// Push the segment.
self.segs.push(Box::new(seg));
}
else {
} else {
// TODO: This is a fatal error.
return decode_error("isomp4: moof atom present without mvex atom");
}
Expand All @@ -234,8 +234,7 @@ impl IsoMp4Reader {
if let Some(track) = self.tracks.get(track_num) {
let tb = track.codec_params.time_base.unwrap();
self.seek_track_by_ts(track_num, tb.calc_timestamp(time))
}
else {
} else {
seek_error(SeekErrorKind::Unseekable)
}
}
Expand Down Expand Up @@ -297,8 +296,7 @@ impl IsoMp4Reader {
);

Ok(SeekedTo { track_id: track_num as u32, required_ts: ts, actual_ts: timing.ts })
}
else {
} else {
// Timestamp was not found.
seek_error(SeekErrorKind::OutOfRange)
}
Expand Down Expand Up @@ -335,12 +333,11 @@ impl FormatReader for IsoMp4Reader {
// Get the total length of the stream, if possible.
let total_len = if is_seekable {
let pos = mss.pos();
let len = mss.seek(SeekFrom::End(0))?;
let len = mss.byte_len().ok_or(Error::SeekError(SeekErrorKind::Unseekable))?;
mss.seek(SeekFrom::Start(pos))?;
info!("stream is seekable with len={} bytes.", len);
Some(len)
}
else {
} else {
None
};

Expand All @@ -365,8 +362,7 @@ impl FormatReader for IsoMp4Reader {
if !is_seekable {
sidx = Some(iter.read_atom::<SidxAtom>()?);
break;
}
else {
} else {
// If the stream is seekable, examine all segment indexes and select the
// index with the earliest presentation timestamp to be the first.
let new_sidx = iter.read_atom::<SidxAtom>()?;
Expand Down Expand Up @@ -444,8 +440,7 @@ impl FormatReader for IsoMp4Reader {
// If a Segment Index (sidx) atom was found, add the segments contained within.
if sidx.is_some() {
info!("stream is segmented with a segment index.");
}
else {
} else {
info!("stream is segmented without a segment index.");
}
}
Expand Down Expand Up @@ -498,8 +493,7 @@ impl FormatReader for IsoMp4Reader {
// Using the current set of segments, try to get the next sample info.
if let Some(info) = self.next_sample_info()? {
break info;
}
else {
} else {
// No more segments. If the stream is unseekable, it may be the case that there are
// more segments coming. Iterate atoms until a new segment is found or the
// end-of-stream is reached.
Expand All @@ -517,14 +511,12 @@ impl FormatReader for IsoMp4Reader {
if reader.is_seekable() {
// Fallback to a slow seek if the stream is seekable.
reader.seek(SeekFrom::Start(sample_info.pos))?;
}
else if sample_info.pos > reader.pos() {
} else if sample_info.pos > reader.pos() {
// The stream is not seekable but the desired seek position is ahead of the reader's
// current position, thus the seek can be emulated by ignoring the bytes up to the
// the desired seek position.
reader.ignore_bytes(sample_info.pos - reader.pos())?;
}
else {
} else {
// The stream is not seekable and the desired seek position falls outside the lower
// bound of the buffer cache. This sample cannot be read.
return decode_error("isomp4: packet out-of-bounds for a non-seekable stream");
Expand Down Expand Up @@ -576,8 +568,7 @@ impl FormatReader for IsoMp4Reader {

// Seek the primary track and return the result.
self.seek_track_by_ts(selected_track_id, ts)
}
else {
} else {
seek_error(SeekErrorKind::Unseekable)
}
}
Expand Down
11 changes: 4 additions & 7 deletions symphonia-format-mkv/src/demuxer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ impl MkvReader {
while let Some(frame) = self.frames.front() {
if frame.timestamp + frame.duration >= ts && frame.track == track_id {
break 'out frame.timestamp;
}
else {
} else {
self.frames.pop_front();
}
}
Expand All @@ -158,8 +157,7 @@ impl MkvReader {
fn seek_track_by_ts(&mut self, track_id: u32, ts: u64) -> Result<SeekedTo> {
if self.clusters.is_empty() {
self.seek_track_by_ts_forward(track_id, ts)
}
else {
} else {
let mut target_cluster = None;
for cluster in &self.clusters {
if cluster.timestamp > ts {
Expand Down Expand Up @@ -307,12 +305,11 @@ impl FormatReader for MkvReader {
// Get the total length of the stream, if possible.
let total_len = if is_seekable {
let pos = reader.pos();
let len = reader.seek(SeekFrom::End(0))?;
let len = reader.byte_len().ok_or(Error::SeekError(SeekErrorKind::Unseekable))?;
reader.seek(SeekFrom::Start(pos))?;
log::info!("stream is seekable with len={} bytes.", len);
Some(len)
}
else {
} else {
None
};

Expand Down

0 comments on commit 87786c1

Please sign in to comment.