using System; using LightGlue.Unity.Networking; using LightGlue.Unity.Bridge; using LightGlue.Unity.Game; using UnityEngine; using UnityEngine.UI; namespace LightGlue.Unity.Demo { /// /// Demo: 从 HardwareToPythonUdpBridge 共享 JPEG 接收器,在 RawImage 上显示硬件图像。 /// 仅支持 Shared 模式(强制复用 Bridge 的 UDPJpegReceiver,避免端口冲突)。 /// public sealed class HardwareUdpJpegViewer : MonoBehaviour { [Header("Source (Shared Only)")] [Tooltip("HardwareToPythonUdpBridge组件(共享其JPEG接收器,避免端口冲突)")] public HardwareToPythonUdpBridge bridge; [Header("UI")] public RawImage target; [Tooltip("是否在没有贴图时自动创建 Texture2D。")] public bool autoCreateTexture = true; private Texture2D _tex; private void Start() { // 优先从全局 LightGlueManager 获取 Bridge(如果未在 Inspector 指定) if (bridge == null && LightGlueManager.Instance != null) { bridge = LightGlueManager.Instance.bridge; } if (bridge == null) { Debug.LogWarning("[Viewer] HardwareUdpJpegViewer 需要 HardwareToPythonUdpBridge 引用,请在 Inspector 中指定或确保存在 LightGlueManager 实例。"); } else { Debug.Log("[Viewer] Using shared receiver from HardwareToPythonUdpBridge (no port conflict)"); } } private void OnDisable() { // Shared 模式下不管理 Bridge 内部的接收器,这里无需额外清理 } private float _lastDecodeFailLogTime = -1f; private void Update() { byte[] latest = null; if (bridge == null) return; latest = bridge.GetLatestJpeg(); if (latest == null || latest.Length < 4) return; // 只尝试解码形似完整 JPEG 的缓冲(SOI+EOI),避免对 UDP 组帧残片反复解码并刷日志 if (latest[0] != 0xFF || latest[1] != 0xD8 || latest[latest.Length - 2] != 0xFF || latest[latest.Length - 1] != 0xD9) return; if (target == null) return; if (_tex == null) { if (!autoCreateTexture) return; _tex = new Texture2D(2, 2, TextureFormat.RGB24, mipChain: false); _tex.wrapMode = TextureWrapMode.Clamp; _tex.filterMode = FilterMode.Bilinear; target.texture = _tex; } bool ok = _tex.LoadImage(latest, markNonReadable: false); if (!ok) { if (Time.unscaledTime - _lastDecodeFailLogTime >= 1f) { _lastDecodeFailLogTime = Time.unscaledTime; Debug.LogWarning($"[Viewer] Failed to decode JPEG (size: {latest.Length} bytes)"); } } } } }