77 lines
2.9 KiB
Rust
77 lines
2.9 KiB
Rust
/// Choose the pre-encoder webcam format path.
|
|
///
|
|
/// V4L2 webcams often expose 720p/30 as MJPEG only, so the default accepts
|
|
/// either raw frames or MJPEG unless the operator explicitly pins a format.
|
|
fn camera_source_profile(allow_v4l2_auto_decode: bool) -> CameraSourceProfile {
|
|
if !allow_v4l2_auto_decode {
|
|
return CameraSourceProfile::Raw;
|
|
}
|
|
if std::env::var("LESAVKA_CAM_MJPG").is_ok() {
|
|
return CameraSourceProfile::Mjpeg;
|
|
}
|
|
match std::env::var("LESAVKA_CAM_FORMAT")
|
|
.ok()
|
|
.as_deref()
|
|
.map(str::trim)
|
|
.map(str::to_ascii_lowercase)
|
|
.as_deref()
|
|
{
|
|
Some("mjpg" | "mjpeg" | "jpeg") => CameraSourceProfile::Mjpeg,
|
|
Some("raw" | "yuyv" | "yuy2") => CameraSourceProfile::Raw,
|
|
_ => CameraSourceProfile::AutoDecode,
|
|
}
|
|
}
|
|
|
|
/// Build the source-to-raw-video chain consumed by the encoder and preview tap.
|
|
fn camera_raw_source_chain(
|
|
src_desc: &str,
|
|
src_caps: &str,
|
|
width: u32,
|
|
height: u32,
|
|
fps: u32,
|
|
profile: CameraSourceProfile,
|
|
) -> String {
|
|
match profile {
|
|
CameraSourceProfile::Raw => format!("{src_desc} ! {src_caps}"),
|
|
CameraSourceProfile::Mjpeg => format!(
|
|
"{src_desc} ! \
|
|
image/jpeg,width={width},height={height},framerate={fps}/1 ! \
|
|
jpegdec ! videoconvert ! videoscale ! videorate ! \
|
|
video/x-raw,width={width},height={height},framerate={fps}/1"
|
|
),
|
|
CameraSourceProfile::AutoDecode => format!(
|
|
"{src_desc} ! \
|
|
capsfilter caps=\"{}\" ! \
|
|
decodebin ! videoconvert ! videoscale ! videorate ! \
|
|
video/x-raw,width={width},height={height},framerate={fps}/1,pixel-aspect-ratio=1/1",
|
|
camera_auto_decode_caps(width, height, fps)
|
|
),
|
|
}
|
|
}
|
|
|
|
/// Caps string that lets decodebin negotiate either raw webcam frames or MJPEG.
|
|
fn camera_auto_decode_caps(width: u32, height: u32, fps: u32) -> String {
|
|
format!(
|
|
"video/x-raw,width=(int){width},height=(int){height},framerate=(fraction){fps}/1;image/jpeg,width=(int){width},height=(int){height},framerate=(fraction){fps}/1"
|
|
)
|
|
}
|
|
|
|
fn camera_preview_tap_path() -> Option<PathBuf> {
|
|
std::env::var(CAMERA_PREVIEW_TAP_ENV)
|
|
.ok()
|
|
.map(|value| value.trim().to_string())
|
|
.filter(|value| !value.is_empty())
|
|
.map(PathBuf::from)
|
|
}
|
|
|
|
fn camera_preview_tap_branch(width: u32, height: u32, fps: u32) -> String {
|
|
let preview_width = width.clamp(1, i32::MAX as u32);
|
|
let preview_height = height.clamp(1, i32::MAX as u32);
|
|
let preview_fps = fps.clamp(1, 60);
|
|
format!(
|
|
"videoconvert ! videoscale ! videorate ! \
|
|
video/x-raw,format=RGBA,width={preview_width},height={preview_height},framerate={preview_fps}/1,pixel-aspect-ratio=1/1 ! \
|
|
appsink name=preview_sink emit-signals=false sync=false max-buffers=1 drop=true"
|
|
)
|
|
}
|