diff --git a/Cargo.lock b/Cargo.lock index fc4140e1fcaa11658a1a81d720942bf3969b5e98..a31f669925720514c2c3c0ea1e89230d34f4de66 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 b446c7f895ef390a9f5ff6ab3dff94fc2f4ccecd..38df703da71f759d5db561758669a8b2bf380a75 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 0000000000000000000000000000000000000000..ae7ec40f0f2b438ca24657d4ffb20af1e6e14f80 --- /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 d2a973197abc4f4de4a7b71aaacd97f4ab7a69cd..d84f9bc500588e5a3147ce8689065549a889ff54 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 0000000000000000000000000000000000000000..6efd89a29f7e7d50aa7e6c494c50142b72e41d4b --- /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 0000000000000000000000000000000000000000..2837a577fe20f37959959d278a283897be0e225b --- /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 0000000000000000000000000000000000000000..91c4c1230900d2931a6c2a6d047727dd8e2ded8a --- /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 0000000000000000000000000000000000000000..287ad3d95eda7a2323771849d331b494525774d7 --- /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 0000000000000000000000000000000000000000..9ddca6fa709695ea71fa5bf130e465fa87ba72c8 --- /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 00c5b11844700d0bb5487b4107f302c5a0bd375b..9d20231e56bfa179ab9951acad9be6c1ccc70376 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 0000000000000000000000000000000000000000..2837a577fe20f37959959d278a283897be0e225b --- /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 0000000000000000000000000000000000000000..c665a7e6f63d8a0d449bffc22f9027d61a864766 --- /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 0000000000000000000000000000000000000000..94a374736817c3aebfa9ba45d821c73a024d0bdd --- /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 0000000000000000000000000000000000000000..2837a577fe20f37959959d278a283897be0e225b --- /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 7a6788e1abe9e70a1ebdc667c07bcd71d32da575..a1e8d0c9aedaf99eb7259bfaa89add0397e70e44 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 30b930b94a7ea861e6813291c8134aabfd3c8783..0000000000000000000000000000000000000000 --- 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 1d0c62218e8f52bd435f328fd5398427a72f138d..0000000000000000000000000000000000000000 --- 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 a633087d81c456a3ae83252eb1ebc1d585ad5e7d..e86963fa641ee114fb3658646c7c93939ea2f0db 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 3c993147eee1430c096f74878febf73f366624e5..448acb1562133cf036658f7af3845106b5fbc5e1 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 de262661247f96d24f9de2c8923fc94561468f26..dc69f6df38d43936176cadd1a6c971ec0f99fd69 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 912034727fe86d9a2921530434be246d6367866a..b4fdc88d275e3730862e169a4eecfd48c7c888f0 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 28d153216d4d47d35355a14b65f6104ee04866b5..0000000000000000000000000000000000000000 --- 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;