diff --git a/.gitignore b/.gitignore index e39cff4..1f9377c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target -*.png \ No newline at end of file +*.png +*.webm diff --git a/Cargo.lock b/Cargo.lock index 51e3a5f..5ce9906 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,8 +80,10 @@ dependencies = [ "clap", "clearscreen", "colored", + "ffmpeg-next", "image", "nokhwa", + "video-rs", ] [[package]] @@ -90,7 +92,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -124,6 +126,26 @@ dependencies = [ "which 4.4.2", ] +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.6.0", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + [[package]] name = "bit_field" version = "0.10.2" @@ -512,6 +534,31 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "ffmpeg-next" +version = "7.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfce5242643c8a57b5a44b7e933ccbde85f9508d1379e578e97eee4a9d4334b" +dependencies = [ + "bitflags 2.6.0", + "ffmpeg-sys-next", + "libc", +] + +[[package]] +name = "ffmpeg-sys-next" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "972a460dd8e901b737ce0482bf71a837e1751e3dd7c8f8b0a4ead808e7f174a5" +dependencies = [ + "bindgen 0.69.4", + "cc", + "libc", + "num_cpus", + "pkg-config", + "vcpkg", +] + [[package]] name = "flate2" version = "1.0.30" @@ -565,6 +612,15 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures-core" version = "0.3.30" @@ -631,6 +687,12 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "home" version = "0.5.9" @@ -640,6 +702,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "image" version = "0.24.9" @@ -664,6 +736,15 @@ version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "jobserver" version = "0.1.31" @@ -930,6 +1011,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + [[package]] name = "objc" version = "0.2.7" @@ -967,6 +1058,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "phf" version = "0.11.2" @@ -1025,6 +1122,18 @@ dependencies = [ "syn", ] +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + [[package]] name = "png" version = "0.17.13" @@ -1278,12 +1387,84 @@ dependencies = [ "weezl", ] +[[package]] +name = "tinyvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6b6a2fb3a985e99cebfaefa9faa3024743da73304ca1c683a36429613d3d22" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-normalization" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "utf8parse" version = "0.2.2" @@ -1307,7 +1488,24 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6779878362b9bacadc7893eac76abe69612e8837ef746573c4a5239daf11990b" dependencies = [ - "bindgen", + "bindgen 0.65.1", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "video-rs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d8155306a18fbe217e0d019018311fd4e82bcfe9ceff39638244dffcb0e411" +dependencies = [ + "ffmpeg-next", + "tracing", + "url", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 96dd29f..421fc64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,5 +8,7 @@ atty = "0.2.14" clap = { version = "4.5.8", features = ["derive"] } clearscreen = "3.0.0" colored = "2.1.0" +ffmpeg-next = "7.0.2" image = "0.24.9" -nokhwa = { path = "/home/mike/Software/nokhwa", version = "0.10.4", features = ["input-native"] } +nokhwa = { path = "../../Software/nokhwa", version = "0.10.4", features = ["input-native"] } +video-rs = "0.8.1" diff --git a/src/libs.rs b/src/libs.rs index b372c9a..a4adefb 100644 --- a/src/libs.rs +++ b/src/libs.rs @@ -2,4 +2,5 @@ pub mod args; pub mod image; pub mod ascii; pub mod gen_handler; -pub mod cam_handler; \ No newline at end of file +pub mod cam_handler; +pub mod vid_handler; \ No newline at end of file diff --git a/src/libs/args.rs b/src/libs/args.rs index 648fed7..2d55163 100644 --- a/src/libs/args.rs +++ b/src/libs/args.rs @@ -72,4 +72,33 @@ pub enum Subcommands { #[arg(long, default_value_t = String::from(""))] lightmap: String, }, + + Vid { + #[arg(long, default_value_t = false)] + invert: bool, + + #[arg(long, default_value_t = false)] + colorful: bool, + + #[arg(long, default_value_t = 80)] + width: usize, + + #[arg(long, default_value_t = 25)] + height: usize, + + #[arg(long, default_value_t = 2)] + pixel: usize, + + #[arg(long, default_value_t = false)] + noresize: bool, + + #[arg(long, default_value_t = false)] + matrix: bool, + + #[arg(long, default_value_t = false)] + nofill: bool, + + #[arg(long, default_value_t = String::from(""))] + lightmap: String, + }, } \ No newline at end of file diff --git a/src/libs/cam_handler.rs b/src/libs/cam_handler.rs index a1219ab..8fcd924 100644 --- a/src/libs/cam_handler.rs +++ b/src/libs/cam_handler.rs @@ -6,7 +6,7 @@ use nokhwa::{ Camera, }; -pub fn video( +pub fn camera( invert: bool, colorful: bool, width: usize, diff --git a/src/libs/vid_handler.rs b/src/libs/vid_handler.rs new file mode 100644 index 0000000..ad00b41 --- /dev/null +++ b/src/libs/vid_handler.rs @@ -0,0 +1,110 @@ +use std::time::Duration; + +use crate::libs; +use image::{DynamicImage, ImageBuffer}; + +use ffmpeg_next::format::{input, Pixel}; +use ffmpeg_next::media::Type; +use ffmpeg_next::software::scaling::{context::Context, flag::Flags}; +use ffmpeg_next::util::frame::video::Video; + +pub fn video( + invert: bool, + colorful: bool, + width: usize, + height: usize, + pixel: usize, + noresize: bool, + matrix: bool, + nofill: bool, + lightmap: String, +) { + ffmpeg_next::init().unwrap(); + + if let Ok(mut ictx) = input("bad_apple.webm") { + let input = ictx + .streams() + .best(Type::Video) + .ok_or(ffmpeg_next::Error::StreamNotFound) + .unwrap(); + let video_stream_index = input.index(); + + let context_decoder = + ffmpeg_next::codec::context::Context::from_parameters(input.parameters()).unwrap(); + let mut decoder = context_decoder.decoder().video().unwrap(); + + let mut scaler = Context::get( + decoder.format(), + decoder.width(), + decoder.height(), + Pixel::RGB24, + decoder.width(), + decoder.height(), + Flags::BILINEAR, + ) + .unwrap(); + + let frame_rate = input.avg_frame_rate(); + let frame_duration = Duration::from_secs_f64(frame_rate.denominator() as f64 / frame_rate.numerator() as f64); + + let mut frame_index = 0; + + let mut receive_and_process_decoded_frames = + |decoder: &mut ffmpeg_next::decoder::Video| -> Result<(), ffmpeg_next::Error> { + let mut decoded = Video::empty(); + while decoder.receive_frame(&mut decoded).is_ok() { + let start = std::time::Instant::now(); + + let mut rgb_frame = Video::empty(); + scaler.run(&decoded, &mut rgb_frame)?; + + let vwidth = rgb_frame.width(); + let vheight = rgb_frame.height(); + + let data = rgb_frame.data(0); + + let buf = ImageBuffer::from_raw(vwidth, vheight, data.to_vec()).unwrap(); + + let mut img = DynamicImage::ImageRgb8(buf); + + if !noresize && !nofill { + img = libs::image::resize_image(&img, width, height); + eprintln!("Image resized"); + libs::image::print_size(&img); + } + + if !noresize && nofill { + img = libs::image::resize_image_no_fill(&img, width, height); + eprintln!("Image resized exact"); + libs::image::print_size(&img); + } + + let ascii_img = + libs::ascii::to_ascii(&img, &lightmap, pixel, invert, colorful, matrix); + eprintln!("ASCII image created"); + + clearscreen::clear().expect("Failed to clear screen"); + print!("{ascii_img}"); + + frame_index += 1; + + let elapsed = start.elapsed(); + if elapsed < frame_duration { + std::thread::sleep(frame_duration - elapsed); + } + } + + Ok(()) + }; + + for (stream, packet) in ictx.packets() { + if stream.index() == video_stream_index { + decoder.send_packet(&packet).unwrap(); + receive_and_process_decoded_frames(&mut decoder).unwrap(); + } + } + + decoder.send_eof().unwrap(); + receive_and_process_decoded_frames(&mut decoder).unwrap(); + } +} diff --git a/src/main.rs b/src/main.rs index 89bc839..0012e65 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,12 +37,27 @@ fn main() { nofill, lightmap, }) => { - libs::cam_handler::video( + libs::cam_handler::camera( + invert, colorful, width, height, pixel, noresize, matrix, nofill, lightmap, + ); + } + Some(libs::args::Subcommands::Vid { + invert, + colorful, + width, + height, + pixel, + noresize, + matrix, + nofill, + lightmap, + }) => { + libs::vid_handler::video( invert, colorful, width, height, pixel, noresize, matrix, nofill, lightmap, ); } None => { - eprintln!("No subcommand provided. Available subcommands: `gen`, `cam`"); + eprintln!("No subcommand provided. Available subcommands: `gen`, `cam`, and `vid`"); std::process::exit(1); } }