increased cam logging
This commit is contained in:
parent
1c095a95d1
commit
6c3942cd2c
@ -2,6 +2,7 @@
|
|||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
use anyhow::Context;
|
||||||
use gstreamer as gst;
|
use gstreamer as gst;
|
||||||
use gstreamer_app as gst_app;
|
use gstreamer_app as gst_app;
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
@ -22,7 +23,7 @@ impl CameraCapture {
|
|||||||
.unwrap_or_else(|| "/dev/video0".into());
|
.unwrap_or_else(|| "/dev/video0".into());
|
||||||
|
|
||||||
let desc = format!(
|
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 ! \
|
videoconvert ! videoscale ! video/x-raw,width=1280,height=720 ! \
|
||||||
v4l2h264enc key-int-max=30 \
|
v4l2h264enc key-int-max=30 \
|
||||||
extra-controls=\"encode,frame_level_rate_control_enable=1,h264_profile=4\" ! \
|
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"
|
appsink name=asink emit-signals=true max-buffers=60 drop=true"
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- NEW: propagate the Result and down‑cast with `?`
|
tracing::debug!("📸 pipeline‑desc:\n{desc}");
|
||||||
let pipeline: gst::Pipeline = gst::parse::launch(&desc)?
|
let pipeline: gst::Pipeline = gst::parse::launch(&desc)
|
||||||
|
.context("gst parse_launch(cam)")?
|
||||||
.downcast::<gst::Pipeline>()
|
.downcast::<gst::Pipeline>()
|
||||||
.expect("not a 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
|
let sink: gst_app::AppSink = pipeline
|
||||||
.by_name("asink")
|
.by_name("asink")
|
||||||
.expect("appsink element not found")
|
.expect("appsink element not found")
|
||||||
@ -43,6 +45,7 @@ impl CameraCapture {
|
|||||||
.expect("appsink down‑cast");
|
.expect("appsink down‑cast");
|
||||||
|
|
||||||
pipeline.set_state(gst::State::Playing)?;
|
pipeline.set_state(gst::State::Playing)?;
|
||||||
|
tracing::info!("📸 webcam pipeline ▶️ device={dev}");
|
||||||
|
|
||||||
Ok(Self { pipeline, sink })
|
Ok(Self { pipeline, sink })
|
||||||
}
|
}
|
||||||
@ -72,14 +75,11 @@ impl CameraCapture {
|
|||||||
/// Cheap stub used when the web‑cam is disabled
|
/// Cheap stub used when the web‑cam is disabled
|
||||||
pub fn new_stub() -> Self {
|
pub fn new_stub() -> Self {
|
||||||
let pipeline = gst::Pipeline::new();
|
let pipeline = gst::Pipeline::new();
|
||||||
|
|
||||||
// --- NEW: AppSink factory helper (AppSink::new() does not exist)
|
|
||||||
let sink: gst_app::AppSink = gst::ElementFactory::make("appsink")
|
let sink: gst_app::AppSink = gst::ElementFactory::make("appsink")
|
||||||
.build()
|
.build()
|
||||||
.expect("make appsink")
|
.expect("appsink")
|
||||||
.downcast::<gst_app::AppSink>()
|
.downcast::<gst_app::AppSink>()
|
||||||
.expect("appsink down‑cast");
|
.unwrap();
|
||||||
|
|
||||||
Self { pipeline, sink }
|
Self { pipeline, sink }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -93,9 +93,22 @@ impl InputAggregator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Auto‑select webcam (may be toggled later through magic chord)
|
// Auto‑select webcam (may be toggled later through magic chord)
|
||||||
self.camera = match CameraCapture::new(std::env::var("LESAVKA_CAM_SOURCE").ok().as_deref()) {
|
let cam_src_env = std::env::var("LESAVKA_CAM_SOURCE").ok();
|
||||||
Ok(cam) => { info!("📸 webcam enabled"); Some(cam) }
|
self.camera = match cam_src_env.as_deref()
|
||||||
Err(e) => { warn!("📸 webcam disabled: {e}"); None }
|
// .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(())
|
Ok(())
|
||||||
@ -135,6 +148,12 @@ impl InputAggregator {
|
|||||||
mouse.process_events();
|
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;
|
self.magic_active = magic_now;
|
||||||
tick.tick().await;
|
tick.tick().await;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -114,7 +114,6 @@ echo 32 >"$U/req_number" 2>/dev/null || true
|
|||||||
|
|
||||||
# ----------------------- UVC function (usb‑video) ------------------
|
# ----------------------- UVC function (usb‑video) ------------------
|
||||||
mkdir -p "$G/functions/uvc.usb0"
|
mkdir -p "$G/functions/uvc.usb0"
|
||||||
# Mandatory control interface strings
|
|
||||||
mkdir -p "$G/functions/uvc.usb0/control/strings/0x409"
|
mkdir -p "$G/functions/uvc.usb0/control/strings/0x409"
|
||||||
echo "Lesavka UVC" >"$G/functions/uvc.usb0/control/strings/0x409/label"
|
echo "Lesavka UVC" >"$G/functions/uvc.usb0/control/strings/0x409/label"
|
||||||
# Simple 720p MJPEG + 720p H.264 alt‑setting
|
# 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
|
# 4. Bind gadget
|
||||||
#──────────────────────────────────────────────────
|
#──────────────────────────────────────────────────
|
||||||
echo "$UDC" >"$G/UDC"
|
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
|
exit 0
|
||||||
|
|||||||
@ -122,7 +122,7 @@ After=network.target lesavka-core.service
|
|||||||
[Service]
|
[Service]
|
||||||
ExecStart=/usr/local/bin/lesavka-server
|
ExecStart=/usr/local/bin/lesavka-server
|
||||||
Restart=always
|
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=RUST_BACKTRACE=1
|
||||||
Environment=GST_DEBUG="*:2,alsasink:6,alsasrc:6"
|
Environment=GST_DEBUG="*:2,alsasink:6,alsasrc:6"
|
||||||
Restart=always
|
Restart=always
|
||||||
|
|||||||
@ -212,11 +212,28 @@ impl Relay for Handler {
|
|||||||
|
|
||||||
async fn stream_camera(
|
async fn stream_camera(
|
||||||
&self,
|
&self,
|
||||||
_req: Request<tonic::Streaming<VideoPacket>>,
|
req: Request<tonic::Streaming<VideoPacket>>,
|
||||||
) -> Result<Response<Self::StreamCameraStream>, Status> {
|
) -> 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);
|
let (tx, rx) = tokio::sync::mpsc::channel(1);
|
||||||
|
|
||||||
|
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();
|
tx.send(Ok(Empty {})).await.ok();
|
||||||
|
Ok::<(), Status>(())
|
||||||
|
});
|
||||||
|
|
||||||
Ok(Response::new(ReceiverStream::new(rx)))
|
Ok(Response::new(ReceiverStream::new(rx)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -259,3 +259,37 @@ impl WebcamSink {
|
|||||||
let _ = self.appsrc.push_buffer(buf);
|
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<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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user