uvc: default to 720p gadget settings

This commit is contained in:
Brad Stein 2026-01-06 09:36:55 -03:00
parent 54c2367c8c
commit 368319af63
3 changed files with 61 additions and 17 deletions

View File

@ -11,6 +11,21 @@ cleanup() { echo "" >"$G/UDC" 2>/dev/null || true; }
DISABLE_UAC=${LESAVKA_DISABLE_UAC:-} DISABLE_UAC=${LESAVKA_DISABLE_UAC:-}
DISABLE_UVC=${LESAVKA_DISABLE_UVC:-} DISABLE_UVC=${LESAVKA_DISABLE_UVC:-}
UVC_FALLBACK=${LESAVKA_UVC_FALLBACK:-1} UVC_FALLBACK=${LESAVKA_UVC_FALLBACK:-1}
UVC_STREAMING_INTERVAL=${LESAVKA_UVC_STREAMING_INTERVAL:-1}
UVC_MAXPACKET=${LESAVKA_UVC_MAXPACKET:-1024}
UVC_MAXBURST=${LESAVKA_UVC_MAXBURST:-1}
UVC_INTERVAL=${LESAVKA_UVC_INTERVAL:-}
UVC_WIDTH=${LESAVKA_UVC_WIDTH:-1280}
UVC_HEIGHT=${LESAVKA_UVC_HEIGHT:-720}
UVC_FPS=${LESAVKA_UVC_FPS:-30}
UVC_DISABLE_IRQ=${LESAVKA_UVC_DISABLE_IRQ:-}
UVC_BULK=${LESAVKA_UVC_BULK:-}
MAX_SPEED=${LESAVKA_MAX_SPEED:-high-speed}
if [[ -z $UVC_INTERVAL ]]; then
UVC_INTERVAL=$((10000000 / UVC_FPS))
fi
UVC_FRAME_SIZE=$((UVC_WIDTH * UVC_HEIGHT * 2))
wait_for_enum() { wait_for_enum() {
local tries=${1:-50} # 50 x 100ms = 5s local tries=${1:-50} # 50 x 100ms = 5s
@ -86,15 +101,19 @@ G=/sys/kernel/config/usb_gadget/lesavka
if [[ -d $G ]]; then if [[ -d $G ]]; then
echo '' >"$G/UDC" 2>/dev/null || true echo '' >"$G/UDC" 2>/dev/null || true
sleep 0.2 sleep 0.2
find "$G/configs" -type l -delete 2>/dev/null || true # configfs doesn't allow unlinking attribute files; remove links then rmdir.
rm -rf "$G" 2>/dev/null || true find "$G" -type l -delete 2>/dev/null || true
for dir in "$G/functions" "$G/configs" "$G/strings" "$G/os_desc" "$G/webusb"; do
[[ -d $dir ]] || continue
find "$dir" -mindepth 1 -depth -type d -exec rmdir {} \; 2>/dev/null || true
done
fi fi
mkdir -p "$G" mkdir -p "$G"
echo 0x1d6b >"$G/idVendor" # Linux Foundation echo 0x1d6b >"$G/idVendor" # Linux Foundation
echo 0x0104 >"$G/idProduct" # Multifunction Composite Gadget echo 0x0104 >"$G/idProduct" # Multifunction Composite Gadget
echo 0x0200 >"$G/bcdUSB" echo 0x0200 >"$G/bcdUSB"
echo high-speed >"$G/max_speed" echo "$MAX_SPEED" >"$G/max_speed"
mkdir -p "$G/strings/0x409" mkdir -p "$G/strings/0x409"
echo "$(cat /proc/sys/kernel/random/uuid)" >"$G/strings/0x409/serialnumber" echo "$(cat /proc/sys/kernel/random/uuid)" >"$G/strings/0x409/serialnumber"
@ -145,6 +164,12 @@ if [[ -z $DISABLE_UVC ]]; then
# ----------------------- UVC function (usbvideo) ------------------ # ----------------------- UVC function (usbvideo) ------------------
mkdir -p "$G/functions/uvc.usb0" mkdir -p "$G/functions/uvc.usb0"
F="$G/functions/uvc.usb0" F="$G/functions/uvc.usb0"
echo "$UVC_STREAMING_INTERVAL" >"$F/streaming_interval"
echo "$UVC_MAXPACKET" >"$F/streaming_maxpacket"
echo "$UVC_MAXBURST" >"$F/streaming_maxburst"
if [[ -n $UVC_BULK ]]; then
echo 1 >"$F/streaming_bulk" 2>/dev/null || true
fi
# ── 1. FORMAT DESCRIPTOR (uncompressed YUY2, 16 bpp) ────────────── # ── 1. FORMAT DESCRIPTOR (uncompressed YUY2, 16 bpp) ──────────────
mkdir -p "$F/streaming/uncompressed/yuyv" mkdir -p "$F/streaming/uncompressed/yuyv"
@ -153,14 +178,15 @@ if [[ -z $DISABLE_UVC ]]; then
>"$F/streaming/uncompressed/yuyv/guidFormat" >"$F/streaming/uncompressed/yuyv/guidFormat"
echo 16 >"$F/streaming/uncompressed/yuyv/bBitsPerPixel" echo 16 >"$F/streaming/uncompressed/yuyv/bBitsPerPixel"
# ── 2. FRAME DESCRIPTOR (720p @ 30 fps) ─────────────────────────── # ── 2. FRAME DESCRIPTOR (480p @ 30 fps) ───────────────────────────
mkdir -p "$F/streaming/uncompressed/yuyv/720p" mkdir -p "$F/streaming/uncompressed/yuyv/480p"
echo 1280 >"$F/streaming/uncompressed/yuyv/720p/wWidth" echo "$UVC_WIDTH" >"$F/streaming/uncompressed/yuyv/480p/wWidth"
echo 720 >"$F/streaming/uncompressed/yuyv/720p/wHeight" echo "$UVC_HEIGHT" >"$F/streaming/uncompressed/yuyv/480p/wHeight"
echo 1843200 >"$F/streaming/uncompressed/yuyv/720p/dwMaxVideoFrameBufferSize" echo "$UVC_FRAME_SIZE" >"$F/streaming/uncompressed/yuyv/480p/dwMaxVideoFrameBufferSize"
echo 333333 >"$F/streaming/uncompressed/yuyv/720p/dwDefaultFrameInterval" echo "$UVC_INTERVAL" >"$F/streaming/uncompressed/yuyv/480p/dwDefaultFrameInterval"
cat <<'EOF' >"$F/streaming/uncompressed/yuyv/720p/dwFrameInterval" cat <<EOF >"$F/streaming/uncompressed/yuyv/480p/dwFrameInterval"
333333 ${UVC_INTERVAL}
$((UVC_INTERVAL * 2))
EOF EOF
# ── 3. REQUIRED HEADER LINKS (per UVC gadget docs) ──────────────── # ── 3. REQUIRED HEADER LINKS (per UVC gadget docs) ────────────────
@ -187,6 +213,10 @@ EOF
done done
set -e set -e
if [[ -n $UVC_DISABLE_IRQ ]]; then
echo 0 >"$F/control/enable_interrupt_ep" 2>/dev/null || true
fi
# optional: hide unsupported controls # optional: hide unsupported controls
echo 0 >"$F/control/terminal/camera/default/bmControls" 2>/dev/null || true echo 0 >"$F/control/terminal/camera/default/bmControls" 2>/dev/null || true
echo 0 >"$F/control/processing/default/bmControls" 2>/dev/null || true echo 0 >"$F/control/processing/default/bmControls" 2>/dev/null || true

View File

@ -229,11 +229,11 @@ fn parse_args() -> Result<(String, UvcConfig)> {
impl UvcConfig { impl UvcConfig {
fn from_env() -> Self { fn from_env() -> Self {
let width = env_u32("LESAVKA_UVC_WIDTH", 640); let width = env_u32("LESAVKA_UVC_WIDTH", 1280);
let height = env_u32("LESAVKA_UVC_HEIGHT", 480); let height = env_u32("LESAVKA_UVC_HEIGHT", 720);
let fps = env_u32("LESAVKA_UVC_FPS", 30).max(1); let fps = env_u32("LESAVKA_UVC_FPS", 30).max(1);
let interval = env_u32("LESAVKA_UVC_INTERVAL", 0); let interval = env_u32("LESAVKA_UVC_INTERVAL", 0);
let mut max_packet = env_u32("LESAVKA_UVC_MAXPACKET", 3072); let mut max_packet = env_u32("LESAVKA_UVC_MAXPACKET", 1024);
if env::var("LESAVKA_UVC_BULK").is_ok() { if env::var("LESAVKA_UVC_BULK").is_ok() {
max_packet = max_packet.min(512); max_packet = max_packet.min(512);
} else { } else {

View File

@ -16,6 +16,13 @@ const EYE_ID: [&str; 2] = ["l", "r"];
static START: std::sync::OnceLock<gst::ClockTime> = std::sync::OnceLock::new(); static START: std::sync::OnceLock<gst::ClockTime> = std::sync::OnceLock::new();
static DEV_MODE: std::sync::OnceLock<bool> = std::sync::OnceLock::new(); static DEV_MODE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
fn env_u32(name: &str, default: u32) -> u32 {
std::env::var(name)
.ok()
.and_then(|v| v.parse::<u32>().ok())
.unwrap_or(default)
}
fn dev_mode_enabled() -> bool { fn dev_mode_enabled() -> bool {
*DEV_MODE *DEV_MODE
.get_or_init(|| std::env::var("LESAVKA_DEV_MODE").is_ok()) .get_or_init(|| std::env::var("LESAVKA_DEV_MODE").is_ok())
@ -271,15 +278,19 @@ impl WebcamSink {
let pipeline = gst::Pipeline::new(); let pipeline = gst::Pipeline::new();
let width = env_u32("LESAVKA_UVC_WIDTH", 1280) as i32;
let height = env_u32("LESAVKA_UVC_HEIGHT", 720) as i32;
let fps = env_u32("LESAVKA_UVC_FPS", 30).max(1) as i32;
let caps_h264 = gst::Caps::builder("video/x-h264") let caps_h264 = gst::Caps::builder("video/x-h264")
.field("stream-format", "byte-stream") .field("stream-format", "byte-stream")
.field("alignment", "au") .field("alignment", "au")
.build(); .build();
let raw_caps = gst::Caps::builder("video/x-raw") let raw_caps = gst::Caps::builder("video/x-raw")
.field("format", "YUY2") .field("format", "YUY2")
.field("width", 1280i32) .field("width", width)
.field("height", 720i32) .field("height", height)
.field("framerate", gst::Fraction::new(30, 1)) .field("framerate", gst::Fraction::new(fps, 1))
.build(); .build();
let src = gst::ElementFactory::make("appsrc") let src = gst::ElementFactory::make("appsrc")
@ -297,6 +308,7 @@ impl WebcamSink {
.build() .build()
.with_context(|| format!("building decoder element {decoder_name}"))?; .with_context(|| format!("building decoder element {decoder_name}"))?;
let convert = gst::ElementFactory::make("videoconvert").build()?; let convert = gst::ElementFactory::make("videoconvert").build()?;
let scale = gst::ElementFactory::make("videoscale").build()?;
let caps = gst::ElementFactory::make("capsfilter") let caps = gst::ElementFactory::make("capsfilter")
.property("caps", &raw_caps) .property("caps", &raw_caps)
.build()?; .build()?;
@ -311,6 +323,7 @@ impl WebcamSink {
&h264parse, &h264parse,
&decoder, &decoder,
&convert, &convert,
&scale,
&caps, &caps,
&sink, &sink,
])?; ])?;
@ -319,6 +332,7 @@ impl WebcamSink {
&h264parse, &h264parse,
&decoder, &decoder,
&convert, &convert,
&scale,
&caps, &caps,
&sink, &sink,
])?; ])?;