From 87786c1faa9bfe20d0c6974097f4ec268b60e1c7 Mon Sep 17 00:00:00 2001 From: Roderick van Domburg Date: Thu, 16 Jan 2025 19:42:57 +0100 Subject: [PATCH] flac, isomp4, mkv: optimize length detection by using byte_len() instead 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. --- symphonia-bundle-flac/src/demuxer.rs | 22 +++++++-------- symphonia-format-isomp4/src/demuxer.rs | 39 ++++++++++---------------- symphonia-format-mkv/src/demuxer.rs | 11 +++----- 3 files changed, 29 insertions(+), 43 deletions(-) diff --git a/symphonia-bundle-flac/src/demuxer.rs b/symphonia-bundle-flac/src/demuxer.rs index 74dbc166..68ad20a4 100644 --- a/symphonia-bundle-flac/src/demuxer.rs +++ b/symphonia-bundle-flac/src/demuxer.rs @@ -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::*; @@ -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"); } } @@ -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); } } @@ -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 { @@ -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; } } @@ -366,8 +365,7 @@ fn read_stream_info_block( // Add the track. tracks.push(Track::new(0, codec_params)); - } - else { + } else { return decode_error("flac: found more than one stream info block"); } diff --git a/symphonia-format-isomp4/src/demuxer.rs b/symphonia-format-isomp4/src/demuxer.rs index 2bab1df1..97431ba6 100644 --- a/symphonia-format-isomp4/src/demuxer.rs +++ b/symphonia-format-isomp4/src/demuxer.rs @@ -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}; @@ -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 }; @@ -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"); } @@ -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) } } @@ -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) } @@ -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 }; @@ -365,8 +362,7 @@ impl FormatReader for IsoMp4Reader { if !is_seekable { sidx = Some(iter.read_atom::()?); 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::()?; @@ -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."); } } @@ -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. @@ -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"); @@ -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) } } diff --git a/symphonia-format-mkv/src/demuxer.rs b/symphonia-format-mkv/src/demuxer.rs index ddab18e5..a11c9687 100644 --- a/symphonia-format-mkv/src/demuxer.rs +++ b/symphonia-format-mkv/src/demuxer.rs @@ -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(); } } @@ -158,8 +157,7 @@ impl MkvReader { fn seek_track_by_ts(&mut self, track_id: u32, ts: u64) -> Result { 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 { @@ -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 };