diff --git a/tests/benchmark_nv12_decode.py b/tests/benchmark_nv12_decode.py index 1d4559e..a54304f 100644 --- a/tests/benchmark_nv12_decode.py +++ b/tests/benchmark_nv12_decode.py @@ -91,15 +91,6 @@ for dev in _HW_VPU_DEVICES: if not hw_decoders: print("[SW] No VPU device or no gst-mpp plugin — using software decode") -# ── Caps: prefer NV12 when HW decode available, same as app ───────────────── - -if hw_decoders: - caps_str = "video/x-raw,format=NV12;video/x-raw,format=BGRA" -else: - caps_str = "video/x-raw,format=BGRA" - -print(f"[caps] appsink caps: {caps_str}") - # ── Pipeline ──────────────────────────────────────────────────────────────── pipeline = Gst.ElementFactory.make("playbin", "player") @@ -107,14 +98,59 @@ if pipeline is None: print("[ERR] playbin unavailable — install gst-plugins-base") sys.exit(1) +# Target display size — same default as the app (R36S screen is 640×480). +SCALE_W, SCALE_H = 640, 480 + appsink = Gst.ElementFactory.make("appsink", "vsink") appsink.set_property("emit-signals", True) appsink.set_property("sync", True) # keep A/V sync on appsink.set_property("max-buffers", 2) appsink.set_property("drop", True) -appsink.set_property("caps", Gst.Caps.from_string(caps_str)) -pipeline.set_property("video-sink", appsink) +# video_sink is what we hand to playbin. For HW decode we mirror _create_appsink() +# from the app: wrap videoscale → capsfilter(NV12,640×480) → appsink in a GstBin +# so the Python callback receives 460 KB per frame instead of 3.1 MB (6.7× smaller). +# Pass --noscale to disable this and benchmark the unscaled path. +video_sink = appsink + +if hw_decoders and "--noscale" not in sys.argv: + scale_el = Gst.ElementFactory.make("videoscale", "vscale") + cfilt_el = Gst.ElementFactory.make("capsfilter", "vcaps") + if scale_el is not None and cfilt_el is not None: + cfilt_el.set_property( + "caps", + Gst.Caps.from_string( + f"video/x-raw,format=NV12,width={SCALE_W},height={SCALE_H}" + ), + ) + bin_ = Gst.Bin.new("vscale-bin") + bin_.add(scale_el) + bin_.add(cfilt_el) + bin_.add(appsink) + scale_el.link(cfilt_el) + cfilt_el.link(appsink) + sink_pad = scale_el.get_static_pad("sink") + ghost = Gst.GhostPad.new("sink", sink_pad) + ghost.set_active(True) + bin_.add_pad(ghost) + video_sink = bin_ + print(f"[scale] videoscale → {SCALE_W}×{SCALE_H} NV12 (mirrors app _create_appsink)") + else: + print("[scale] videoscale element unavailable — falling back to unscaled NV12") + appsink.set_property( + "caps", Gst.Caps.from_string("video/x-raw,format=NV12;video/x-raw,format=BGRA") + ) +elif hw_decoders: + print(f"[scale] --noscale: unscaled NV12 ({SCALE_W}×{SCALE_H} disabled)") + appsink.set_property( + "caps", Gst.Caps.from_string("video/x-raw,format=NV12;video/x-raw,format=BGRA") + ) +else: + appsink.set_property("caps", Gst.Caps.from_string("video/x-raw,format=BGRA")) + +print(f"[caps] video-sink: {'GstBin(videoscale+capsfilter+appsink)' if video_sink is not appsink else 'appsink'}") + +pipeline.set_property("video-sink", video_sink) pipeline.set_property("uri", url) # Disable subtitles / visualisations, keep audio+video (same flags as app). @@ -302,7 +338,9 @@ with stats.lock: fmt = stats.fmt w, h = stats.width, stats.height +scale_active = video_sink is not appsink print(f" URL : {url}") +print(f" Scale : {'videoscale → %d×%d NV12' % (SCALE_W, SCALE_H) if scale_active else 'none (--noscale or SW)'}") print(f" Format : {fmt} {w}x{h}") print(f" Decoder : {'HW (' + ', '.join(hw_decoders) + ')' if hw_decoders else 'SW (avdec_*)'}") print(f" Total frames decoded : {total} (excl. {WARMUP_FRAMES} warmup)")