Files
mindbot/scripts/environments/teleoperation/xr_teleop/streaming.py
2026-03-23 22:06:13 +08:00

66 lines
2.4 KiB
Python

# SPDX-FileCopyrightText: Copyright (c) 2022-2025, The Isaac Lab Project Developers.
# SPDX-License-Identifier: BSD-3-Clause
"""Sim-to-VR H264 stereo streaming manager."""
import logging
logger = logging.getLogger(__name__)
class StreamingManager:
"""Manages stereo camera → H264 → TCP streaming to VR headset.
Wraps SimVideoStreamer with graceful error handling and auto-disable on failure.
"""
def __init__(self, host: str, port: int, scene, bitrate: int = 20_000_000):
self._streamer = None
self._enabled = False
if "vr_left_eye" not in scene.sensors:
logger.warning("[Stream] vr_left_eye camera not found. Streaming disabled.")
return
try:
from mindbot.utils.sim_video_streamer import SimVideoStreamer
cam = scene["vr_left_eye"]
self._streamer = SimVideoStreamer(
host=host, port=port,
width=cam.cfg.width, height=cam.cfg.height,
fps=30, bitrate=bitrate,
)
if self._streamer.connect():
self._enabled = True
print(f"[INFO] Sim stereo streaming to {host}:{port} "
f"({cam.cfg.width * 2}x{cam.cfg.height} SBS @ 30fps)")
else:
logger.error("[Stream] Failed to connect. Streaming disabled.")
self._streamer = None
except ImportError:
logger.error("[Stream] mindbot.utils.sim_video_streamer not found. pip install av?")
except Exception as e:
logger.error(f"[Stream] Init failed: {e}")
@property
def enabled(self) -> bool:
return self._enabled
def send(self, scene, frame_count: int = 0) -> None:
"""Send one stereo frame from scene cameras. Auto-disables on failure."""
if not self._enabled:
return
try:
left_rgb = scene["vr_left_eye"].data.output["rgb"][0].cpu().numpy()
right_rgb = scene["vr_right_eye"].data.output["rgb"][0].cpu().numpy()
if not self._streamer.send_frame(left_rgb, right_rgb):
logger.warning("[Stream] Connection lost. Disabling streaming.")
self._enabled = False
except Exception as e:
if frame_count % 300 == 0:
logger.warning(f"[Stream] Frame error: {e}")
def close(self):
if self._streamer is not None:
self._streamer.close()