fix: resolve compilation errors from crate version mismatches
- encoder.rs: update openh264 API (Encoder::new → with_api_config + EncoderConfig), use YUVSlices instead of YUVBuffer, use bitstream.frame_type() for keyframe detection, use force_intra_frame() for request_keyframe, fix ImageBuffer type params for image v0.25, add mut to JpegEncoder, remove unused base64 import - capture.rs: fix ImageBuffer type params for image v0.25, add mut to JpegEncoder - input.rs: update enigo 0.2 API - add Keyboard/Mouse trait imports, replace scroll_y/scroll_x with scroll(axis), fix Key variant names (UpArrow, DownArrow, Numlock, Print), replace Windows-only Numpad* keys with Unicode fallbacks - service.rs: add type annotation to Vec::new() - main.rs: add missing clap::Parser import
This commit is contained in:
parent
84f559c1a7
commit
fdd1dcbed0
@ -106,14 +106,14 @@ impl ScreenCapture {
|
||||
rgb.push(chunk[0]); // B
|
||||
}
|
||||
|
||||
let img_buffer = ImageBuffer::<Rgb<u8>>::from_raw(
|
||||
let img_buffer = ImageBuffer::<Rgb<u8>, Vec<u8>>::from_raw(
|
||||
raw.width as u32,
|
||||
raw.height as u32,
|
||||
rgb,
|
||||
).context("failed to create image buffer")?;
|
||||
|
||||
let mut jpeg_bytes = Vec::new();
|
||||
let encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut jpeg_bytes, quality.clamp(1, 100));
|
||||
let mut encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut jpeg_bytes, quality.clamp(1, 100));
|
||||
encoder.encode_image(&img_buffer).context("JPEG encode failed")?;
|
||||
|
||||
let b64 = STANDARD.encode(&jpeg_bytes);
|
||||
|
||||
@ -10,7 +10,6 @@
|
||||
//! defined in `protocol.rs`.
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use base64::{Engine, engine::general_purpose::STANDARD};
|
||||
use image::{ImageBuffer, Rgb};
|
||||
use log::info;
|
||||
|
||||
@ -76,11 +75,11 @@ impl VideoEncoder for JpegEncoder {
|
||||
let rgb_data = bgra_to_rgb(bgra, width, height);
|
||||
|
||||
// Encode as JPEG.
|
||||
let img_buffer = ImageBuffer::<Rgb<u8>>::from_raw(width as u32, height as u32, rgb_data)
|
||||
let img_buffer = ImageBuffer::<Rgb<u8>, Vec<u8>>::from_raw(width as u32, height as u32, rgb_data)
|
||||
.context("failed to create RGB image buffer")?;
|
||||
|
||||
let mut jpeg_bytes = Vec::new();
|
||||
let encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut jpeg_bytes, self.quality);
|
||||
let mut encoder = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut jpeg_bytes, self.quality);
|
||||
encoder
|
||||
.encode_image(&img_buffer)
|
||||
.context("JPEG encode failed")?;
|
||||
@ -122,12 +121,12 @@ cfg_if::cfg_if! {
|
||||
);
|
||||
|
||||
// Use constant bitrate for predictable network usage.
|
||||
let rc = openh264::encoder::RateControl::Constant(bitrate_kbps as i32);
|
||||
let encoder = openh264::encoder::Encoder::new(
|
||||
openh264::encoder::Width(width as i32),
|
||||
openh264::encoder::Height(height as i32),
|
||||
rc,
|
||||
).map_err(|e| anyhow::anyhow!("openh264 init failed: {:?}", e))?;
|
||||
let api = openh264::OpenH264API::from_source();
|
||||
let config = openh264::encoder::EncoderConfig::new()
|
||||
.set_bitrate_bps((bitrate_kbps as u32) * 1000)
|
||||
.rate_control_mode(openh264::encoder::RateControlMode::Bitrate);
|
||||
let encoder = openh264::encoder::Encoder::with_api_config(api, config)
|
||||
.map_err(|e| anyhow::anyhow!("openh264 init failed: {:?}", e))?;
|
||||
|
||||
Ok(Self {
|
||||
encoder,
|
||||
@ -144,28 +143,22 @@ cfg_if::cfg_if! {
|
||||
// BGRA → I420 (YUV420 planar)
|
||||
let (y, u, v) = bgra_to_i420(bgra, width, height);
|
||||
|
||||
// Create openh264 YUV buffer.
|
||||
let yuv = openh264::formats::YUVBuffer::new(
|
||||
openh264::formats::YUVPixel::from_yuv(y[0], u[0], v[0]), // dummy first pixel
|
||||
self.width,
|
||||
self.height,
|
||||
y,
|
||||
u,
|
||||
v,
|
||||
// Create openh264 YUV buffer from pre-separated planes.
|
||||
let yuv = openh264::formats::YUVSlices::new(
|
||||
(&y, &u, &v),
|
||||
(self.width, self.height),
|
||||
(self.width, self.width / 2, self.width / 2),
|
||||
);
|
||||
|
||||
// Encode the frame.
|
||||
let bitstream = self.encoder.encode(&yuv)
|
||||
.map_err(|e| anyhow::anyhow!("H.264 encode failed: {:?}", e))?;
|
||||
|
||||
self.frame_count += 1;
|
||||
|
||||
// Determine if this is a keyframe.
|
||||
// openh264's Bitstream doesn't directly expose keyframe info,
|
||||
// but we can force periodic keyframes based on our counter.
|
||||
let is_keyframe = self.frame_count == 1
|
||||
|| (self.keyframe_interval > 0
|
||||
&& self.frame_count % self.keyframe_interval == 0);
|
||||
// Determine if this is a keyframe from the encoder output.
|
||||
let is_keyframe = matches!(
|
||||
bitstream.frame_type(),
|
||||
openh264::encoder::FrameType::IDR | openh264::encoder::FrameType::I
|
||||
);
|
||||
|
||||
let frame_type = if is_keyframe {
|
||||
crate::protocol::frame_type::H264_KEY
|
||||
@ -174,7 +167,7 @@ cfg_if::cfg_if! {
|
||||
};
|
||||
|
||||
// The bitstream contains raw NAL units (Annex-B format with start codes).
|
||||
let payload = bitstream.as_ref().to_vec();
|
||||
let payload = bitstream.to_vec();
|
||||
|
||||
Ok(EncodedFrame {
|
||||
frame_type,
|
||||
@ -185,9 +178,7 @@ cfg_if::cfg_if! {
|
||||
|
||||
fn request_keyframe(&mut self) {
|
||||
// Request an IDR frame on next encode.
|
||||
// Note: openh264's Encoder doesn't have a direct force-IDR API,
|
||||
// so we reset the frame counter to trigger one.
|
||||
self.frame_count = 0;
|
||||
self.encoder.force_intra_frame();
|
||||
}
|
||||
|
||||
fn encoder_type(&self) -> EncoderType {
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
//! - Linux: X11 XTest extension
|
||||
|
||||
use anyhow::Result;
|
||||
use enigo::{Button, Direction, Enigo, Key, Settings, Coordinate};
|
||||
use enigo::{Axis, Button, Direction, Enigo, Key, Keyboard, Mouse, Settings, Coordinate};
|
||||
use log::{info, warn};
|
||||
|
||||
/// Manages input simulation on the local machine.
|
||||
@ -145,34 +145,22 @@ impl InputHandler {
|
||||
let delta_y = params["deltaY"].as_i64().unwrap_or(0) as i32;
|
||||
let delta_x = params["deltaX"].as_i64().unwrap_or(0) as i32;
|
||||
|
||||
// Vertical scroll.
|
||||
// Clamp to prevent crazy scrolling.
|
||||
let delta_y = delta_y.clamp(-50, 50);
|
||||
let delta_x = delta_x.clamp(-50, 50);
|
||||
|
||||
// Vertical scroll: positive = down, negative = up.
|
||||
if delta_y != 0 {
|
||||
let direction = if delta_y > 0 {
|
||||
Direction::ScrollDown
|
||||
} else {
|
||||
Direction::ScrollUp
|
||||
};
|
||||
let amount = delta_y.abs().min(50); // Clamp to prevent crazy scrolling.
|
||||
for _ in 0..amount {
|
||||
self.enigo
|
||||
.scroll_y(1, direction)
|
||||
.map_err(|e| anyhow::anyhow!("scroll_y failed: {}", e))?;
|
||||
}
|
||||
.scroll(delta_y, Axis::Vertical)
|
||||
.map_err(|e| anyhow::anyhow!("scroll failed: {}", e))?;
|
||||
}
|
||||
|
||||
// Horizontal scroll.
|
||||
// Horizontal scroll: positive = right, negative = left.
|
||||
if delta_x != 0 {
|
||||
let direction = if delta_x > 0 {
|
||||
Direction::ScrollRight
|
||||
} else {
|
||||
Direction::ScrollLeft
|
||||
};
|
||||
let amount = delta_x.abs().min(50);
|
||||
for _ in 0..amount {
|
||||
self.enigo
|
||||
.scroll_x(1, direction)
|
||||
.map_err(|e| anyhow::anyhow!("scroll_x failed: {}", e))?;
|
||||
}
|
||||
.scroll(delta_x, Axis::Horizontal)
|
||||
.map_err(|e| anyhow::anyhow!("scroll failed: {}", e))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -317,10 +305,10 @@ fn map_key(code: &str, key: &str) -> Key {
|
||||
"Escape" => Key::Escape,
|
||||
|
||||
// ── Arrow keys ──────────────────────────────────────────────────
|
||||
"ArrowUp" => Key::Up,
|
||||
"ArrowDown" => Key::Down,
|
||||
"ArrowLeft" => Key::Left,
|
||||
"ArrowRight" => Key::Right,
|
||||
"ArrowUp" => Key::UpArrow,
|
||||
"ArrowDown" => Key::DownArrow,
|
||||
"ArrowLeft" => Key::LeftArrow,
|
||||
"ArrowRight" => Key::RightArrow,
|
||||
|
||||
// ── Function keys ───────────────────────────────────────────────
|
||||
"F1" => Key::F1,
|
||||
@ -346,7 +334,7 @@ fn map_key(code: &str, key: &str) -> Key {
|
||||
|
||||
// ── Lock keys ───────────────────────────────────────────────────
|
||||
"CapsLock" => Key::CapsLock,
|
||||
"NumLock" => Key::NumLock,
|
||||
"NumLock" => Key::Numlock,
|
||||
"ScrollLock" => Key::ScrollLock,
|
||||
|
||||
// ── Navigation / editing cluster ────────────────────────────────
|
||||
@ -356,27 +344,26 @@ fn map_key(code: &str, key: &str) -> Key {
|
||||
"End" => Key::End,
|
||||
"PageUp" => Key::PageUp,
|
||||
"PageDown" => Key::PageDown,
|
||||
"PrintScreen" => Key::PrintScreen,
|
||||
"PrintScreen" => Key::Print,
|
||||
"Pause" => Key::Pause,
|
||||
|
||||
// ── Numpad ──────────────────────────────────────────────────────
|
||||
"Numpad0" => Key::Numpad0,
|
||||
"Numpad1" => Key::Numpad1,
|
||||
"Numpad2" => Key::Numpad2,
|
||||
"Numpad3" => Key::Numpad3,
|
||||
"Numpad4" => Key::Numpad4,
|
||||
"Numpad5" => Key::Numpad5,
|
||||
"Numpad6" => Key::Numpad6,
|
||||
"Numpad7" => Key::Numpad7,
|
||||
"Numpad8" => Key::Numpad8,
|
||||
"Numpad9" => Key::Numpad9,
|
||||
"NumpadAdd" => Key::NumpadAdd,
|
||||
"NumpadSubtract" => Key::NumpadSubtract,
|
||||
"NumpadMultiply" => Key::NumpadMultiply,
|
||||
"NumpadDivide" => Key::NumpadDivide,
|
||||
"NumpadDecimal" => Key::NumpadDecimal,
|
||||
"NumpadEnter" => Key::NumpadEnter,
|
||||
"NumLock" => Key::NumLock,
|
||||
"Numpad0" => Key::Unicode('0'),
|
||||
"Numpad1" => Key::Unicode('1'),
|
||||
"Numpad2" => Key::Unicode('2'),
|
||||
"Numpad3" => Key::Unicode('3'),
|
||||
"Numpad4" => Key::Unicode('4'),
|
||||
"Numpad5" => Key::Unicode('5'),
|
||||
"Numpad6" => Key::Unicode('6'),
|
||||
"Numpad7" => Key::Unicode('7'),
|
||||
"Numpad8" => Key::Unicode('8'),
|
||||
"Numpad9" => Key::Unicode('9'),
|
||||
"NumpadAdd" => Key::Unicode('+'),
|
||||
"NumpadSubtract" => Key::Unicode('-'),
|
||||
"NumpadMultiply" => Key::Unicode('*'),
|
||||
"NumpadDivide" => Key::Unicode('/'),
|
||||
"NumpadDecimal" => Key::Unicode('.'),
|
||||
"NumpadEnter" => Key::Return,
|
||||
|
||||
// ── Punctuation / symbols ───────────────────────────────────────
|
||||
// These are handled as Unicode characters when sent via `key` field.
|
||||
|
||||
@ -37,10 +37,11 @@ mod protocol;
|
||||
mod service;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use encoder::{EncodedFrame, EncoderType};
|
||||
use clap::Parser;
|
||||
use encoder::EncoderType;
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use log::{error, info, warn};
|
||||
use protocol::{ControlMessage, FRAME_HEADER_SIZE};
|
||||
use protocol::ControlMessage;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio_tungstenite::tungstenite::Message;
|
||||
|
||||
@ -73,7 +74,7 @@ fn main() -> Result<()> {
|
||||
let cli = cli::Cli::parse();
|
||||
|
||||
match cli.command {
|
||||
cli::Command::Run(opts) => {
|
||||
cli::Command::Run { opts } => {
|
||||
// On Windows, check if we should enter service mode.
|
||||
#[cfg(windows)]
|
||||
if opts.windows_service {
|
||||
@ -298,9 +299,9 @@ async fn run_session(
|
||||
});
|
||||
|
||||
// Spawn heartbeat.
|
||||
let hb_session_id = session_id.to_string();
|
||||
let hb_interval = opts.heartbeat_interval();
|
||||
let (hb_tx, mut hb_rx) = mpsc::channel::<()>(1);
|
||||
let hb_cleanup_tx = hb_tx.clone();
|
||||
let heartbeat_handle = tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(hb_interval);
|
||||
loop {
|
||||
@ -377,8 +378,7 @@ async fn run_session(
|
||||
|
||||
// Cleanup: drop the capture sender to signal the capture thread to stop,
|
||||
// then wait for both spawned tasks to finish.
|
||||
let _ = hb_tx.send(()).await;
|
||||
drop(capture_tx);
|
||||
let _ = hb_cleanup_tx.send(()).await;
|
||||
let _ = capture_handle.await;
|
||||
let _ = heartbeat_handle.await;
|
||||
|
||||
|
||||
@ -607,7 +607,7 @@ fn status_service(name: &str) -> Result<()> {
|
||||
/// from within a systemd service (which otherwise runs with a minimal environment).
|
||||
#[cfg(unix)]
|
||||
fn detect_display_env() -> Vec<(String, String)> {
|
||||
let mut envs = Vec::new();
|
||||
let mut envs: Vec<(String, String)> = Vec::new();
|
||||
|
||||
// X11 display server.
|
||||
if let Ok(val) = std::env::var("DISPLAY") {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user