From d00866a795d38a0a0106ad00063ea79d05f381ca Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski <janne@kuschku.de> Date: Tue, 10 Mar 2020 23:59:27 +0100 Subject: [PATCH] reorganize code to allow for easier testing --- Cargo.lock | 48 ++++++++++++++----- Cargo.toml | 8 ++-- lib/ffmpeg_api/Cargo.toml | 16 +++++++ {src/ffmpeg_api => lib/ffmpeg_api/src}/api.rs | 5 +- .../ffmpeg_api/src}/enums.rs | 0 lib/ffmpeg_api/src/lib.rs | 5 ++ lib/ffmpeg_api/src/tests.rs | 4 ++ lib/media_time/Cargo.toml | 11 +++++ lib/media_time/src/errors.rs | 17 +++++++ lib/media_time/src/lib.rs | 8 ++++ .../util => lib/media_time/src}/media_time.rs | 14 +++--- lib/media_time/src/tests.rs | 4 ++ lib/webvtt/Cargo.toml | 10 ++++ lib/webvtt/src/lib.rs | 6 +++ lib/webvtt/src/tests.rs | 4 ++ {src/util => lib/webvtt/src}/webvtt.rs | 8 ++-- src/ffmpeg_api/mod.rs | 2 - src/ingest/mod.rs | 2 - src/{ingest/extract.rs => lib.rs} | 20 ++++---- src/main.rs | 15 ++---- src/{ingest => }/spritesheet.rs | 4 +- src/{util => }/stream_metadata.rs | 2 +- src/util/mod.rs | 3 -- 23 files changed, 157 insertions(+), 59 deletions(-) create mode 100644 lib/ffmpeg_api/Cargo.toml rename {src/ffmpeg_api => lib/ffmpeg_api/src}/api.rs (98%) rename {src/ffmpeg_api => lib/ffmpeg_api/src}/enums.rs (100%) create mode 100644 lib/ffmpeg_api/src/lib.rs create mode 100644 lib/ffmpeg_api/src/tests.rs create mode 100644 lib/media_time/Cargo.toml create mode 100644 lib/media_time/src/errors.rs create mode 100644 lib/media_time/src/lib.rs rename {src/util => lib/media_time/src}/media_time.rs (83%) create mode 100644 lib/media_time/src/tests.rs create mode 100644 lib/webvtt/Cargo.toml create mode 100644 lib/webvtt/src/lib.rs create mode 100644 lib/webvtt/src/tests.rs rename {src/util => lib/webvtt/src}/webvtt.rs (83%) delete mode 100644 src/ffmpeg_api/mod.rs delete mode 100644 src/ingest/mod.rs rename src/{ingest/extract.rs => lib.rs} (93%) rename src/{ingest => }/spritesheet.rs (98%) rename src/{util => }/stream_metadata.rs (96%) delete mode 100644 src/util/mod.rs diff --git a/Cargo.lock b/Cargo.lock index fc4140e..a31f669 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,9 +8,9 @@ checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" [[package]] name = "aho-corasick" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743ad5a418686aad3b87fd14c43badd828cf26e214a00f92a384291cf22e1811" +checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" dependencies = [ "memchr", ] @@ -287,6 +287,18 @@ dependencies = [ "tar", ] +[[package]] +name = "ffmpeg_api" +version = "0.1.0" +dependencies = [ + "bitflags", + "enum_primitive", + "failure", + "ffmpeg-dev", + "fraction", + "media_time", +] + [[package]] name = "filetime" version = "0.2.8" @@ -454,23 +466,30 @@ checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" name = "media-ingestion" version = "0.1.0" dependencies = [ - "bitflags", - "enum_primitive", "failure", - "ffmpeg-dev", + "ffmpeg_api", "fraction", "image", + "media_time", "serde", "serde_json", "structopt", + "webvtt", +] + +[[package]] +name = "media_time" +version = "0.1.0" +dependencies = [ + "fraction", "time", ] [[package]] name = "memchr" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" [[package]] name = "memoffset" @@ -719,9 +738,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.14" +version = "0.6.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06" +checksum = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1" [[package]] name = "rustc-demangle" @@ -1009,11 +1028,18 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" +[[package]] +name = "webvtt" +version = "0.1.0" +dependencies = [ + "media_time", +] + [[package]] name = "which" -version = "3.1.0" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5475d47078209a02e60614f7ba5e645ef3ed60f771920ac1906d7c1cc65024c8" +checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" dependencies = [ "libc", ] diff --git a/Cargo.toml b/Cargo.toml index b446c7f..38df703 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,13 +17,13 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bitflags = "1.2.1" -enum_primitive = "0.1.1" failure = "0.1.6" -ffmpeg-dev = "0.3.8" fraction = "0.6.2" image = "0.23.0" serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } structopt = "0.3" -time = "0.2" + +ffmpeg_api = { path = "lib/ffmpeg_api" } +media_time = { path = "lib/media_time" } +webvtt = { path = "lib/webvtt" } \ No newline at end of file diff --git a/lib/ffmpeg_api/Cargo.toml b/lib/ffmpeg_api/Cargo.toml new file mode 100644 index 0000000..ae7ec40 --- /dev/null +++ b/lib/ffmpeg_api/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "ffmpeg_api" +version = "0.1.0" +authors = ["Janne Mareike Koschinski <janne@kuschku.de>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bitflags = "1.2.1" +enum_primitive = "0.1.1" +failure = "0.1.6" +ffmpeg-dev = "0.3.8" +fraction = "0.6.2" + +media_time = { path = "../media_time" } \ No newline at end of file diff --git a/src/ffmpeg_api/api.rs b/lib/ffmpeg_api/src/api.rs similarity index 98% rename from src/ffmpeg_api/api.rs rename to lib/ffmpeg_api/src/api.rs index d2a9731..d84f9bc 100644 --- a/src/ffmpeg_api/api.rs +++ b/lib/ffmpeg_api/src/api.rs @@ -6,8 +6,7 @@ use failure::{bail, format_err, Error}; use ffmpeg_dev::sys as ffi; use fraction::Fraction; -use crate::ffmpeg_api::enums::*; -use crate::util::media_time; +use crate::enums::*; fn native_string(ptr: *const std::os::raw::c_char) -> Result<String, Error> { if ptr.is_null() { @@ -96,6 +95,7 @@ impl AVFormatContext { unsafe { (*self.base).duration }, Fraction::new(1 as u64, ffi::AV_TIME_BASE as u64), ) + .map_err(|err| format_err!("error extracting duration: {}", err)) } } @@ -343,6 +343,7 @@ impl<'a> AVStream<'a> { pub fn timestamp(&self, timestamp: i64) -> Result<media_time::MediaTime, Error> { media_time::MediaTime::from_rational(timestamp, self.time_base()) + .map_err(|err| format_err!("error extracting timestamp: {}", err)) } pub fn duration(&self) -> Result<media_time::MediaTime, Error> { diff --git a/src/ffmpeg_api/enums.rs b/lib/ffmpeg_api/src/enums.rs similarity index 100% rename from src/ffmpeg_api/enums.rs rename to lib/ffmpeg_api/src/enums.rs diff --git a/lib/ffmpeg_api/src/lib.rs b/lib/ffmpeg_api/src/lib.rs new file mode 100644 index 0000000..6efd89a --- /dev/null +++ b/lib/ffmpeg_api/src/lib.rs @@ -0,0 +1,5 @@ +#[cfg(test)] +mod tests; + +pub mod api; +pub mod enums; diff --git a/lib/ffmpeg_api/src/tests.rs b/lib/ffmpeg_api/src/tests.rs new file mode 100644 index 0000000..2837a57 --- /dev/null +++ b/lib/ffmpeg_api/src/tests.rs @@ -0,0 +1,4 @@ +#[test] +fn it_works() { + assert_eq!(2 + 2, 4); +} diff --git a/lib/media_time/Cargo.toml b/lib/media_time/Cargo.toml new file mode 100644 index 0000000..91c4c12 --- /dev/null +++ b/lib/media_time/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "media_time" +version = "0.1.0" +authors = ["Janne Mareike Koschinski <janne@kuschku.de>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +fraction = "0.6.2" +time = "0.2" \ No newline at end of file diff --git a/lib/media_time/src/errors.rs b/lib/media_time/src/errors.rs new file mode 100644 index 0000000..287ad3d --- /dev/null +++ b/lib/media_time/src/errors.rs @@ -0,0 +1,17 @@ +use std::error; +use std::fmt; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct TimeBaseError; + +impl fmt::Display for TimeBaseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "invalid time base") + } +} + +impl error::Error for TimeBaseError { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { + None + } +} diff --git a/lib/media_time/src/lib.rs b/lib/media_time/src/lib.rs new file mode 100644 index 0000000..9ddca6f --- /dev/null +++ b/lib/media_time/src/lib.rs @@ -0,0 +1,8 @@ +#[cfg(test)] +mod tests; + +mod errors; +mod media_time; + +pub use errors::*; +pub use media_time::MediaTime; diff --git a/src/util/media_time.rs b/lib/media_time/src/media_time.rs similarity index 83% rename from src/util/media_time.rs rename to lib/media_time/src/media_time.rs index 00c5b11..9d20231 100644 --- a/src/util/media_time.rs +++ b/lib/media_time/src/media_time.rs @@ -1,17 +1,15 @@ -use failure::{format_err, Error}; +use crate::errors::TimeBaseError; use fraction::Fraction; +type Result<T> = std::result::Result<T, TimeBaseError>; + #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] pub struct MediaTime(time::Duration); impl MediaTime { - pub fn from_rational(timestamp: i64, base: Fraction) -> Result<MediaTime, Error> { - let num: u64 = *base - .numer() - .ok_or_else(|| format_err!("time base of unusable format"))?; - let den: u64 = *base - .denom() - .ok_or_else(|| format_err!("time base of unusable format"))?; + pub fn from_rational(timestamp: i64, base: Fraction) -> Result<MediaTime> { + let num: u64 = *base.numer().ok_or(TimeBaseError)?; + let den: u64 = *base.denom().ok_or(TimeBaseError)?; Ok(MediaTime(time::Duration::milliseconds( (1000 * timestamp as i128 * num as i128 / den as i128) as i64, diff --git a/lib/media_time/src/tests.rs b/lib/media_time/src/tests.rs new file mode 100644 index 0000000..2837a57 --- /dev/null +++ b/lib/media_time/src/tests.rs @@ -0,0 +1,4 @@ +#[test] +fn it_works() { + assert_eq!(2 + 2, 4); +} diff --git a/lib/webvtt/Cargo.toml b/lib/webvtt/Cargo.toml new file mode 100644 index 0000000..c665a7e --- /dev/null +++ b/lib/webvtt/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "webvtt" +version = "0.1.0" +authors = ["Janne Mareike Koschinski <janne@kuschku.de>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +media_time = { path = "../media_time" } \ No newline at end of file diff --git a/lib/webvtt/src/lib.rs b/lib/webvtt/src/lib.rs new file mode 100644 index 0000000..94a3747 --- /dev/null +++ b/lib/webvtt/src/lib.rs @@ -0,0 +1,6 @@ +#[cfg(test)] +mod tests; + +mod webvtt; + +pub use webvtt::{WebVTTCue, WebVTTFile}; diff --git a/lib/webvtt/src/tests.rs b/lib/webvtt/src/tests.rs new file mode 100644 index 0000000..2837a57 --- /dev/null +++ b/lib/webvtt/src/tests.rs @@ -0,0 +1,4 @@ +#[test] +fn it_works() { + assert_eq!(2 + 2, 4); +} diff --git a/src/util/webvtt.rs b/lib/webvtt/src/webvtt.rs similarity index 83% rename from src/util/webvtt.rs rename to lib/webvtt/src/webvtt.rs index 7a6788e..a1e8d0c 100644 --- a/src/util/webvtt.rs +++ b/lib/webvtt/src/webvtt.rs @@ -4,7 +4,9 @@ use std::io::LineWriter; use std::path::Path; use std::string::String; -use crate::util::media_time::MediaTime; +use media_time::MediaTime; + +type Result<T> = std::result::Result<T, std::io::Error>; pub struct WebVTTFile { cues: Vec<WebVTTCue>, @@ -25,7 +27,7 @@ impl WebVTTFile { self.cues.push(cue); } - pub fn save(&self, path: impl AsRef<Path>) -> Result<(), std::io::Error> { + pub fn save(&self, path: impl AsRef<Path>) -> Result<()> { let file = File::create(path)?; let mut file = LineWriter::new(file); file.write_all(b"WEBVTT\n\n")?; @@ -46,7 +48,7 @@ impl WebVTTCue { } } - fn save(&self, writer: &mut LineWriter<File>) -> Result<(), std::io::Error> { + fn save(&self, writer: &mut LineWriter<File>) -> Result<()> { writer.write_all(format!("{} --> {}\n", self.start, self.end).as_bytes())?; writer.write_all(self.payload.as_bytes())?; writer.write_all(b"\n\n")?; diff --git a/src/ffmpeg_api/mod.rs b/src/ffmpeg_api/mod.rs deleted file mode 100644 index 30b930b..0000000 --- a/src/ffmpeg_api/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub(crate) mod api; -pub(crate) mod enums; diff --git a/src/ingest/mod.rs b/src/ingest/mod.rs deleted file mode 100644 index 1d0c622..0000000 --- a/src/ingest/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub(crate) mod extract; -pub(crate) mod spritesheet; diff --git a/src/ingest/extract.rs b/src/lib.rs similarity index 93% rename from src/ingest/extract.rs rename to src/lib.rs index a633087..e86963f 100644 --- a/src/ingest/extract.rs +++ b/src/lib.rs @@ -1,19 +1,19 @@ +#![allow(dead_code)] +pub mod spritesheet; +pub mod stream_metadata; + use std::path::Path; use failure::{format_err, Error}; +use ffmpeg_api::api::*; +use ffmpeg_api::enums::*; use image::ImageOutputFormat; -use crate::ffmpeg_api::api::*; -use crate::ffmpeg_api::enums::*; -use crate::ingest::spritesheet::*; -use crate::util::media_time::*; -use crate::util::stream_metadata::*; - pub fn extract( max_size: u32, num_horizontal: u32, num_vertical: u32, - frame_interval: MediaTime, + frame_interval: media_time::MediaTime, input_file: &Path, output_folder: &Path, format: ImageOutputFormat, @@ -29,7 +29,7 @@ pub fn extract( let spritesheet_path = output_folder.join("spritesheets"); std::fs::create_dir_all(&spritesheet_path)?; - let mut spritesheet_manager = SpritesheetManager::new( + let mut spritesheet_manager = spritesheet::SpritesheetManager::new( max_size, num_horizontal, num_vertical, @@ -63,7 +63,7 @@ pub fn extract( local_codec.name()? ); - let mut metadata = StreamMetadata::new( + let mut metadata = stream_metadata::StreamMetadata::new( avformat_context .input_format()? .determine_mime(local_codec.name()?)?, @@ -98,7 +98,7 @@ pub fn extract( .in_packet(&mut packet) .map_err(|error| format_err!("Could not load packet: {}", error))?; while codec_context.out_frame(&mut frame).is_ok() { - let timestamp = MediaTime::from_rational(frame.pts(), time_base)?; + let timestamp = media_time::MediaTime::from_rational(frame.pts(), time_base)?; println!( "Frame {}: {} @ {}", diff --git a/src/main.rs b/src/main.rs index 3c99314..448acb1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,10 @@ -#![allow(dead_code)] - -pub(crate) mod ffmpeg_api; -pub(crate) mod ingest; -pub(crate) mod util; - use std::path::Path; use failure::Error; -use structopt::StructOpt; - -use crate::ffmpeg_api::enums::{SwsFlags, SwsScaler}; -use crate::util::media_time::MediaTime; +use ffmpeg_api::enums::{SwsFlags, SwsScaler}; use image::ImageOutputFormat; +use media_time::MediaTime; +use structopt::StructOpt; fn parse_scaler(src: &str) -> Result<SwsScaler, String> { match src { @@ -71,7 +64,7 @@ fn main() -> Result<(), Error> { flags |= SwsFlags::BIT_EXACT_SCALING; } - ingest::extract::extract( + media_ingestion::extract( options.max_size, options.num_horizontal, options.num_vertical, diff --git a/src/ingest/spritesheet.rs b/src/spritesheet.rs similarity index 98% rename from src/ingest/spritesheet.rs rename to src/spritesheet.rs index de26266..dc69f6d 100644 --- a/src/ingest/spritesheet.rs +++ b/src/spritesheet.rs @@ -5,8 +5,8 @@ use std::path::PathBuf; use failure::{bail, format_err, Error}; use image::{DynamicImage, ImageOutputFormat, RgbImage}; -use crate::util::media_time::MediaTime; -use crate::util::webvtt::{WebVTTCue, WebVTTFile}; +use media_time::MediaTime; +use webvtt::{WebVTTCue, WebVTTFile}; pub enum ImageFormat { Jpeg(i32), diff --git a/src/util/stream_metadata.rs b/src/stream_metadata.rs similarity index 96% rename from src/util/stream_metadata.rs rename to src/stream_metadata.rs index 9120347..b4fdc88 100644 --- a/src/util/stream_metadata.rs +++ b/src/stream_metadata.rs @@ -4,7 +4,7 @@ use std::path::Path; use serde::{Deserialize, Serialize}; -use crate::util::media_time::MediaTime; +use media_time::MediaTime; #[derive(Serialize, Deserialize)] pub struct StreamMetadata { diff --git a/src/util/mod.rs b/src/util/mod.rs deleted file mode 100644 index 28d1532..0000000 --- a/src/util/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub(crate) mod media_time; -pub(crate) mod stream_metadata; -pub(crate) mod webvtt; -- GitLab