servicepoint-cli/src/pixels.rs
Vinzenz Schroeter 3c57191c70
Some checks failed
Rust / build (pull_request) Failing after 2m5s
stream video file
2025-05-04 17:21:05 +02:00

158 lines
5.4 KiB
Rust

use crate::{
cli::{ImageProcessingOptions, PixelCommand, SendImageOptions},
image_processing::ImageProcessingPipeline,
stream_window::stream_window,
transport::Transport,
};
use ffmpeg_next as ffmpeg;
use image::{DynamicImage, RgbImage};
use log::info;
use servicepoint::{
BinaryOperation, BitVecCommand, BitmapCommand, ClearCommand, CompressionCode, DisplayBitVec,
Origin, PIXEL_COUNT,
};
pub(crate) fn pixels(connection: &Transport, pixel_command: PixelCommand) {
match pixel_command {
PixelCommand::Off => pixels_off(connection),
PixelCommand::Flip => pixels_invert(connection),
PixelCommand::On => pixels_on(connection),
PixelCommand::Image {
image_processing_options: processing_options,
send_image_options: image_options,
} => pixels_image(connection, image_options, processing_options),
PixelCommand::Screen {
stream_options,
image_processing,
} => stream_window(connection, stream_options, image_processing),
PixelCommand::Video {
image_processing_options: processing_options,
send_image_options: image_options,
} => pixels_video(connection, image_options, processing_options),
}
}
fn pixels_on(connection: &Transport) {
let mask = DisplayBitVec::repeat(true, PIXEL_COUNT);
let command = BitVecCommand {
offset: 0,
bitvec: mask,
compression: CompressionCode::Lzma,
operation: BinaryOperation::Overwrite,
};
connection
.send_command(command)
.expect("could not send command");
info!("turned on all pixels")
}
fn pixels_invert(connection: &Transport) {
let mask = DisplayBitVec::repeat(true, PIXEL_COUNT);
let command = BitVecCommand {
offset: 0,
bitvec: mask,
compression: CompressionCode::Lzma,
operation: BinaryOperation::Xor,
};
connection
.send_command(command)
.expect("could not send command");
info!("inverted all pixels");
}
pub(crate) fn pixels_off(connection: &Transport) {
connection
.send_command(ClearCommand)
.expect("failed to clear pixels");
info!("reset pixels");
}
fn pixels_image(
connection: &Transport,
options: SendImageOptions,
processing_options: ImageProcessingOptions,
) {
let image = image::open(&options.file_name).expect("failed to open image file");
let mut pipeline = ImageProcessingPipeline::new(processing_options);
let bitmap = pipeline.process(image);
connection
.send_command(BitmapCommand {
origin: Origin::ZERO,
bitmap,
compression: CompressionCode::default(),
})
.expect("failed to send image command");
info!("sent image to display");
}
fn pixels_video(
connection: &Transport,
options: SendImageOptions,
processing_options: ImageProcessingOptions,
) {
ffmpeg::init().unwrap();
let mut ictx = ffmpeg::format::input(&options.file_name).expect("failed to open video input file");
let input = ictx
.streams()
.best(ffmpeg::media::Type::Video)
.ok_or(ffmpeg::Error::StreamNotFound)
.expect("could not get video stream from input file");
let video_stream_index = input.index();
let context_decoder = ffmpeg::codec::context::Context::from_parameters(input.parameters())
.expect("could not extract video context from parameters");
let mut decoder = context_decoder.decoder().video()
.expect("failed to create decoder for video stream");
let src_width = decoder.width();
let src_height = decoder.height();
let mut scaler = ffmpeg::software::scaling::Context::get(
decoder.format(),
src_width,
src_height,
ffmpeg::format::Pixel::RGB24,
src_width,
src_height,
ffmpeg::software::scaling::Flags::BILINEAR,
).expect("failed to create scaling context");
let mut frame_index = 0;
let mut processing_pipeline = ImageProcessingPipeline::new(processing_options);
let mut receive_and_process_decoded_frames =
|decoder: &mut ffmpeg::decoder::Video| -> Result<(), ffmpeg::Error> {
let mut decoded = ffmpeg::util::frame::video::Video::empty();
let mut rgb_frame = ffmpeg::util::frame::video::Video::empty();
while decoder.receive_frame(&mut decoded).is_ok() {
scaler.run(&decoded, &mut rgb_frame)
.expect("failed to scale frame");
let image = RgbImage::from_raw(src_width, src_height, rgb_frame.data(0).to_owned())
.expect("could not read rgb data to image");
let image = DynamicImage::from(image);
let bitmap= processing_pipeline.process(image);
connection.send_command(BitmapCommand::from(bitmap))
.expect("failed to send image command");
frame_index += 1;
}
Ok(())
};
for (stream, packet) in ictx.packets() {
if stream.index() == video_stream_index {
decoder.send_packet(&packet)
.expect("failed to send video packet");
receive_and_process_decoded_frames(&mut decoder)
.expect("failed to process video packet");
}
}
decoder.send_eof().expect("failed to send eof");
receive_and_process_decoded_frames(&mut decoder)
.expect("failed to eof packet");
}