increased cam logging

This commit is contained in:
Brad Stein 2025-07-03 09:24:57 -05:00
parent 1c095a95d1
commit 6c3942cd2c
6 changed files with 87 additions and 18 deletions

View File

@ -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 downcast with `?`
let pipeline: gst::Pipeline = gst::parse::launch(&desc)?
tracing::debug!("📸 pipelinedesc:\n{desc}");
let pipeline: gst::Pipeline = gst::parse::launch(&desc)
.context("gst parse_launch(cam)")?
.downcast::<gst::Pipeline>()
.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 downcast");
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 webcam 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::<gst_app::AppSink>()
.expect("appsink downcast");
.unwrap();
Self { pipeline, sink }
}
}

View File

@ -93,9 +93,22 @@ impl InputAggregator {
}
// Autoselect 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 callsites 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;
}

View File

@ -114,7 +114,6 @@ echo 32 >"$U/req_number" 2>/dev/null || true
# ----------------------- UVC function (usbvideo) ------------------
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 altsetting
@ -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

View File

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

View File

@ -212,11 +212,28 @@ impl Relay for Handler {
async fn stream_camera(
&self,
_req: Request<tonic::Streaming<VideoPacket>>,
req: Request<tonic::Streaming<VideoPacket>>,
) -> Result<Response<Self::StreamCameraStream>, 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)))
}

View File

@ -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 multicam)
frames: std::sync::atomic::AtomicU64,
}
impl CameraRelay {
pub fn new(id: u32, uvc_dev: &str) -> anyhow::Result<Self> {
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);
}
}