"""Provide functionality to record stream.""" import threading from typing import List from homeassistant.core import callback from .core import Segment, StreamOutput, PROVIDERS @callback def async_setup_recorder(hass): """Only here so Provider Registry works.""" def recorder_save_worker(file_out: str, segments: List[Segment]): """Handle saving stream.""" import av output = av.open(file_out, "w", options={"movflags": "frag_keyframe"}) output_v = None for segment in segments: # Seek to beginning and open segment segment.segment.seek(0) source = av.open(segment.segment, "r", format="mpegts") source_v = source.streams.video[0] # Add output streams if not output_v: output_v = output.add_stream(template=source_v) # Remux video for packet in source.demux(source_v): if packet is not None and packet.dts is not None: packet.stream = output_v output.mux(packet) output.close() @PROVIDERS.register("recorder") class RecorderOutput(StreamOutput): """Represents HLS Output formats.""" def __init__(self, stream, timeout: int = 30) -> None: """Initialize recorder output.""" super().__init__(stream, timeout) self.video_path = None self._segments = [] @property def name(self) -> str: """Return provider name.""" return "recorder" @property def format(self) -> str: """Return container format.""" return "mpegts" @property def audio_codec(self) -> str: """Return desired audio codec.""" return "aac" @property def video_codec(self) -> str: """Return desired video codec.""" return "h264" def prepend(self, segments: List[Segment]) -> None: """Prepend segments to existing list.""" own_segments = self.segments segments = [s for s in segments if s.sequence not in own_segments] self._segments = segments + self._segments @callback def _timeout(self, _now=None): """Handle recorder timeout.""" self._unsub = None self.cleanup() def cleanup(self): """Write recording and clean up.""" thread = threading.Thread( name="recorder_save_worker", target=recorder_save_worker, args=(self.video_path, self._segments), ) thread.start() self._segments = [] self._stream.remove_provider(self)