Additional stream typing improvements (#129695)

This commit is contained in:
Marc Mueller 2024-11-02 22:22:31 +01:00 committed by GitHub
parent e18ffc53f2
commit 5cf13d9273
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 24 additions and 20 deletions

View File

@ -9,7 +9,7 @@ from dataclasses import dataclass, field
import datetime import datetime
from enum import IntEnum from enum import IntEnum
import logging import logging
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any, cast
from aiohttp import web from aiohttp import web
import numpy as np import numpy as np
@ -27,7 +27,8 @@ from .const import (
) )
if TYPE_CHECKING: if TYPE_CHECKING:
from av import CodecContext, Packet from av import Packet
from av.video.codeccontext import VideoCodecContext
from homeassistant.components.camera import DynamicStreamSettings from homeassistant.components.camera import DynamicStreamSettings
@ -448,7 +449,7 @@ class KeyFrameConverter:
self._image: bytes | None = None self._image: bytes | None = None
self._turbojpeg = TurboJPEGSingleton.instance() self._turbojpeg = TurboJPEGSingleton.instance()
self._lock = asyncio.Lock() self._lock = asyncio.Lock()
self._codec_context: CodecContext | None = None self._codec_context: VideoCodecContext | None = None
self._stream_settings = stream_settings self._stream_settings = stream_settings
self._dynamic_stream_settings = dynamic_stream_settings self._dynamic_stream_settings = dynamic_stream_settings
@ -460,7 +461,7 @@ class KeyFrameConverter:
self._packet = packet self._packet = packet
self._hass.loop.call_soon_threadsafe(self._event.set) self._hass.loop.call_soon_threadsafe(self._event.set)
def create_codec_context(self, codec_context: CodecContext) -> None: def create_codec_context(self, codec_context: VideoCodecContext) -> None:
"""Create a codec context to be used for decoding the keyframes. """Create a codec context to be used for decoding the keyframes.
This is run by the worker thread and will only be called once per worker. This is run by the worker thread and will only be called once per worker.
@ -474,7 +475,9 @@ class KeyFrameConverter:
# pylint: disable-next=import-outside-toplevel # pylint: disable-next=import-outside-toplevel
from av import CodecContext from av import CodecContext
self._codec_context = CodecContext.create(codec_context.name, "r") self._codec_context = cast(
"VideoCodecContext", CodecContext.create(codec_context.name, "r")
)
self._codec_context.extradata = codec_context.extradata self._codec_context.extradata = codec_context.extradata
self._codec_context.skip_frame = "NONKEY" self._codec_context.skip_frame = "NONKEY"
self._codec_context.thread_type = "NONE" self._codec_context.thread_type = "NONE"

View File

@ -122,7 +122,7 @@ class RecorderOutput(StreamOutput):
if not output_v: if not output_v:
output_v = output.add_stream(template=source_v) output_v = output.add_stream(template=source_v)
context = output_v.codec_context context = output_v.codec_context
context.flags |= "GLOBAL_HEADER" context.global_header = True
if source_a and not output_a: if source_a and not output_a:
output_a = output.add_stream(template=source_a) output_a = output.add_stream(template=source_a)

View File

@ -127,6 +127,16 @@ class StreamState:
class StreamMuxer: class StreamMuxer:
"""StreamMuxer re-packages video/audio packets for output.""" """StreamMuxer re-packages video/audio packets for output."""
_segment_start_dts: int
_memory_file: BytesIO
_av_output: av.container.OutputContainer
_output_video_stream: av.video.VideoStream
_output_audio_stream: av.audio.AudioStream | None
_segment: Segment | None
# the following 2 member variables are used for Part formation
_memory_file_pos: int
_part_start_dts: int
def __init__( def __init__(
self, self,
hass: HomeAssistant, hass: HomeAssistant,
@ -138,19 +148,10 @@ class StreamMuxer:
) -> None: ) -> None:
"""Initialize StreamMuxer.""" """Initialize StreamMuxer."""
self._hass = hass self._hass = hass
self._segment_start_dts: int = cast(int, None) self._input_video_stream = video_stream
self._memory_file: BytesIO = cast(BytesIO, None) self._input_audio_stream = audio_stream
self._av_output: av.container.OutputContainer = None
self._input_video_stream: av.video.VideoStream = video_stream
self._input_audio_stream: av.audio.AudioStream | None = audio_stream
self._audio_bsf = audio_bsf self._audio_bsf = audio_bsf
self._audio_bsf_context: av.BitStreamFilterContext = None self._audio_bsf_context: av.BitStreamFilterContext | None = None
self._output_video_stream: av.video.VideoStream = None
self._output_audio_stream: av.audio.AudioStream | None = None
self._segment: Segment | None = None
# the following 3 member variables are used for Part formation
self._memory_file_pos: int = cast(int, None)
self._part_start_dts: int = cast(int, None)
self._part_has_keyframe = False self._part_has_keyframe = False
self._stream_settings = stream_settings self._stream_settings = stream_settings
self._stream_state = stream_state self._stream_state = stream_state
@ -256,7 +257,7 @@ class StreamMuxer:
input_astream=self._input_audio_stream, input_astream=self._input_audio_stream,
) )
if self._output_video_stream.name == "hevc": if self._output_video_stream.name == "hevc":
self._output_video_stream.codec_tag = "hvc1" self._output_video_stream.codec_context.codec_tag = "hvc1"
def mux_packet(self, packet: av.Packet) -> None: def mux_packet(self, packet: av.Packet) -> None:
"""Mux a packet to the appropriate output stream.""" """Mux a packet to the appropriate output stream."""
@ -562,7 +563,7 @@ def stream_worker(
dts_validator = TimestampValidator( dts_validator = TimestampValidator(
int(1 / video_stream.time_base), int(1 / video_stream.time_base),
1 / audio_stream.time_base if audio_stream else 1, int(1 / audio_stream.time_base) if audio_stream else 1,
) )
container_packets = PeekIterator( container_packets = PeekIterator(
filter(dts_validator.is_valid, container.demux((video_stream, audio_stream))) filter(dts_validator.is_valid, container.demux((video_stream, audio_stream)))