|
|
|
@ -91,15 +91,6 @@ for dev in _HW_VPU_DEVICES: |
|
|
|
if not hw_decoders: |
|
|
|
if not hw_decoders: |
|
|
|
print("[SW] No VPU device or no gst-mpp plugin — using software decode") |
|
|
|
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 ──────────────────────────────────────────────────────────────── |
|
|
|
|
|
|
|
|
|
|
|
pipeline = Gst.ElementFactory.make("playbin", "player") |
|
|
|
pipeline = Gst.ElementFactory.make("playbin", "player") |
|
|
|
@ -107,14 +98,59 @@ if pipeline is None: |
|
|
|
print("[ERR] playbin unavailable — install gst-plugins-base") |
|
|
|
print("[ERR] playbin unavailable — install gst-plugins-base") |
|
|
|
sys.exit(1) |
|
|
|
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 = Gst.ElementFactory.make("appsink", "vsink") |
|
|
|
appsink.set_property("emit-signals", True) |
|
|
|
appsink.set_property("emit-signals", True) |
|
|
|
appsink.set_property("sync", True) # keep A/V sync on |
|
|
|
appsink.set_property("sync", True) # keep A/V sync on |
|
|
|
appsink.set_property("max-buffers", 2) |
|
|
|
appsink.set_property("max-buffers", 2) |
|
|
|
appsink.set_property("drop", True) |
|
|
|
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) |
|
|
|
pipeline.set_property("uri", url) |
|
|
|
|
|
|
|
|
|
|
|
# Disable subtitles / visualisations, keep audio+video (same flags as app). |
|
|
|
# Disable subtitles / visualisations, keep audio+video (same flags as app). |
|
|
|
@ -302,7 +338,9 @@ with stats.lock: |
|
|
|
fmt = stats.fmt |
|
|
|
fmt = stats.fmt |
|
|
|
w, h = stats.width, stats.height |
|
|
|
w, h = stats.width, stats.height |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
scale_active = video_sink is not appsink |
|
|
|
print(f" URL : {url}") |
|
|
|
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" Format : {fmt} {w}x{h}") |
|
|
|
print(f" Decoder : {'HW (' + ', '.join(hw_decoders) + ')' if hw_decoders else 'SW (avdec_*)'}") |
|
|
|
print(f" Decoder : {'HW (' + ', '.join(hw_decoders) + ')' if hw_decoders else 'SW (avdec_*)'}") |
|
|
|
print(f" Total frames decoded : {total} (excl. {WARMUP_FRAMES} warmup)") |
|
|
|
print(f" Total frames decoded : {total} (excl. {WARMUP_FRAMES} warmup)") |
|
|
|
|