@@ -12253,6 +12253,76 @@ def get_queue_config(lowlatency=False):
1225312253 print(f"The video input {args.v4l2} exists, but no permissions to read.")
1225412254 error = True
1225512255
12256+ # Legacy Raspberry Pi (GStreamer < 1.20) USB/UVC capture quirks
12257+ #
12258+ # - Some MacroSilicon/MS2109 HDMI dongles produce MJPEG streams that v4l2jpegdec
12259+ # fails to decode reliably, while jpegdec succeeds.
12260+ # - The device also supports only discrete capture sizes; requesting an unsupported
12261+ # size results in "not-negotiated (-4)" from v4l2src.
12262+ #
12263+ # Prefer resiliency over acceleration for these older stacks, while keeping
12264+ # modern GStreamer behavior unchanged.
12265+ if args.rpi and not error:
12266+ gst_ver = Gst.version()
12267+ if gst_ver.major == 1 and gst_ver.minor < 20:
12268+ v4l2_device_name = None
12269+ try:
12270+ video_node = os.path.basename(args.v4l2)
12271+ with open(f"/sys/class/video4linux/{video_node}/name", "r", encoding="utf-8") as f:
12272+ v4l2_device_name = f.read().strip()
12273+ except Exception:
12274+ pass
12275+
12276+ if v4l2_device_name:
12277+ v4l2_device_name_l = v4l2_device_name.lower()
12278+ is_macrosilicon = ("macrosilicon" in v4l2_device_name_l) or ("ms2109" in v4l2_device_name_l)
12279+
12280+ # Default to software JPEG decoding for known-problematic dongles.
12281+ if is_macrosilicon and (not args.raw) and (not args.soft_jpeg):
12282+ args.soft_jpeg = True
12283+ printc("Auto-enabled --soft-jpeg for MacroSilicon/MS2109 capture device (GStreamer < 1.20)", "FA0")
12284+
12285+ # If the requested MJPEG capture size isn't supported, choose the nearest.
12286+ if is_macrosilicon and (not args.raw):
12287+ try:
12288+ result = subprocess.run(
12289+ ['v4l2-ctl', '-d', args.v4l2, '--list-formats-ext'],
12290+ stdout=subprocess.PIPE,
12291+ stderr=subprocess.PIPE,
12292+ text=True,
12293+ timeout=2,
12294+ )
12295+ except (subprocess.SubprocessError, subprocess.TimeoutExpired, FileNotFoundError):
12296+ result = None
12297+
12298+ if result and result.returncode == 0:
12299+ current_format = None
12300+ sizes = set()
12301+ for line in result.stdout.splitlines():
12302+ m = re.match(r"\s*\[\d+\]:\s*'(\w+)'", line)
12303+ if m:
12304+ current_format = m.group(1)
12305+ continue
12306+ m = re.match(r"\s*Size:\s*Discrete\s*(\d+)x(\d+)", line)
12307+ if m and current_format == "MJPG":
12308+ sizes.add((int(m.group(1)), int(m.group(2))))
12309+
12310+ if sizes and (args.width, args.height) not in sizes:
12311+ target_ratio = (args.width / args.height) if args.height else 0.0
12312+ target_area = args.width * args.height
12313+
12314+ def _score(size):
12315+ w, h = size
12316+ ratio = (w / h) if h else 0.0
12317+ return (abs(ratio - target_ratio), abs((w * h) - target_area))
12318+
12319+ best_w, best_h = min(sizes, key=_score)
12320+ printc(
12321+ f"Requested {args.width}x{args.height} not supported by {args.v4l2} ({v4l2_device_name}); using {best_w}x{best_h}",
12322+ "FA0",
12323+ )
12324+ args.width, args.height = best_w, best_h
12325+
1225612326 if error:
1225712327 pipeline_video_input = f'v4l2src device={args.v4l2} io-mode={str(args.iomode)}'
1225812328 pipeline_video_converter = "" # Add this line
0 commit comments