diff --git a/.idea/dictionaries/bt.xml b/.idea/dictionaries/bt.xml index 562630d84e..beefee1473 100644 --- a/.idea/dictionaries/bt.xml +++ b/.idea/dictionaries/bt.xml @@ -10,6 +10,7 @@ hifigan scrollback softvc + symphonia tacotron transcoded transcodings diff --git a/Cargo.lock b/Cargo.lock index edca6e1420..95074fe1b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2868,6 +2868,7 @@ name = "media" version = "0.0.1" dependencies = [ "errors", + "log", "symphonia", ] @@ -4722,9 +4723,13 @@ checksum = "17033fe05e4f7f10a6ad602c272bafd2520b2e5cdd9feb61494d9cdce08e002f" dependencies = [ "lazy_static", "symphonia-bundle-flac", + "symphonia-bundle-mp3", + "symphonia-codec-aac", + "symphonia-codec-alac", "symphonia-codec-pcm", "symphonia-codec-vorbis", "symphonia-core", + "symphonia-format-isomp4", "symphonia-format-mkv", "symphonia-format-ogg", "symphonia-format-wav", @@ -4743,6 +4748,40 @@ dependencies = [ "symphonia-utils-xiph", ] +[[package]] +name = "symphonia-bundle-mp3" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db5d3d53535ae2b7d0e39e82f683cac5398a6c8baca25ff1183e107d13959d3e" +dependencies = [ + "bitflags", + "lazy_static", + "log", + "symphonia-core", + "symphonia-metadata", +] + +[[package]] +name = "symphonia-codec-aac" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e9431b89428c31b01428563df18e52b1aff7c49e71fb80b2fa8e85632094776" +dependencies = [ + "lazy_static", + "log", + "symphonia-core", +] + +[[package]] +name = "symphonia-codec-alac" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "452438d6f32bf07f2f55f35371c3c8e9cce4a028a08b47fb301001f85a950f02" +dependencies = [ + "log", + "symphonia-core", +] + [[package]] name = "symphonia-codec-pcm" version = "0.5.1" @@ -4777,6 +4816,19 @@ dependencies = [ "log", ] +[[package]] +name = "symphonia-format-isomp4" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dce84cea63247dfcc43e12edf2d19406fbb62ebc24bac1b8e6c49f1ba67b890" +dependencies = [ + "encoding_rs", + "log", + "symphonia-core", + "symphonia-metadata", + "symphonia-utils-xiph", +] + [[package]] name = "symphonia-format-mkv" version = "0.5.1" diff --git a/crates/lib/database_queries/src/queries/media_uploads/insert_media_upload.rs b/crates/lib/database_queries/src/queries/media_uploads/insert_media_upload.rs index 747ebe8aa3..9145110069 100644 --- a/crates/lib/database_queries/src/queries/media_uploads/insert_media_upload.rs +++ b/crates/lib/database_queries/src/queries/media_uploads/insert_media_upload.rs @@ -70,7 +70,7 @@ SET args.media_type, args.maybe_original_filename, args.original_file_size_bytes, - args.maybe_original_duration_millis, + args.maybe_original_duration_millis.unwrap_or(0), args.maybe_original_mime_type, args.maybe_original_audio_encoding, args.maybe_original_video_encoding, diff --git a/crates/lib/media/Cargo.toml b/crates/lib/media/Cargo.toml index ed5a46ae05..fc36b15be5 100644 --- a/crates/lib/media/Cargo.toml +++ b/crates/lib/media/Cargo.toml @@ -18,7 +18,8 @@ path = "src/lib.rs" errors = { path = "../errors" } # External -symphonia = "0.5.1" # Powerful audio decoder and demuxer +log = "0.4.14" +symphonia = { version = "0.5.1", features = ["aac", "alac", "mp3", "isomp4"] } # Powerful audio decoder and demuxer [dev-dependencies] # None yet diff --git a/crates/lib/media/src/decode_basic_audio_info.rs b/crates/lib/media/src/decode_basic_audio_info.rs index aa879d3533..2135efd6af 100644 --- a/crates/lib/media/src/decode_basic_audio_info.rs +++ b/crates/lib/media/src/decode_basic_audio_info.rs @@ -69,3 +69,69 @@ pub fn decode_basic_audio_info( codec_name: maybe_codec_name, }) } + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + use errors::AnyhowResult; + use crate::decode_basic_audio_info::decode_basic_audio_info; + + fn test_file(root_file_path: &str) -> PathBuf { + // https://doc.rust-lang.org/cargo/reference/environment-variables.html + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push(format!("../../../{}", root_file_path)); + path + } + + #[test] + fn decode_flac_info() -> AnyhowResult<()> { + let path = test_file("test_data/audio/flac/zelda_ocarina_small_item.flac"); + let bytes = std::fs::read(path)?; + let info = decode_basic_audio_info(&bytes, None, None)?; + assert_eq!(info.codec_name, Some("flac".to_string())); + assert_eq!(info.duration_millis, Some(5120)); + Ok(()) + } + + // NB: Requires symphonia 'aac' and 'isomp4' feature flags + #[test] + fn decode_m4a_alac_info() -> AnyhowResult<()> { + let path = test_file("test_data/audio/m4a/super_mario_bros_lost_life.m4a"); + let bytes = std::fs::read(path)?; + let info = decode_basic_audio_info(&bytes, None, None)?; + assert_eq!(info.codec_name, Some("alac".to_string())); + assert_eq!(info.duration_millis, Some(5493)); + Ok(()) + } + + // NB: Requires symphonia 'mp3' feature flag + #[test] + fn decode_mp3_info() -> AnyhowResult<()> { + let path = test_file("test_data/audio/mp3/super_mario_rpg_beware_the_forests_mushrooms.mp3"); + let bytes = std::fs::read(path)?; + let info = decode_basic_audio_info(&bytes, None, None)?; + assert_eq!(info.codec_name, Some("mp3".to_string())); + assert_eq!(info.duration_millis, Some(15023)); + Ok(()) + } + + #[test] + fn decode_ogg_info() -> AnyhowResult<()> { + let path = test_file("test_data/audio/ogg/banjo-kazooie_jiggy_appearance.ogg"); + let bytes = std::fs::read(path)?; + let info = decode_basic_audio_info(&bytes, None, None)?; + assert_eq!(info.codec_name, Some("vorbis".to_string())); + // assert_eq!(info.duration_millis, Some(5120)); // TODO/FIXME: No duration reported. + Ok(()) + } + + #[test] + fn decode_wav_info_pcm_s16le() -> AnyhowResult<()> { + let path = test_file("test_data/audio/wav/sm64_mario_its_me.wav"); + let bytes = std::fs::read(path)?; + let info = decode_basic_audio_info(&bytes, None, None)?; + assert_eq!(info.codec_name, Some("pcm_s16le".to_string())); + assert_eq!(info.duration_millis, Some(1891)); + Ok(()) + } +} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/media_uploads/upload_audio.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/media_uploads/upload_audio.rs index 1cb2dd7284..955d7be1ad 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/media_uploads/upload_audio.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/media_uploads/upload_audio.rs @@ -172,45 +172,11 @@ pub async fn upload_audio_handler( if let Some(mimetype) = maybe_mimetype.as_deref() { match mimetype { - "audio/x-wav" => { - // FIXME(bt, 2022-12-21): Ugh. - /* - let bytes = bytes.to_vec(); - let reader = Cursor::new(bytes); - let source = ReadOnlySource::new(reader); - let mss = MediaSourceStream::new(Box::new(source), Default::default()); - - // Create a probe hint using the file's extension. [Optional] - let mut hint = Hint::new(); - hint.mime_type(mimetype); - - // Use the default options for metadata and format readers. - let meta_opts: MetadataOptions = Default::default(); - let fmt_opts: FormatOptions = Default::default(); - - // Probe the media source. - let probed = symphonia::default::get_probe() - .format(&hint, mss, &fmt_opts, &meta_opts) - .map_err(|e| { - warn!("Invalid format: {:?}", e); - UploadAudioError::BadInput("bad file".to_string()) - })?; - - let format = probed.format; - - let duration = format.default_track() - .map(|track| { - let timebase= track.codec_params.time_base.unwrap(); - let n_frames = track.codec_params.n_frames.unwrap(); - timebase.calc_time(n_frames) - }) - .unwrap(); - - let duration_millis = duration.seconds * 1000; - let frac_millis = (duration.frac * 1000.0).trunc() as u64; - let duration_millis = duration_millis + frac_millis; - */ - + "video/x-msvideo" => { + // .avi + }, + "audio/x-wav" | "audio/ogg" => { + // .wav, .ogg let basic_info = decode_basic_audio_info(bytes.as_ref(), Some(mimetype), None) .map_err(|e| { warn!("file decoding error: {:?}", e); diff --git a/test_data/audio/flac/zelda_ocarina_small_item.flac b/test_data/audio/flac/zelda_ocarina_small_item.flac new file mode 100644 index 0000000000..87885640cd Binary files /dev/null and b/test_data/audio/flac/zelda_ocarina_small_item.flac differ diff --git a/test_data/audio/m4a/super_mario_bros_lost_life.m4a b/test_data/audio/m4a/super_mario_bros_lost_life.m4a new file mode 100644 index 0000000000..7e736c7eac Binary files /dev/null and b/test_data/audio/m4a/super_mario_bros_lost_life.m4a differ diff --git a/test_data/audio/mp3/super_mario_rpg_beware_the_forests_mushrooms.mp3 b/test_data/audio/mp3/super_mario_rpg_beware_the_forests_mushrooms.mp3 new file mode 100644 index 0000000000..41b9209122 Binary files /dev/null and b/test_data/audio/mp3/super_mario_rpg_beware_the_forests_mushrooms.mp3 differ diff --git a/test_data/audio/ogg/banjo-kazooie_jiggy_appearance.ogg b/test_data/audio/ogg/banjo-kazooie_jiggy_appearance.ogg new file mode 100644 index 0000000000..50a3b5b724 Binary files /dev/null and b/test_data/audio/ogg/banjo-kazooie_jiggy_appearance.ogg differ diff --git a/test_data/audio/wav/sm64_mario_its_me.wav b/test_data/audio/wav/sm64_mario_its_me.wav new file mode 100644 index 0000000000..d5385f85cf Binary files /dev/null and b/test_data/audio/wav/sm64_mario_its_me.wav differ