From 4eb295d578aabc19efa6adeaa5193dc9a5b11986 Mon Sep 17 00:00:00 2001 From: xin Date: Fri, 27 Sep 2024 16:04:51 +0800 Subject: [PATCH] first commit --- Cargo.toml | 24 +++++ readme.md | 0 src/main.rs | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 295 insertions(+) create mode 100644 Cargo.toml create mode 100644 readme.md create mode 100644 src/main.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..763be83 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "video_player" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + + + +[dependencies] + +minifb = "0.26.0" +rand = "0.8.5" +clap = { version = "3", features = ["derive"] } +bincode = "1.3.3" +serde = { version = "1.0.136", features = ["derive"] } +imageproc = "0.25.0" +image = "0.25.2" +rusttype = "0.9.3" +ab_glyph = "0.2.28" +lazy_static = "1.5.0" +little_exif = "0.4.1" +chrono = "0.4.38" + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..e69de29 diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..21dbb76 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,271 @@ +extern crate image; +extern crate minifb; +extern crate rand; +extern crate clap; +extern crate bincode; +extern crate serde; +use little_exif::metadata::Metadata; +use little_exif::exif_tag::ExifTag; +use clap::{App, Arg}; +use std::sync::Mutex; +use image::{GrayImage, Luma}; +use minifb::{Key, Window, WindowOptions}; +use rand::Rng; +use serde::de::value; +use serde::{Deserialize, Serialize}; +use std::fs::File; +use std::io::{Read, Seek, SeekFrom}; +use std::thread; +use std::time::Duration; +use imageproc::drawing::draw_text_mut; +use ab_glyph::{FontArc,PxScale}; +use rusttype::{Font,Scale}; +use lazy_static::lazy_static; +const WIDTH: usize = 640; +const HEIGHT: usize = 512; +const BATCH_SIZE: usize = 1; // 每次加载的帧数 +const FRAME_RATE: u64 = 100; // 帧率 + +#[derive(Serialize, Deserialize)] +struct FrameData { + pixels: Vec, +} + +lazy_static! { + static ref FRAME_INDEX_TOTAL :Mutex = Mutex::new(0); +} + +fn generate_random_frame() -> GrayImage { + let mut img = GrayImage::new(WIDTH as u32, HEIGHT as u32); + let mut rng = rand::thread_rng(); + + for x in 0..WIDTH { + for y in 0..HEIGHT { + let pixel_value = (rng.gen::() * 255.0) as u8; + img.put_pixel(x as u32, y as u32, Luma([pixel_value])); + } + } + + img +} +fn save_frame_as_jpg(frame: &GrayImage, frame_index: usize,gpstimestamp:f64) { + let filename = format!("frame_{}.jpg", frame_index); + + + + frame.save(&filename).expect("保存帧失败"); + + // let _file = File::open(&filename).expect("打开文件失败"); + + // let file = File::open(filename.clone()).unwrap(); + thread::sleep(Duration::from_millis(100)); + + let image_path=std::path::Path::new(&filename); + println!("filename:{:?}",image_path); + + let mut metadata = Metadata::new_from_path(&image_path).unwrap(); + metadata.set_tag( + ExifTag::ImageDescription("Hello World!".to_string()) + + ); + + if gpstimestamp < 2721994000000.0 + { + let gpstimestamp=gpstimestamp-421206.0*1000.0; //-8.0*60.0*60.0*1000.0 + //用 chrono 将gps stamp 转换成 yyyy:MM:dd HH:mm:ss + let gps_time = chrono::NaiveDateTime::from_timestamp((gpstimestamp/1000.0) as i64, 0); + let gps_time_str = gps_time.format("%Y:%m:%d %H:%M:%S").to_string(); + println!("gps_time_str:{}",gps_time_str); + + + metadata.set_tag( + ExifTag::DateTimeOriginal(gps_time_str) + ); + + } + + + metadata.write_to_file(&image_path); + +} + + + +use std::mem; +use std::sync::Arc; +const font_path: &str = "C:\\Windows\\Fonts\\arial.ttf"; +fn load_frames_from_file(file: &mut File, start_frame: usize, batch_size: usize) -> Vec { + let mut frames = Vec::new(); + let frame_size = WIDTH * HEIGHT * std::mem::size_of::(); + let offset = start_frame * (frame_size+8); + file.seek(SeekFrom::Start(offset as u64)).expect("Failed to seek file"); + // // 加载字体 + // let font = Vec::from(include_bytes!("C:\\Windows\\Fonts\\arial.ttf") as &[u8]); + // let font = Font::try_from_vec(font).unwrap(); + // 加载 Windows 系统字体 + + let mut font_data = Vec::new(); + let mut file1 = File::open(font_path).unwrap(); + file1.read_to_end(&mut font_data).unwrap(); + let ab_glyph_font = FontArc::try_from_vec(font_data).unwrap(); + // 使用 Font 而不是 FontArc + // let font = Font::try_from_vec(font_data).unwrap(); + + + let scale = PxScale { x: 20.0, y: 20.0 }; + + + + let mut buffer = vec![0u8; (frame_size+8) * batch_size]; + let bytes_read = file.read(&mut buffer).expect("Failed to read file"); + let mut isfirst = true; + for chunk in buffer.chunks_exact(frame_size+8).take(bytes_read / (frame_size+8)) { + let mut img = GrayImage::new(WIDTH as u32, HEIGHT as u32); + let mut bytesd = [0u8; 8]; + bytesd.copy_from_slice(&chunk[0..8]); + let value1 :f64= unsafe { mem::transmute(bytesd) }; + println!("value1:{}",value1); + + // if value < 1721994000000.0 + // {continue;} + + + + // println!("times:{}",value); + for i in 2..(frame_size / std::mem::size_of::()) { + let mut bytes = [0u8; std::mem::size_of::()]; + bytes.copy_from_slice(&chunk[i * std::mem::size_of::()..(i + 1) * std::mem::size_of::()]); + + let value: f32 = unsafe { mem::transmute(bytes) }; + let x = (i % WIDTH) as u32; + let y = (i / WIDTH) as u32; + let pixel_value = (value/50.0 * 255.0) as u8; + img.put_pixel(x, y, Luma([pixel_value])); + + } + let mut frame_index_total = FRAME_INDEX_TOTAL.lock().unwrap(); + + if *frame_index_total % 20 == 0 + { + save_frame_as_jpg(&img, *frame_index_total,value1); + } + + let text = format!("{:.2}", value1); + draw_text_mut(&mut img, Luma([0]), 10, 10, scale, &ab_glyph_font, &text); + *frame_index_total=*frame_index_total+1; + frames.push(img); + + } + + frames +} + +fn main() { + let matches = App::new("Video Player") + .version("1.0") + .author("Your Name ") + .about("Plays a video from a file or generates random frames") + .arg( + Arg::new("file") + .short('f') + .long("file") + .value_name("FILE") + .help("Path to the data file") // 原本的.about 应该是 .help + .action(clap::ArgAction::Set) // 新的处理值的方式 + ) + .get_matches(); + + let file_path = matches.get_one::("file"); // 使用 get_one 获取值 + // let file_path = Some("D:/01Hydata/20240923rehongwai/ML/2024_09_28_03_42_45.temperature"); + println!("File path provided: {:?}", file_path); + + let mut window = Window::new( + "Video Player - ESC to exit", + WIDTH, + HEIGHT, + WindowOptions::default(), + ) + .unwrap_or_else(|e| { + panic!("{}", e); + }); + + let mut current_frame = 0; + let mut batch_start_frame = 0; + let mut frames = Vec::new(); + let mut paused = false; + if let Some(path) = file_path { + let mut file = File::open(path).expect("Failed to open file"); + frames = load_frames_from_file(&mut file, batch_start_frame, BATCH_SIZE); + } else { + frames = (0..BATCH_SIZE).map(|_| generate_random_frame()).collect(); + } + + while window.is_open() && !window.is_key_down(Key::Escape) { + // 检查暂停/继续 + window.update(); + if window.is_key_down(Key::Space) { + paused = !paused; + std::thread::sleep(std::time::Duration::from_millis(100)); + window.update(); + // 防止快速切换状态 + while window.is_key_down(Key::Space) + { + println!("Space key down"); + std::thread::sleep(std::time::Duration::from_millis(100)); + window.update(); + } + } + //快进 + if window.is_key_down(Key::Right) { + current_frame += BATCH_SIZE; + std::thread::sleep(std::time::Duration::from_millis(100)); + window.update(); + // 防止快速切换状态 + // while window.is_key_down(Key::Right) + // { + // println!("Right key down"); + // std::thread::sleep(std::time::Duration::from_millis(100)); + // window.update(); + // } + } + + + + + + + if paused { + std::thread::sleep(std::time::Duration::from_millis(100)); + + continue; + } + + if current_frame >= frames.len() { + if let Some(path) = file_path { + let mut file = File::open(path).expect("Failed to open file"); + batch_start_frame += BATCH_SIZE; + frames = load_frames_from_file(&mut file, batch_start_frame, BATCH_SIZE); + current_frame = 0; + if frames.is_empty() { + break; // 文件已读取完 + } + } else { + current_frame = 0; + } + } + + let frame = &frames[current_frame]; + let buffer: Vec = frame + .pixels() + .map(|p| { + let gray = p[0] as u32; + (gray << 16) | (gray << 8) | gray + }) + .collect(); + + window.update_with_buffer(&buffer, WIDTH, HEIGHT).unwrap(); + + current_frame += 1; + // std::thread::sleep(std::time::Duration::from_millis(1000 / FRAME_RATE)); // ~30 FPS + } +}