Skip to content

Commit 0741afc

Browse files
committed
fix(rpi): Auto soft-jpeg + supported MJPEG size
1 parent 4b535bd commit 0741afc

1 file changed

Lines changed: 70 additions & 0 deletions

File tree

publish.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)