Skip to content

Commit

Permalink
Input: Fix audio stream selection for video files. (#150)
Browse files Browse the repository at this point in the history
`youtube-dl` has a tendency to return mp4 files containing video and audio data vs `yt-dlp`. Naturally, the default stream in such cases will be the video data, so we fail to create a decoder.

This PR changes `LiveInput::promote` to first attempt to instantiate a decoder for the default track. If this fails to do so, then we reattempt this for all available tracks until one can be found. Previously, this method chose the default (or first available) track ID, and then failed if *that* track could not be decoded even if a compatible stream existed.

This was tested using `cargo make ready`, as well as manually verifying and adding a test case with a simple mp4 file.
  • Loading branch information
FelixMcFelix committed Nov 20, 2023
1 parent 372156e commit 03b0803
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 23 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }

Expand Down
Binary file added resources/ting-vid.mp4
Binary file not shown.
3 changes: 3 additions & 0 deletions src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
64 changes: 42 additions & 22 deletions src/input/live_input.rs
Original file line number Diff line number Diff line change
@@ -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},
Expand Down Expand Up @@ -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<Box<dyn Decoder>> = 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,
Expand Down Expand Up @@ -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();
}
}

0 comments on commit 03b0803

Please sign in to comment.