Source code for go2.modules.video.streaming.stream_track

import asyncio
import threading
from fractions import Fraction
from typing import Optional
 
import cv2
import numpy as np
from aiortc import VideoStreamTrack
from av import VideoFrame

from .stream_config import StreamConfig

# command to install rtc: pip install aiortc opencv-python

[docs] class OpenCVStreamTrack(VideoStreamTrack): def __init__(self, stream_config: StreamConfig) -> None: super().__init__() self._cfg = stream_config yuv_height = stream_config.height * 3 // 2 self._frame_buf = np.zeros( (yuv_height, stream_config.width), dtype=np.uint8, order="C", ) self._version = 0 self._latest_version = -1 self._start_time: Optional[float] = None self._loop = asyncio.get_event_loop() self._cached_frame: Optional[VideoFrame] = None def push_frame(self, bgr_frame: np.ndarray) -> None: if bgr_frame is None: return h, w = bgr_frame.shape[:2] if w != self._cfg.width or h != self._cfg.height: bgr_frame = cv2.resize( bgr_frame, (self._cfg.width, self._cfg.height), interpolation=cv2.INTER_LINEAR, ) yuv = cv2.cvtColor(bgr_frame, cv2.COLOR_BGR2YUV_I420) np.copyto(self._frame_buf, yuv) self._version += 1
[docs] async def recv(self) -> VideoFrame: if self._start_time is None: self._start_time = self._loop.time() elapsed = self._loop.time() - self._start_time pts = int(elapsed * self._cfg.fps) version = self._version if version == self._latest_version and self._cached_frame is not None: self._cached_frame.pts = pts self._cached_frame.time_base = Fraction(1, self._cfg.fps) return self._cached_frame frame = VideoFrame.from_ndarray(self._frame_buf, format="yuv420p") frame.pts = pts frame.time_base = Fraction(1, self._cfg.fps) self._cached_frame = frame self._latest_version = version return frame