From 6c3942cd2c7bb8437bc02d2c7b44b9f986c9596c Mon Sep 17 00:00:00 2001 From: Brad Stein Date: Thu, 3 Jul 2025 09:24:57 -0500 Subject: [PATCH] increased cam logging --- client/src/input/camera.rs | 18 +++++++++--------- client/src/input/inputs.rs | 25 ++++++++++++++++++++++--- scripts/daemon/lesavka-core.sh | 3 +-- scripts/install/server.sh | 2 +- server/src/main.rs | 23 ++++++++++++++++++++--- server/src/video.rs | 34 ++++++++++++++++++++++++++++++++++ 6 files changed, 87 insertions(+), 18 deletions(-) diff --git a/client/src/input/camera.rs b/client/src/input/camera.rs index 49476e8..e751e63 100644 --- a/client/src/input/camera.rs +++ b/client/src/input/camera.rs @@ -2,6 +2,7 @@ #![forbid(unsafe_code)] use anyhow::Result; +use anyhow::Context; use gstreamer as gst; use gstreamer_app as gst_app; use gst::prelude::*; @@ -22,7 +23,7 @@ impl CameraCapture { .unwrap_or_else(|| "/dev/video0".into()); let desc = format!( - "v4l2src device={dev} io-mode=dmabuf-do-timestamp=true ! \ + "v4l2src device={dev} io-mode=dmabuf do-timestamp=true ! \ videoconvert ! videoscale ! video/x-raw,width=1280,height=720 ! \ v4l2h264enc key-int-max=30 \ extra-controls=\"encode,frame_level_rate_control_enable=1,h264_profile=4\" ! \ @@ -30,12 +31,13 @@ impl CameraCapture { appsink name=asink emit-signals=true max-buffers=60 drop=true" ); - // --- NEW: propagate the Result and down‑cast with `?` - let pipeline: gst::Pipeline = gst::parse::launch(&desc)? + tracing::debug!("πŸ“Έ pipeline‑desc:\n{desc}"); + let pipeline: gst::Pipeline = gst::parse::launch(&desc) + .context("gst parse_launch(cam)")? .downcast::() .expect("not a pipeline"); - // --- NEW: the lookup already returns the concrete AppSink β†’ just unwrap with `?` + tracing::debug!("πŸ“Έ pipeline built OK – setting PLAYING…"); let sink: gst_app::AppSink = pipeline .by_name("asink") .expect("appsink element not found") @@ -43,6 +45,7 @@ impl CameraCapture { .expect("appsink down‑cast"); pipeline.set_state(gst::State::Playing)?; + tracing::info!("πŸ“Έ webcam pipeline ▢️ device={dev}"); Ok(Self { pipeline, sink }) } @@ -72,14 +75,11 @@ impl CameraCapture { /// Cheap stub used when the web‑cam is disabled pub fn new_stub() -> Self { let pipeline = gst::Pipeline::new(); - - // --- NEW: AppSink factory helper (AppSink::new() does not exist) let sink: gst_app::AppSink = gst::ElementFactory::make("appsink") .build() - .expect("make appsink") + .expect("appsink") .downcast::() - .expect("appsink down‑cast"); - + .unwrap(); Self { pipeline, sink } } } diff --git a/client/src/input/inputs.rs b/client/src/input/inputs.rs index 861e596..39c0efb 100644 --- a/client/src/input/inputs.rs +++ b/client/src/input/inputs.rs @@ -93,9 +93,22 @@ impl InputAggregator { } // Auto‑select webcam (may be toggled later through magic chord) - self.camera = match CameraCapture::new(std::env::var("LESAVKA_CAM_SOURCE").ok().as_deref()) { - Ok(cam) => { info!("πŸ“Έ webcam enabled"); Some(cam) } - Err(e) => { warn!("πŸ“Έ webcam disabled: {e}"); None } + let cam_src_env = std::env::var("LESAVKA_CAM_SOURCE").ok(); + self.camera = match cam_src_env.as_deref() + // .or(device_fragment) // <- if you later wire CLI arg + .map(|s| CameraCapture::new(Some(s))) { + Some(Ok(c)) => { + info!("πŸ“Έ webcam enabled (device fragment = {:?})", cam_src_env); + Some(c) + } + Some(Err(e)) => { + warn!("πŸ“Έ webcam disabled: {e:#}"); + Some(CameraCapture::new_stub()) // keep stub so call‑sites compile + } + None => { + info!("πŸ“Έ webcam disabled (no CAM_SOURCE set)"); + None // or Stub – your choice + } }; Ok(()) @@ -135,6 +148,12 @@ impl InputAggregator { mouse.process_events(); } + if let Some(cam) = &self.camera { + debug!("πŸ“Έ CameraCapture present – first pull() will block until a frame arrives"); + } else { + debug!("πŸ“Έ No camera pipeline active"); + } + self.magic_active = magic_now; tick.tick().await; } diff --git a/scripts/daemon/lesavka-core.sh b/scripts/daemon/lesavka-core.sh index e4c1ece..e687396 100644 --- a/scripts/daemon/lesavka-core.sh +++ b/scripts/daemon/lesavka-core.sh @@ -114,7 +114,6 @@ echo 32 >"$U/req_number" 2>/dev/null || true # ----------------------- UVC function (usb‑video) ------------------ mkdir -p "$G/functions/uvc.usb0" -# Mandatory control interface strings mkdir -p "$G/functions/uvc.usb0/control/strings/0x409" echo "Lesavka UVC" >"$G/functions/uvc.usb0/control/strings/0x409/label" # Simple 720p MJPEG + 720p H.264 alt‑setting @@ -152,6 +151,6 @@ ln -s $G/functions/uvc.usb0 $G/configs/c.1/ # 4. Bind gadget #────────────────────────────────────────────────── echo "$UDC" >"$G/UDC" -log "πŸŽ‰ gadget bound on $UDC (hidg0, hidg1, UAC2 L+R)" +log "πŸŽ‰ gadget bound on $UDC (hidg0, hidg1, UAC2 L+R, UVC)" exit 0 diff --git a/scripts/install/server.sh b/scripts/install/server.sh index 1a9f19a..9823865 100755 --- a/scripts/install/server.sh +++ b/scripts/install/server.sh @@ -122,7 +122,7 @@ After=network.target lesavka-core.service [Service] ExecStart=/usr/local/bin/lesavka-server Restart=always -Environment=RUST_LOG=lesavka_server=info,lesavka_server::audio=info,lesavka_server::video=info,lesavka_server::gadget=info +Environment=RUST_LOG=lesavka_server=info,lesavka_server::audio=info,lesavka_server::video=trace,lesavka_server::gadget=info Environment=RUST_BACKTRACE=1 Environment=GST_DEBUG="*:2,alsasink:6,alsasrc:6" Restart=always diff --git a/server/src/main.rs b/server/src/main.rs index 3b56188..4c9cda1 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -212,11 +212,28 @@ impl Relay for Handler { async fn stream_camera( &self, - _req: Request>, + req: Request>, ) -> Result, Status> { - // Simply consume the inbound stream and discard; echo back a single Empty. + // map gRPC camera id β†’ UVC device + let uvc = std::env::var("LESAVKA_UVC_DEV") + .unwrap_or_else(|_| "/dev/video4".into()); + + // build once + let relay = video::CameraRelay::new(0, &uvc) + .map_err(|e| Status::internal(format!("{e:#}")))?; + + // dummy outbound (same pattern as other streams) let (tx, rx) = tokio::sync::mpsc::channel(1); - tx.send(Ok(Empty {})).await.ok(); + + tokio::spawn(async move { + let mut s = req.into_inner(); + while let Some(pkt) = s.next().await.transpose()? { + relay.feed(pkt); // ← all logging inside video.rs + } + tx.send(Ok(Empty {})).await.ok(); + Ok::<(), Status>(()) + }); + Ok(Response::new(ReceiverStream::new(rx))) } diff --git a/server/src/video.rs b/server/src/video.rs index 78884a5..dd244b4 100644 --- a/server/src/video.rs +++ b/server/src/video.rs @@ -259,3 +259,37 @@ impl WebcamSink { let _ = self.appsrc.push_buffer(buf); } } + +/*─────────────────────────────────*/ +/* gRPC β†’ WebcamSink relay */ +/*─────────────────────────────────*/ + +pub struct CameraRelay { + sink: WebcamSink, // the v4l2sink pipeline (or stub) + id: u32, // gRPC β€œid” (for future multi‑cam) + frames: std::sync::atomic::AtomicU64, +} + +impl CameraRelay { + pub fn new(id: u32, uvc_dev: &str) -> anyhow::Result { + Ok(Self { + sink: WebcamSink::new(uvc_dev)?, + id, + frames: std::sync::atomic::AtomicU64::new(0), + }) + } + + /// Push one VideoPacket coming from the client + pub fn feed(&self, pkt: VideoPacket) { + let n = self.frames.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + if n < 10 || n % 300 == 0 { + tracing::debug!(target:"lesavka_server::video", + cam_id = self.id, + frame = n, + bytes = pkt.data.len(), + pts = pkt.pts, + "πŸ“Έ srv webcam frame"); + } + self.sink.push(pkt); + } +}