66 lines
2.4 KiB
Python
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()
|