first commit

This commit is contained in:
xin
2024-09-27 16:04:51 +08:00
commit 4eb295d578
3 changed files with 295 additions and 0 deletions

24
Cargo.toml Normal file
View File

@ -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"

0
readme.md Normal file
View File

271
src/main.rs Normal file
View File

@ -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<f32>,
}
lazy_static! {
static ref FRAME_INDEX_TOTAL :Mutex<usize> = 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::<f32>() * 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<GrayImage> {
let mut frames = Vec::new();
let frame_size = WIDTH * HEIGHT * std::mem::size_of::<f32>();
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::<f32>()) {
let mut bytes = [0u8; std::mem::size_of::<f32>()];
bytes.copy_from_slice(&chunk[i * std::mem::size_of::<f32>()..(i + 1) * std::mem::size_of::<f32>()]);
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 <your.email@example.com>")
.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::<String>("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<u32> = 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
}
}