diff --git a/Cargo.toml b/Cargo.toml index 642e9a5..a908243 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,7 +74,7 @@ version = "0.5" [dev-dependencies] criterion = "0.3" ntest = "0.8" -symphonia = { version = "0.5", features = ["mp3"], git = "https://github.com/FelixMcFelix/Symphonia", branch = "songbird-fixes" } +symphonia = { version = "0.5", features = ["aac", "isomp4", "mp3"], git = "https://github.com/FelixMcFelix/Symphonia", branch = "songbird-fixes" } utils = { path = "utils" } tokio = { version = "1", features = ["rt", "rt-multi-thread"] } diff --git a/resources/ting-vid.mp4 b/resources/ting-vid.mp4 new file mode 100644 index 0000000..0d68855 Binary files /dev/null and b/resources/ting-vid.mp4 differ diff --git a/src/constants.rs b/src/constants.rs index bf7e6d5..48976a5 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -114,4 +114,7 @@ pub mod test_data { /// Path to a Wav source which can be read via a File. pub const FILE_WAV_TARGET: &str = "resources/loop.wav"; + + /// Path to an MP4 (H264 + AAC) source which can be read via a File. + pub const FILE_VID_TARGET: &str = "resources/ting-vid.mp4"; } diff --git a/src/input/live_input.rs b/src/input/live_input.rs index a44166a..ee3cf9a 100644 --- a/src/input/live_input.rs +++ b/src/input/live_input.rs @@ -1,7 +1,7 @@ use super::{AudioStream, Metadata, MetadataError, Parsed}; use symphonia_core::{ - codecs::{CodecRegistry, Decoder, DecoderOptions}, + codecs::{CodecRegistry, DecoderOptions}, errors::Error as SymphError, formats::FormatOptions, io::{MediaSource, MediaSourceStream, MediaSourceStreamOptions}, @@ -66,31 +66,30 @@ impl LiveInput { let format = probe_data.format; let meta = probe_data.metadata; - let mut default_track_id = format.default_track().map(|track| track.id); - let mut decoder: Option> = None; - - // Awkward loop: we need BOTH a track ID, and a decoder matching that track ID. - // Take default track (if it exists), take first track to be found otherwise. - for track in format.tracks() { - if default_track_id.is_some() && Some(track.id) != default_track_id { - continue; - } - - let this_decoder = codecs.make(&track.codec_params, &DecoderOptions::default())?; - - decoder = Some(this_decoder); - default_track_id = Some(track.id); - - break; - } + // if default track exists, try to make a decoder + // if that fails, linear scan and take first that succeeds + let decoder = format + .default_track() + .and_then(|track| { + codecs + .make(&track.codec_params, &DecoderOptions::default()) + .ok() + .map(|d| (d, track.id)) + }) + .or_else(|| { + format.tracks().iter().find_map(|track| { + codecs + .make(&track.codec_params, &DecoderOptions::default()) + .ok() + .map(|d| (d, track.id)) + }) + }); // No tracks is a playout error, a bad default track is also possible. // These are probably malformed? We could go best-effort, and fall back to tracks[0] // but drop such tracks for now. - let track_id = default_track_id.ok_or(SymphError::DecodeError("no track found"))?; - let decoder = decoder.ok_or(SymphError::DecodeError( - "reported default track did not exist", - ))?; + let (decoder, track_id) = + decoder.ok_or(SymphError::DecodeError("no compatible track found"))?; let p = Parsed { format, @@ -145,3 +144,24 @@ impl LiveInput { } } } + +#[cfg(test)] +mod tests { + use crate::{ + constants::test_data::FILE_VID_TARGET, + input::{codecs::*, File, Input}, + }; + + #[tokio::test] + #[ntest::timeout(10_000)] + async fn promote_finds_valid_audio() { + // Video files often set their default to... the video stream, unsurprisingly. + // In these cases we still want to play the attached audio -- this checks that songbird + // finds the audio on a non-default track via `LiveInput::promote`. + let input = Input::from(File::new(FILE_VID_TARGET)); + input + .make_playable_async(&CODEC_REGISTRY, &PROBE) + .await + .unwrap(); + } +}